Added service ordering and implemented basic service creation
This commit is contained in:
parent
b19208b3b0
commit
48c95d4ec6
15 changed files with 370 additions and 19 deletions
22
Moonlight/App/Actions/Dummy/DummyActions.cs
Normal file
22
Moonlight/App/Actions/Dummy/DummyActions.cs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
using Moonlight.App.Database.Entities.Store;
|
||||||
|
using Moonlight.App.Models.Abstractions;
|
||||||
|
|
||||||
|
namespace Moonlight.App.Actions.Dummy;
|
||||||
|
|
||||||
|
public class DummyActions : ServiceActions
|
||||||
|
{
|
||||||
|
public override Task Create(IServiceProvider provider, Service service)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task Update(IServiceProvider provider, Service service)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task Delete(IServiceProvider provider, Service service)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,7 +16,7 @@ public class Product
|
||||||
public int MaxPerUser { get; set; }
|
public int MaxPerUser { get; set; }
|
||||||
public int Duration { get; set; }
|
public int Duration { get; set; }
|
||||||
|
|
||||||
public ProductType Type { get; set; }
|
public ServiceType Type { get; set; }
|
||||||
public string ConfigJson { get; set; } = "{}";
|
public string ConfigJson { get; set; } = "{}";
|
||||||
|
|
||||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Moonlight.App.Database.Enums;
|
namespace Moonlight.App.Database.Enums;
|
||||||
|
|
||||||
public enum ProductType
|
public enum ServiceType
|
||||||
{
|
{
|
||||||
Server,
|
Server,
|
||||||
Webspace,
|
Webspace,
|
10
Moonlight/App/Models/Abstractions/ServiceActions.cs
Normal file
10
Moonlight/App/Models/Abstractions/ServiceActions.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using Moonlight.App.Database.Entities.Store;
|
||||||
|
|
||||||
|
namespace Moonlight.App.Models.Abstractions;
|
||||||
|
|
||||||
|
public abstract class ServiceActions
|
||||||
|
{
|
||||||
|
public abstract Task Create(IServiceProvider provider, Service service);
|
||||||
|
public abstract Task Update(IServiceProvider provider, Service service);
|
||||||
|
public abstract Task Delete(IServiceProvider provider, Service service);
|
||||||
|
}
|
|
@ -31,6 +31,6 @@ public class AddProductForm
|
||||||
[Range(0, double.MaxValue, ErrorMessage = "The duration needs to be equals or above 0")]
|
[Range(0, double.MaxValue, ErrorMessage = "The duration needs to be equals or above 0")]
|
||||||
public int Duration { get; set; }
|
public int Duration { get; set; }
|
||||||
|
|
||||||
public ProductType Type { get; set; }
|
public ServiceType Type { get; set; }
|
||||||
public string ConfigJson { get; set; } = "{}";
|
public string ConfigJson { get; set; } = "{}";
|
||||||
}
|
}
|
|
@ -31,6 +31,6 @@ public class EditProductForm
|
||||||
[Range(0, double.MaxValue, ErrorMessage = "The duration needs to be equals or above 0")]
|
[Range(0, double.MaxValue, ErrorMessage = "The duration needs to be equals or above 0")]
|
||||||
public int Duration { get; set; }
|
public int Duration { get; set; }
|
||||||
|
|
||||||
public ProductType Type { get; set; }
|
public ServiceType Type { get; set; }
|
||||||
public string ConfigJson { get; set; } = "{}";
|
public string ConfigJson { get; set; } = "{}";
|
||||||
}
|
}
|
63
Moonlight/App/Services/ServiceManage/ServiceAdminService.cs
Normal file
63
Moonlight/App/Services/ServiceManage/ServiceAdminService.cs
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Moonlight.App.Database.Entities;
|
||||||
|
using Moonlight.App.Database.Entities.Store;
|
||||||
|
using Moonlight.App.Database.Enums;
|
||||||
|
using Moonlight.App.Exceptions;
|
||||||
|
using Moonlight.App.Models.Abstractions;
|
||||||
|
using Moonlight.App.Repositories;
|
||||||
|
|
||||||
|
namespace Moonlight.App.Services.ServiceManage;
|
||||||
|
|
||||||
|
public class ServiceAdminService
|
||||||
|
{
|
||||||
|
public readonly Dictionary<ServiceType, ServiceActions> Actions = new();
|
||||||
|
private readonly IServiceScopeFactory ServiceScopeFactory;
|
||||||
|
|
||||||
|
public ServiceAdminService(IServiceScopeFactory serviceScopeFactory)
|
||||||
|
{
|
||||||
|
ServiceScopeFactory = serviceScopeFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Service> Create(User u, Product p, Action<Service>? modifyService = null)
|
||||||
|
{
|
||||||
|
if (!Actions.ContainsKey(p.Type))
|
||||||
|
throw new DisplayException($"The product type {p.Type} is not registered");
|
||||||
|
|
||||||
|
// Load models in new scope
|
||||||
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
|
var userRepo = scope.ServiceProvider.GetRequiredService<Repository<User>>();
|
||||||
|
var productRepo = scope.ServiceProvider.GetRequiredService<Repository<Product>>();
|
||||||
|
var serviceRepo = scope.ServiceProvider.GetRequiredService<Repository<Service>>();
|
||||||
|
|
||||||
|
var user = userRepo.Get().First(x => x.Id == u.Id);
|
||||||
|
var product = productRepo.Get().First(x => x.Id == p.Id);
|
||||||
|
|
||||||
|
// Create database model
|
||||||
|
var service = new Service()
|
||||||
|
{
|
||||||
|
Product = product,
|
||||||
|
Owner = user,
|
||||||
|
Suspended = false,
|
||||||
|
CreatedAt = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allow further modifications
|
||||||
|
if(modifyService != null)
|
||||||
|
modifyService.Invoke(service);
|
||||||
|
|
||||||
|
// Add new service in database
|
||||||
|
var finishedService = serviceRepo.Add(service);
|
||||||
|
|
||||||
|
// Call the action for the logic behind the service type
|
||||||
|
var actions = Actions[product.Type];
|
||||||
|
await actions.Create(scope.ServiceProvider, finishedService);
|
||||||
|
|
||||||
|
return finishedService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task RegisterAction(ServiceType type, ServiceActions actions) // Use this function to register service types
|
||||||
|
{
|
||||||
|
Actions.Add(type, actions);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
43
Moonlight/App/Services/ServiceManage/ServiceService.cs
Normal file
43
Moonlight/App/Services/ServiceManage/ServiceService.cs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Moonlight.App.Database.Entities;
|
||||||
|
using Moonlight.App.Database.Entities.Store;
|
||||||
|
using Moonlight.App.Repositories;
|
||||||
|
|
||||||
|
namespace Moonlight.App.Services.ServiceManage;
|
||||||
|
|
||||||
|
public class ServiceService // This service is used for managing services and create the connection to the actual logic behind a service type
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider ServiceProvider;
|
||||||
|
private readonly Repository<Service> ServiceRepository;
|
||||||
|
|
||||||
|
public ServiceAdminService Admin => ServiceProvider.GetRequiredService<ServiceAdminService>();
|
||||||
|
|
||||||
|
public ServiceService(IServiceProvider serviceProvider, Repository<Service> serviceRepository)
|
||||||
|
{
|
||||||
|
ServiceProvider = serviceProvider;
|
||||||
|
ServiceRepository = serviceRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<Service[]> Get(User user)
|
||||||
|
{
|
||||||
|
var result = ServiceRepository
|
||||||
|
.Get()
|
||||||
|
.Include(x => x.Product)
|
||||||
|
.Where(x => x.Owner.Id == user.Id)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
return Task.FromResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<Service[]> GetShared(User user)
|
||||||
|
{
|
||||||
|
var result = ServiceRepository
|
||||||
|
.Get()
|
||||||
|
.Include(x => x.Product)
|
||||||
|
.Include(x => x.Owner)
|
||||||
|
.Where(x => x.Shares.Any(y => y.User.Id == user.Id))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
return Task.FromResult(result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,7 +31,7 @@ public class StoreAdminService
|
||||||
return Task.FromResult(result);
|
return Task.FromResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Product> AddProduct(string name, string description, string slug, ProductType type, string configJson,
|
public Task<Product> AddProduct(string name, string description, string slug, ServiceType type, string configJson,
|
||||||
Action<Product>? modifyProduct = null)
|
Action<Product>? modifyProduct = null)
|
||||||
{
|
{
|
||||||
if (ProductRepository.Get().Any(x => x.Slug == slug))
|
if (ProductRepository.Get().Any(x => x.Slug == slug))
|
||||||
|
|
|
@ -3,6 +3,7 @@ using Moonlight.App.Database.Entities;
|
||||||
using Moonlight.App.Database.Entities.Store;
|
using Moonlight.App.Database.Entities.Store;
|
||||||
using Moonlight.App.Exceptions;
|
using Moonlight.App.Exceptions;
|
||||||
using Moonlight.App.Repositories;
|
using Moonlight.App.Repositories;
|
||||||
|
using Moonlight.App.Services.ServiceManage;
|
||||||
|
|
||||||
namespace Moonlight.App.Services.Store;
|
namespace Moonlight.App.Services.Store;
|
||||||
|
|
||||||
|
@ -84,4 +85,33 @@ public class StoreOrderService
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Service> Process(User u, Product p, int durationMultiplier, Coupon? c)
|
||||||
|
{
|
||||||
|
// Validate to ensure we dont process an illegal order
|
||||||
|
await Validate(u, p, durationMultiplier, c);
|
||||||
|
|
||||||
|
// Create scope and get required services
|
||||||
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
|
var serviceService = scope.ServiceProvider.GetRequiredService<ServiceService>();
|
||||||
|
var transactionService = scope.ServiceProvider.GetRequiredService<TransactionService>();
|
||||||
|
|
||||||
|
// Calculate price
|
||||||
|
var price = p.Price * durationMultiplier;
|
||||||
|
|
||||||
|
if (c != null)
|
||||||
|
price = Math.Round(price * c.Percent / 100, 2);
|
||||||
|
|
||||||
|
// Calculate duration
|
||||||
|
var duration = durationMultiplier * p.Duration;
|
||||||
|
|
||||||
|
// Add transaction
|
||||||
|
await transactionService.Add(u, -1 * price, $"Bought product '{p.Name}' for {duration} days");
|
||||||
|
|
||||||
|
// Create service
|
||||||
|
return await serviceService.Admin.Create(u, p, service =>
|
||||||
|
{
|
||||||
|
service.RenewAt = DateTime.UtcNow.AddDays(duration);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
35
Moonlight/App/Services/Store/TransactionService.cs
Normal file
35
Moonlight/App/Services/Store/TransactionService.cs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
using Moonlight.App.Database.Entities;
|
||||||
|
using Moonlight.App.Database.Entities.Store;
|
||||||
|
using Moonlight.App.Repositories;
|
||||||
|
|
||||||
|
namespace Moonlight.App.Services.Store;
|
||||||
|
|
||||||
|
public class TransactionService
|
||||||
|
{
|
||||||
|
private readonly Repository<User> UserRepository;
|
||||||
|
|
||||||
|
public TransactionService(Repository<User> userRepository)
|
||||||
|
{
|
||||||
|
UserRepository = userRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task Add(User u, double amount, string message)
|
||||||
|
{
|
||||||
|
var user = UserRepository.Get().First(x => x.Id == u.Id); // Load user with current repo
|
||||||
|
|
||||||
|
user.Transactions.Add(new Transaction()
|
||||||
|
{
|
||||||
|
Text = message,
|
||||||
|
Price = amount
|
||||||
|
});
|
||||||
|
|
||||||
|
UserRepository.Update(user);
|
||||||
|
|
||||||
|
// We divide the call to ensure the transaction can be written to the database
|
||||||
|
|
||||||
|
user.Balance += amount;
|
||||||
|
UserRepository.Update(user);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
using BlazorTable;
|
using BlazorTable;
|
||||||
|
using Moonlight.App.Actions.Dummy;
|
||||||
using Moonlight.App.Database;
|
using Moonlight.App.Database;
|
||||||
|
using Moonlight.App.Database.Enums;
|
||||||
using Moonlight.App.Extensions;
|
using Moonlight.App.Extensions;
|
||||||
using Moonlight.App.Helpers;
|
using Moonlight.App.Helpers;
|
||||||
using Moonlight.App.Helpers.LogMigrator;
|
using Moonlight.App.Helpers.LogMigrator;
|
||||||
|
@ -7,6 +9,7 @@ using Moonlight.App.Repositories;
|
||||||
using Moonlight.App.Services;
|
using Moonlight.App.Services;
|
||||||
using Moonlight.App.Services.Background;
|
using Moonlight.App.Services.Background;
|
||||||
using Moonlight.App.Services.Interop;
|
using Moonlight.App.Services.Interop;
|
||||||
|
using Moonlight.App.Services.ServiceManage;
|
||||||
using Moonlight.App.Services.Store;
|
using Moonlight.App.Services.Store;
|
||||||
using Moonlight.App.Services.Users;
|
using Moonlight.App.Services.Users;
|
||||||
using Moonlight.App.Services.Utils;
|
using Moonlight.App.Services.Utils;
|
||||||
|
@ -44,6 +47,7 @@ builder.Services.AddScoped<AlertService>();
|
||||||
builder.Services.AddScoped<StoreService>();
|
builder.Services.AddScoped<StoreService>();
|
||||||
builder.Services.AddScoped<StoreAdminService>();
|
builder.Services.AddScoped<StoreAdminService>();
|
||||||
builder.Services.AddScoped<StoreOrderService>();
|
builder.Services.AddScoped<StoreOrderService>();
|
||||||
|
builder.Services.AddScoped<TransactionService>();
|
||||||
|
|
||||||
// Services / Users
|
// Services / Users
|
||||||
builder.Services.AddScoped<UserService>();
|
builder.Services.AddScoped<UserService>();
|
||||||
|
@ -53,6 +57,10 @@ builder.Services.AddScoped<UserDetailsService>();
|
||||||
// Services / Background
|
// Services / Background
|
||||||
builder.Services.AddSingleton<AutoMailSendService>();
|
builder.Services.AddSingleton<AutoMailSendService>();
|
||||||
|
|
||||||
|
// Services / ServiceManage
|
||||||
|
builder.Services.AddScoped<ServiceService>();
|
||||||
|
builder.Services.AddSingleton<ServiceAdminService>();
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
builder.Services.AddScoped<IdentityService>();
|
builder.Services.AddScoped<IdentityService>();
|
||||||
builder.Services.AddSingleton<ConfigService>();
|
builder.Services.AddSingleton<ConfigService>();
|
||||||
|
@ -86,4 +94,7 @@ app.MapControllers();
|
||||||
// Auto start background services
|
// Auto start background services
|
||||||
app.Services.GetRequiredService<AutoMailSendService>();
|
app.Services.GetRequiredService<AutoMailSendService>();
|
||||||
|
|
||||||
|
var serviceService = app.Services.GetRequiredService<ServiceAdminService>();
|
||||||
|
await serviceService.RegisterAction(ServiceType.Server, new DummyActions());
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
|
@ -1,34 +1,49 @@
|
||||||
@if (loaded)
|
@if (Loaded)
|
||||||
{
|
{
|
||||||
@ChildContent
|
@ChildContent
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="d-flex justify-content-center py-4">
|
if (ShowAsCard)
|
||||||
<span class="fs-1 spinner-border spinner-border-lg align-middle me-2"></span>
|
{
|
||||||
<span class="mt-3 fs-5">@(Text)</span>
|
<div class="card card-body">
|
||||||
</div>
|
<div class="d-flex justify-content-center py-4">
|
||||||
|
<span class="fs-1 spinner-border spinner-border-lg align-middle me-2"></span>
|
||||||
|
<span class="mt-3 fs-5">@(Text)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="d-flex justify-content-center py-4">
|
||||||
|
<span class="fs-1 spinner-border spinner-border-lg align-middle me-2"></span>
|
||||||
|
<span class="mt-3 fs-5">@(Text)</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment ChildContent { get; set; }
|
public RenderFragment ChildContent { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool ShowAsCard { get; set; } = false;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Func<LazyLoader, Task> Load { get; set; }
|
public Func<LazyLoader, Task> Load { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Text { get; set; } = "";
|
public string Text { get; set; } = "";
|
||||||
|
|
||||||
private bool loaded = false;
|
private bool Loaded = false;
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
if (firstRender)
|
if (firstRender)
|
||||||
{
|
{
|
||||||
await Load.Invoke(this);
|
await Load.Invoke(this);
|
||||||
loaded = true;
|
Loaded = true;
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,10 +56,10 @@ else
|
||||||
|
|
||||||
public async Task Reload()
|
public async Task Reload()
|
||||||
{
|
{
|
||||||
loaded = false;
|
Loaded = false;
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
await Load.Invoke(this);
|
await Load.Invoke(this);
|
||||||
loaded = true;
|
Loaded = true;
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
}
|
}
|
||||||
}
|
}
|
101
Moonlight/Shared/Views/Services/Index.razor
Normal file
101
Moonlight/Shared/Views/Services/Index.razor
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
@page "/services"
|
||||||
|
|
||||||
|
@using Moonlight.App.Services.ServiceManage
|
||||||
|
@using Moonlight.App.Database.Entities.Store
|
||||||
|
@using Moonlight.App.Services
|
||||||
|
|
||||||
|
@inject ServiceService ServiceService
|
||||||
|
@inject IdentityService IdentityService
|
||||||
|
@inject ConfigService ConfigService
|
||||||
|
|
||||||
|
<LazyLoader ShowAsCard="true" Load="Load">
|
||||||
|
<div class="row mb-5">
|
||||||
|
@foreach (var service in MyServices)
|
||||||
|
{
|
||||||
|
<div class="col-md-3 col-12">
|
||||||
|
<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">@(service.Nickname ?? $"Service {service.Id}")</span>
|
||||||
|
<span class="text-gray-400 mt-1 fw-semibold fs-6">@(service.Product.Name)</span>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body fs-6">
|
||||||
|
<div class="d-flex flex-stack">
|
||||||
|
<div class="text-gray-700 fw-semibold me-2">Price</div>
|
||||||
|
<div class="d-flex align-items-senter">
|
||||||
|
<span class="fw-bold">@(ConfigService.Get().Store.Currency) @(service.Product.Price)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex flex-stack">
|
||||||
|
<div class="text-gray-700 fw-semibold me-2">Renew at</div>
|
||||||
|
<div class="d-flex align-items-senter">
|
||||||
|
<span class="fw-bold">@(Formatter.FormatDate(service.RenewAt))</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex flex-stack">
|
||||||
|
<div class="text-gray-700 fw-semibold me-2">Created at</div>
|
||||||
|
<div class="d-flex align-items-senter">
|
||||||
|
<span class="fw-bold">@(Formatter.FormatDate(service.CreatedAt))</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer p-3">
|
||||||
|
<div class="btn-group w-100 mb-3">
|
||||||
|
<button class="btn btn-primary w-50 me-3">Manage</button>
|
||||||
|
<button 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 class="btn btn-danger w-50">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@foreach (var service in SharedServices)
|
||||||
|
{
|
||||||
|
<div class="col-md-3 col-12">
|
||||||
|
<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">@(service.Nickname ?? $"Service {service.Id}")</span>
|
||||||
|
<span class="text-gray-400 mt-1 fw-semibold fs-6">@(service.Product.Name)</span>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body fs-6">
|
||||||
|
<div class="d-flex flex-stack">
|
||||||
|
<div class="text-gray-700 fw-semibold me-2">Shared by</div>
|
||||||
|
<div class="d-flex align-items-senter">
|
||||||
|
<span class="fw-bold">@(service.Owner.Username)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex flex-stack">
|
||||||
|
<div class="text-gray-700 fw-semibold me-2">Created at</div>
|
||||||
|
<div class="d-flex align-items-senter">
|
||||||
|
<span class="fw-bold">@(Formatter.FormatDate(service.CreatedAt))</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer p-3 text-center">
|
||||||
|
<button class="btn btn-primary">Manage</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</LazyLoader>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
private Service[] MyServices;
|
||||||
|
private Service[] SharedServices;
|
||||||
|
|
||||||
|
private async Task Load(LazyLoader _)
|
||||||
|
{
|
||||||
|
MyServices = await ServiceService.Get(IdentityService.CurrentUser);
|
||||||
|
SharedServices = await ServiceService.GetShared(IdentityService.CurrentUser);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,11 +4,14 @@
|
||||||
@using Moonlight.App.Services
|
@using Moonlight.App.Services
|
||||||
@using Moonlight.App.Database.Entities.Store
|
@using Moonlight.App.Database.Entities.Store
|
||||||
@using Moonlight.App.Repositories
|
@using Moonlight.App.Repositories
|
||||||
|
@using Moonlight.App.Services.ServiceManage
|
||||||
|
|
||||||
@inject ConfigService ConfigService
|
@inject ConfigService ConfigService
|
||||||
@inject StoreService StoreService
|
@inject StoreService StoreService
|
||||||
@inject IdentityService IdentityService
|
@inject IdentityService IdentityService
|
||||||
@inject AlertService AlertService
|
@inject AlertService AlertService
|
||||||
|
@inject NavigationManager Navigation
|
||||||
|
@inject ServiceService ServiceService
|
||||||
@inject Repository<Product> ProductRepository
|
@inject Repository<Product> ProductRepository
|
||||||
@inject Repository<Coupon> CouponRepository
|
@inject Repository<Coupon> CouponRepository
|
||||||
|
|
||||||
|
@ -73,7 +76,7 @@ TODO: Add 404 here
|
||||||
actualPrice = defaultPrice;
|
actualPrice = defaultPrice;
|
||||||
else
|
else
|
||||||
actualPrice = Math.Round(defaultPrice * SelectedCoupon.Percent / 100, 2);
|
actualPrice = Math.Round(defaultPrice * SelectedCoupon.Percent / 100, 2);
|
||||||
|
|
||||||
var currency = ConfigService.Get().Store.Currency;
|
var currency = ConfigService.Get().Store.Currency;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +84,7 @@ 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">Today</div>
|
<div class="text-gray-700 fw-semibold me-2">Today</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) @(actualPrice)</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex flex-stack">
|
<div class="d-flex flex-stack">
|
||||||
|
@ -136,7 +139,7 @@ TODO: Add 404 here
|
||||||
{
|
{
|
||||||
if (CanBeOrdered)
|
if (CanBeOrdered)
|
||||||
{
|
{
|
||||||
<button class="btn btn-primary w-100">Order for @(currency) @(actualPrice)</button>
|
<WButton OnClick="OnSubmit" Text="@($"Order for {currency} {actualPrice}")" CssClasses="btn btn-primary w-100"/>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -161,7 +164,7 @@ TODO: Add 404 here
|
||||||
private int DurationMultiplicator = 1;
|
private int DurationMultiplicator = 1;
|
||||||
|
|
||||||
private string CouponCode = "";
|
private string CouponCode = "";
|
||||||
|
|
||||||
private bool CanBeOrdered = false;
|
private bool CanBeOrdered = false;
|
||||||
private bool IsValidating = false;
|
private bool IsValidating = false;
|
||||||
private string ErrorMessage = "";
|
private string ErrorMessage = "";
|
||||||
|
@ -227,4 +230,22 @@ TODO: Add 404 here
|
||||||
|
|
||||||
await Revalidate();
|
await Revalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task OnSubmit()
|
||||||
|
{
|
||||||
|
if (SelectedProduct == null) // Prevent processing null
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Process the order with the selected values
|
||||||
|
var service = await StoreService
|
||||||
|
.Order
|
||||||
|
.Process(
|
||||||
|
IdentityService.CurrentUser,
|
||||||
|
SelectedProduct,
|
||||||
|
DurationMultiplicator,
|
||||||
|
SelectedCoupon
|
||||||
|
);
|
||||||
|
|
||||||
|
Navigation.NavigateTo("/service/" + service.Id);
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue