Browse Source

Dashboard WIP Commit 2

pull/7/head
Benjamin Arhen 2 years ago
parent
commit
8a92ae36b4
  1. 1
      Client/Biskilog Accounting.Client.csproj
  2. 2
      Client/Elements/Footer.razor
  3. 16
      Client/Elements/Sidebar.razor.cs
  4. 16
      Client/Layouts/MainLayout.razor
  5. 24
      Client/Layouts/MainLayout.razor.cs
  6. 13
      Client/Pages/Auth/Login.razor
  7. 107
      Client/Pages/Auth/Login.razor.cs
  8. 119
      Client/Pages/Dashboard/Dashboard.razor
  9. 42
      Client/Pages/Dashboard/Dashboard.razor.cs
  10. 28
      Client/Pages/Dashboard/Elements/AnalyticsItemSmall.razor
  11. 16
      Client/Pages/Dashboard/Elements/AnalyticsItemSmall.razor.cs
  12. 2
      Client/Pages/Dashboard/Elements/WelcomeCard.razor
  13. 17
      Client/Pages/Dashboard/Elements/WelcomeCard.razor.cs
  14. 4
      Client/Program.cs
  15. 16
      Client/wwwroot/assets/vendor/css/core.css
  16. 20
      Client/wwwroot/assets/vendor/css/theme-default.css
  17. 3
      Server/Biskilog Accounting.Server.csproj
  18. 9
      Server/Controllers/AnalyticsController.cs
  19. 42
      Server/Services/AnalyticalService.cs
  20. 1
      Shared/Biskilog Accounting.Shared.csproj
  21. 16
      Shared/CustomModels/TradeSummary.cs
  22. 6
      Shared/Interfaces/IAnalytics.cs
  23. 4
      Shared/Interfaces/ITokenService.cs
  24. 43
      Shared/ServiceRepo/TokenService.cs

1
Client/Biskilog Accounting.Client.csproj

@ -14,6 +14,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" /> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
<PackageReference Include="Blazored.LocalStorage" Version="4.3.0" />
<PackageReference Include="Blazored.SessionStorage" Version="2.3.0" /> <PackageReference Include="Blazored.SessionStorage" Version="2.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.5" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.5" PrivateAssets="all" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.5" PrivateAssets="all" />

2
Client/Elements/Footer.razor

@ -1,4 +1,4 @@
<footer class="content-footer footer bg-footer-theme"> <footer class="content-footer footer bg-footer-theme" style="position:relative;">
<div class="container-xxl d-flex flex-wrap justify-content-between py-2 flex-md-row flex-column"> <div class="container-xxl d-flex flex-wrap justify-content-between py-2 flex-md-row flex-column">
<div class="mb-2 mb-md-0"> <div class="mb-2 mb-md-0">
Biskilog © @DateTime.Now.Year Biskilog © @DateTime.Now.Year

16
Client/Elements/Sidebar.razor.cs

@ -12,14 +12,14 @@
} }
private void Collapse() private void Collapse()
{ {
if (m_class == "layout-menu") //if (m_class == "layout-menu")
{ //{
m_class = "layout-menu-collapsed"; // m_class = "layout-menu-collapsed";
} //}
else //else
{ //{
m_class = "layout-menu"; // m_class = "layout-menu";
} //}
} }
} }
} }

16
Client/Layouts/MainLayout.razor

@ -1,5 +1,11 @@
@inherits LayoutComponentBase @using Biskilog_Accounting.Client.Elements
@using Biskilog_Accounting.Client.Elements @using Biskilog_Accounting.Shared.Interfaces;
@inherits LayoutComponentBase
@inject NavigationManager m_navigationManager
@layout AuthLayout
@inject HttpClient m_http
@inject ITokenService m_tokenService
<!-- Layout wrapper --> <!-- Layout wrapper -->
<div class="layout-wrapper layout-content-navbar"> <div class="layout-wrapper layout-content-navbar">
<div class="layout-container"> <div class="layout-container">
@ -7,6 +13,7 @@
<Sidebar /> <Sidebar />
<!-- / Menu --> <!-- / Menu -->
<!-- Body Layout container --> <!-- Body Layout container -->
<div style="width: 100vh;display: flex;flex: 1 1 auto;flex-direction: column;flex-wrap: nowrap;">
<div class="layout-page"> <div class="layout-page">
<Headbar /> <Headbar />
<!-- Content wrapper --> <!-- Content wrapper -->
@ -16,13 +23,12 @@
@Body @Body
<!-- / Content --> <!-- / Content -->
<!-- Footer --> <!-- Footer -->
<Footer />
<!-- / Footer -->
</div> </div>
<!-- Content wrapper --> <!-- Content wrapper -->
</div> </div>
<!-- / Layout page --> <!-- / Layout page -->
<Footer />
</div>
</div> </div>
</div> </div>
<!-- / Layout wrapper --> <!-- / Layout wrapper -->

24
Client/Layouts/MainLayout.razor.cs

@ -0,0 +1,24 @@
using System.Net.Http.Headers;
namespace Biskilog_Accounting.Client.Layouts
{
public partial class MainLayout
{
protected override async Task OnInitializedAsync()
{
//Checks if user token is set else redirect user to login page
if (!await m_tokenService.IsTokenSet())
{
m_navigationManager.NavigateTo("/login");
}
else
{
string token = await m_tokenService.GetToken();
var authHeader = new AuthenticationHeaderValue("Bearer", token.Substring(6).Trim());
m_http.DefaultRequestHeaders.Authorization = authHeader;
}
return;
}
}
}

13
Client/Pages/Auth/Login.razor

@ -1,5 +1,10 @@
@layout AuthLayout @using Biskilog_Accounting.Shared.Interfaces
@inject NavigationManager m_navigationManager
@layout AuthLayout
@inject HttpClient m_http
@inject ITokenService m_tokenService
@page "/login" @page "/login"
<style> <style>
.gradient-custom-2 { .gradient-custom-2 {
/* fallback for old browsers */ /* fallback for old browsers */
@ -78,17 +83,17 @@
<div class="form-outline mb-4"> <div class="form-outline mb-4">
<input type="email" id="form2Example11" class="form-control" <input type="email" id="form2Example11" class="form-control"
placeholder="Phone number or email address" /> placeholder="Phone number or email address" @oninput="@(args => usernameInput(args.Value.ToString()))" @onkeydown="@Enter" />
<label class="form-label" for="form2Example11">Username</label> <label class="form-label" for="form2Example11">Username</label>
</div> </div>
<div class="form-outline mb-4"> <div class="form-outline mb-4">
<input type="password" id="form2Example22" class="form-control" /> <input type="password" id="form2Example22" class="form-control" @oninput="@(args => passwordInput(args.Value.ToString()))" @onkeydown="@Enter" />
<label class="form-label" for="form2Example22">Password</label> <label class="form-label" for="form2Example22">Password</label>
</div> </div>
<div class="text-center pt-1 mb-5 pb-1"> <div class="text-center pt-1 mb-5 pb-1">
<button class="btn btn-primary btn-block fa-lg gradient-custom-2 mb-3" type="button"> <button class="btn btn-primary btn-block fa-lg gradient-custom-2 mb-3" type="button" @onclick="pagaAuth">
Log Log
in in
</button> </button>

107
Client/Pages/Auth/Login.razor.cs

@ -1,7 +1,112 @@
namespace Biskilog_Accounting.Client.Pages.Auth using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components;
using System.Net.Http.Headers;
using Biskilog_Accounting.Shared.ClientContractModels;
using Biskilog_Accounting.Shared.Interfaces;
using System.Net.Http.Json;
namespace Biskilog_Accounting.Client.Pages.Auth
{ {
public partial class Login public partial class Login
{ {
private string m_email, m_password;
private bool m_remember { get; set; }
protected bool IsVisible { get; set; }
//NotificationMessage notificationMessage = new NotificationMessage();
private Userauth authenticatedUser;
/// <summary>
/// Handles the click or press event of the enter key
/// </summary>
/// <param name="e"></param>
public async void Enter(KeyboardEventArgs e)
{
if (e.Code == "Enter" || e.Code == "NumpadEnter")
{
await pagaAuth();
}
}
/// <summary>
/// Authenticates the user and determines the type of page layout to show
/// </summary>
/// <returns></returns>
async Task pagaAuth()
{
ShowSpinner();
try
{
authenticatedUser = new Userauth
{
UserId = 0,
Username = m_email,
Email = m_email,
Passsword = m_password
};
var responseMain = await m_http.PostAsJsonAsync("api/authentication/type-a", authenticatedUser);
if (responseMain.IsSuccessStatusCode)
{
string token = await responseMain.Content.ReadAsStringAsync();
await m_tokenService.SetToken(token, m_remember);
var authHeader = new AuthenticationHeaderValue("Bearer", token);
m_http.DefaultRequestHeaders.Authorization = authHeader;
m_navigationManager.NavigateTo("/");
}
else if (responseMain.StatusCode == System.Net.HttpStatusCode.BadRequest)
{
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
HideSpinner();
}
/// <summary>
/// Shows the loading spinner
/// </summary>
public void ShowSpinner()
{
IsVisible = true;
StateHasChanged();
}
/// <summary>
/// Hides the loading spinner
/// </summary>
public void HideSpinner()
{
IsVisible = false;
StateHasChanged();
}
/// <summary>
/// Shows a notification message
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
//async Task ShowNotification(NotificationMessage message)
//{
// notificationService.Notify(message);
// await InvokeAsync(() => { StateHasChanged(); });
//}
/// <summary>
/// Sets the username value
/// </summary>
/// <param name="value"></param>
void usernameInput(string value)
{
m_email = value;
StateHasChanged();
}
/// <summary>
/// Sets the password value
/// </summary>
/// <param name="value"></param>
void passwordInput(string value)
{
m_password = value;
StateHasChanged();
}
} }
} }

119
Client/Pages/Dashboard/Dashboard.razor

@ -1,72 +1,21 @@
@using Biskilog_Accounting.Client.Pages.Dashboard.Elements; @using Biskilog_Accounting.Client.Pages.Dashboard.Elements
@using Biskilog_Accounting.Shared.Interfaces
@inject HttpClient m_http
@inject ITokenService m_tokenService
@page "/" @page "/"
<div class="container-xxl flex-grow-1 container-p-y"> <div class="container-xxl flex-grow-1 container-p-y">
<div class="row"> <div class="row">
<div class="col-lg-8 mb-4 order-0"> <div class="col-lg-8 mb-4 order-0">
<WelcomeCard CurrentTradeSales="@CurrentTradeSale" PreviousTradeSales="@PreviousTradeSale" Username="@username"/> <WelcomeCard CurrentTradeSales="@m_tradeSummary.CurrentTradeSales" PreviousTradeSales="@m_tradeSummary.LastTradeSales" Username="@m_username" />
</div> </div>
<div class="col-lg-4 col-md-4 order-1"> <div class="col-lg-4 col-md-4 order-1">
<div class="row"> <div class="row">
<div class="col-lg-6 col-md-12 col-6 mb-4"> <div class="col-lg-6 col-md-12 col-6 mb-4">
<div class="card"> <AnalyticsItemSmall Icon="../assets/img/icons/unicons/chart-success.png" Title="Transactions" Value="2000" Percentage="70"/>
<div class="card-body">
<div class="card-title d-flex align-items-start justify-content-between">
<div class="avatar flex-shrink-0">
<img src="../assets/img/icons/unicons/chart-success.png"
alt="chart success"
class="rounded" />
</div>
<div class="dropdown">
<button class="btn p-0"
type="button"
id="cardOpt3"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
<i class="bx bx-dots-vertical-rounded"></i>
</button>
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="cardOpt3">
<a class="dropdown-item" href="javascript:void(0);">View More</a>
<a class="dropdown-item" href="javascript:void(0);">Delete</a>
</div>
</div>
</div>
<span class="fw-semibold d-block mb-1">Profit</span>
<h3 class="card-title mb-2">$12,628</h3>
<small class="text-success fw-semibold"><i class="bx bx-up-arrow-alt"></i> +72.80%</small>
</div>
</div>
</div> </div>
<div class="col-lg-6 col-md-12 col-6 mb-4"> <div class="col-lg-6 col-md-12 col-6 mb-4">
<div class="card"> <AnalyticsItemSmall Icon="../assets/img/icons/unicons/wallet-info.png" Title="Cancelled Sales" Value="4679" Percentage="28.42" />
<div class="card-body">
<div class="card-title d-flex align-items-start justify-content-between">
<div class="avatar flex-shrink-0">
<img src="../assets/img/icons/unicons/wallet-info.png"
alt="Credit Card"
class="rounded" />
</div>
<div class="dropdown">
<button class="btn p-0"
type="button"
id="cardOpt6"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
<i class="bx bx-dots-vertical-rounded"></i>
</button>
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="cardOpt6">
<a class="dropdown-item" href="javascript:void(0);">View More</a>
<a class="dropdown-item" href="javascript:void(0);">Delete</a>
</div>
</div>
</div>
<span>Sales</span>
<h3 class="card-title text-nowrap mb-1">$4,679</h3>
<small class="text-success fw-semibold"><i class="bx bx-up-arrow-alt"></i> +28.42%</small>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -129,60 +78,10 @@
<div class="col-12 col-md-8 col-lg-4 order-3 order-md-2"> <div class="col-12 col-md-8 col-lg-4 order-3 order-md-2">
<div class="row"> <div class="row">
<div class="col-6 mb-4"> <div class="col-6 mb-4">
<div class="card"> <AnalyticsItemSmall Icon="../assets/img/icons/unicons/paypal.png" Title="Out Of Stock" Value="2456" Percentage="-22" />
<div class="card-body">
<div class="card-title d-flex align-items-start justify-content-between">
<div class="avatar flex-shrink-0">
<img src="../assets/img/icons/unicons/paypal.png" alt="Credit Card" class="rounded" />
</div>
<div class="dropdown">
<button class="btn p-0"
type="button"
id="cardOpt4"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
<i class="bx bx-dots-vertical-rounded"></i>
</button>
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="cardOpt4">
<a class="dropdown-item" href="javascript:void(0);">View More</a>
<a class="dropdown-item" href="javascript:void(0);">Delete</a>
</div>
</div>
</div>
<span class="d-block mb-1">Payments</span>
<h3 class="card-title text-nowrap mb-2">$2,456</h3>
<small class="text-danger fw-semibold"><i class="bx bx-down-arrow-alt"></i> -14.82%</small>
</div>
</div>
</div> </div>
<div class="col-6 mb-4"> <div class="col-6 mb-4">
<div class="card"> <AnalyticsItemSmall Icon="../assets/img/icons/unicons/cc-primary.png" Title="Transactions" Value="2456" Percentage="-22" />
<div class="card-body">
<div class="card-title d-flex align-items-start justify-content-between">
<div class="avatar flex-shrink-0">
<img src="../assets/img/icons/unicons/cc-primary.png" alt="Credit Card" class="rounded" />
</div>
<div class="dropdown">
<button class="btn p-0"
type="button"
id="cardOpt1"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
<i class="bx bx-dots-vertical-rounded"></i>
</button>
<div class="dropdown-menu" aria-labelledby="cardOpt1">
<a class="dropdown-item" href="javascript:void(0);">View More</a>
<a class="dropdown-item" href="javascript:void(0);">Delete</a>
</div>
</div>
</div>
<span class="fw-semibold d-block mb-1">Transactions</span>
<h3 class="card-title mb-2">$14,857</h3>
<small class="text-success fw-semibold"><i class="bx bx-up-arrow-alt"></i> +28.14%</small>
</div>
</div>
</div> </div>
<!-- </div> <!-- </div>
<div class="row"> --> <div class="row"> -->

42
Client/Pages/Dashboard/Dashboard.razor.cs

@ -1,19 +1,41 @@
using Biskilog_Accounting.Shared.Interfaces; using Biskilog_Accounting.Shared.CustomModels;
using System.Net.Http.Json;
using System.Text.Json;
namespace Biskilog_Accounting.Client.Pages.Dashboard namespace Biskilog_Accounting.Client.Pages.Dashboard
{ {
public partial class Dashboard public partial class Dashboard
{ {
private readonly ITokenService m_tokenService; private TradeSummary m_tradeSummary { get; set; } = new TradeSummary();
private double CurrentTradeSale = 7000; private string m_username { get; set; } = string.Empty;
private double PreviousTradeSale = 2000.80; protected override async Task OnInitializedAsync()
private string username { get; set; }
public Dashboard() { }
public Dashboard(ITokenService a_tokenService) => m_tokenService = a_tokenService;
protected override void OnInitialized()
{ {
username = "username"; m_username = m_tokenService.GetUserNameFromToken(await m_tokenService.GetToken())!;
base.OnInitialized(); await GetSummary();
return;
}
/// <summary>
/// Gets the tade summary
/// </summary>
/// <returns></returns>
async Task GetSummary()
{
try
{
var response = await m_http.GetAsync("api/analytics/tradesummary");
if (response.IsSuccessStatusCode)
{
var jsonContent = await response.Content.ReadAsStringAsync();
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
var tradeSummary = JsonSerializer.Deserialize<TradeSummary>(jsonContent, options);
m_tradeSummary = tradeSummary;
StateHasChanged();
}
}catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
} }
} }
} }

28
Client/Pages/Dashboard/Elements/AnalyticsItemSmall.razor

@ -0,0 +1,28 @@
<div class="card">
<div class="card-body">
<div class="card-title d-flex align-items-start justify-content-between">
<div class="avatar flex-shrink-0">
<img src="@Icon"
alt="chart success"
class="rounded" />
</div>
<div class="dropdown">
<button class="btn p-0"
type="button"
id="cardOpt3"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
<i class="bx bx-dots-vertical-rounded"></i>
</button>
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="cardOpt3">
<a class="dropdown-item" href="javascript:void(0);">View More</a>
<a class="dropdown-item" href="javascript:void(0);">Delete</a>
</div>
</div>
</div>
<span class="fw-semibold d-block mb-1">@Title</span>
<h3 class="card-title mb-2">@Value</h3>
<small class="@(Percentage > 0 ? "text-success" : Percentage == 0 ? "bx-forward-arrow-alt" : "text-danger") fw-semibold"><i class="bx @(Percentage > 0 ? "bx-up-arrow-alt" : Percentage == 0 ? "bx-forward-arrow-alt" : "bx-down-arrow-alt")"></i> @Percentage %</small>
</div>
</div>

16
Client/Pages/Dashboard/Elements/AnalyticsItemSmall.razor.cs

@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Components;
namespace Biskilog_Accounting.Client.Pages.Dashboard.Elements
{
public partial class AnalyticsItemSmall
{
[Parameter]
public string Icon { get; set; } = string.Empty;
[Parameter]
public string Title { get; set; }= string.Empty;
[Parameter]
public double Value{ get; set; }
[Parameter]
public double Percentage { get;set; }
}
}

2
Client/Pages/Dashboard/Elements/WelcomeCard.razor

@ -5,7 +5,7 @@
<div class="d-flex align-items-end row"> <div class="d-flex align-items-end row">
<div class="col-sm-7"> <div class="col-sm-7">
<div class="card-body"> <div class="card-body">
<h5 class="card-title text-primary">@(CurrentTradeSales > PreviousTradeSales ? "Congratulations 🎉" : "Tough shift there") @Username!</h5> <h5 class="card-title text-primary">@(CurrentTradeSales > PreviousTradeSales ? "Congratulations 🎉" : PreviousTradeSales > CurrentTradeSales ? "Tough shift there" : "Well Done") @Username!</h5>
<p class="mb-4"> <p class="mb-4">
@m_remarks @m_remarks
</p> </p>

17
Client/Pages/Dashboard/Elements/WelcomeCard.razor.cs

@ -14,27 +14,28 @@ namespace Biskilog_Accounting.Client.Pages.Dashboard.Elements
private string m_remarks { get; set; } = ""; private string m_remarks { get; set; } = "";
protected override void OnInitialized() protected override void OnParametersSet()
{ {
CalculateStatistic(); CalculateStatistic();
base.OnInitialized();
} }
private void CalculateStatistic() private void CalculateStatistic()
{ {
double change = ((CurrentTradeSales - PreviousTradeSales) / PreviousTradeSales) * 100;
if (CurrentTradeSales > PreviousTradeSales) if (CurrentTradeSales > PreviousTradeSales)
{ {
double change = (CurrentTradeSales / (CurrentTradeSales + PreviousTradeSales)) * 100;
string changePercent = change.ToString("0.00") + "%"; string changePercent = change.ToString("0.00") + "%";
m_remarks = $"You made a total of {m_calculator.FormatMoneyWithCurrency(CurrentTradeSales)} in the current trade, {changePercent} more than the previous trade sales"; m_remarks = $"You made a total of {m_calculator.FormatMoneyWithCurrency(CurrentTradeSales)} in the current trade, {changePercent} more than the previous trade sales";
} }
else if (PreviousTradeSales > CurrentTradeSales) { else if (PreviousTradeSales > CurrentTradeSales)
double change = (CurrentTradeSales / (CurrentTradeSales + PreviousTradeSales)) * -100; {
string changePercent = change.ToString("0.00") + "%"; string changePercent = (change * -1).ToString("0.00") + "%";
m_remarks = $"You made a total of {m_calculator.FormatMoneyWithCurrency(CurrentTradeSales)} in the current trade, {changePercent} less than the previous trade sales"; m_remarks = $"You made a total of {m_calculator.FormatMoneyWithCurrency(CurrentTradeSales)} in the current trade, {changePercent} less than the previous trade sales";
} else { }
else
{
m_remarks = $"You made a total of {m_calculator.FormatMoneyWithCurrency(CurrentTradeSales)} in the current trade, same as the previous trade sales"; m_remarks = $"You made a total of {m_calculator.FormatMoneyWithCurrency(CurrentTradeSales)} in the current trade, same as the previous trade sales";
} }
StateHasChanged();
} }
} }
} }

4
Client/Program.cs

@ -2,6 +2,8 @@ using Biskilog_Accounting.Client;
using Biskilog_Accounting.ServiceRepo; using Biskilog_Accounting.ServiceRepo;
using Biskilog_Accounting.Shared.Interfaces; using Biskilog_Accounting.Shared.Interfaces;
using Biskilog_Accounting.Shared.ServiceRepo; using Biskilog_Accounting.Shared.ServiceRepo;
using Blazored.LocalStorage;
using Blazored.SessionStorage;
using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
@ -13,6 +15,8 @@ builder.Services.AddHttpClient("Biskilog_Accounting.ServerAPI", client => client
// Supply HttpClient instances that include access tokens when making requests to the server project // Supply HttpClient instances that include access tokens when making requests to the server project
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("Biskilog_Accounting.ServerAPI")); builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("Biskilog_Accounting.ServerAPI"));
builder.Services.AddBlazoredLocalStorageAsSingleton();
builder.Services.AddBlazoredSessionStorageAsSingleton();
builder.Services.AddScoped<ICalculator, CalculatorService>(); builder.Services.AddScoped<ICalculator, CalculatorService>();
builder.Services.AddScoped<ITokenService, TokenService>(); builder.Services.AddScoped<ITokenService, TokenService>();

16
Client/wwwroot/assets/vendor/css/core.css

@ -235,7 +235,7 @@ a {
} }
a:hover { a:hover {
color: #5f61e6; color: #11726d;
} }
a:not([href]):not([class]), a:not([href]):not([class]):hover { a:not([href]):not([class]), a:not([href]):not([class]):hover {
@ -2925,7 +2925,7 @@ textarea.form-control-lg {
} }
.btn-link:hover { .btn-link:hover {
color: #5f61e6; color: #11726d;
} }
.btn-link:disabled, .btn-link.disabled { .btn-link:disabled, .btn-link.disabled {
@ -3466,7 +3466,7 @@ textarea.form-control-lg {
} }
.nav-link:hover, .nav-link:focus { .nav-link:hover, .nav-link:focus {
color: #5f61e6; color: #11726d;
} }
.nav-link.disabled { .nav-link.disabled {
@ -6038,7 +6038,7 @@ textarea.form-control-lg {
} }
.link-primary:hover, .link-primary:focus { .link-primary:hover, .link-primary:focus {
color: #5f61e6; color: #11726d;
} }
.link-secondary { .link-secondary {
@ -15902,6 +15902,7 @@ html:not(.layout-menu-fixed) .menu-inner-shadow {
.menu-vertical .menu-block, .menu-vertical .menu-block,
.menu-vertical .menu-inner > .menu-item, .menu-vertical .menu-inner > .menu-item,
.menu-vertical .menu-inner > .menu-header { .menu-vertical .menu-inner > .menu-header {
max-width: 16.25rem;
width: 16.25rem; width: 16.25rem;
} }
@ -16273,7 +16274,6 @@ html:not(.layout-menu-fixed) .menu-inner-shadow {
.layout-content-navbar .layout-page { .layout-content-navbar .layout-page {
flex-basis: 100%; flex-basis: 100%;
flex-direction: column; flex-direction: column;
width: 0;
min-width: 0; min-width: 0;
max-width: 100%; max-width: 100%;
} }
@ -17778,6 +17778,7 @@ html:not(.layout-footer-fixed) .content-wrapper {
overflow-x: hidden; overflow-x: hidden;
max-height: 100vh; /* Adjust as needed */ max-height: 100vh; /* Adjust as needed */
} }
.layout-page::-webkit-scrollbar { .layout-page::-webkit-scrollbar {
width: 6px; width: 6px;
} }
@ -17789,11 +17790,14 @@ html:not(.layout-footer-fixed) .content-wrapper {
.layout-page::-webkit-scrollbar-thumb:hover { .layout-page::-webkit-scrollbar-thumb:hover {
background-color: #555; /* Color of the scrollbar thumb on hover */ background-color: #555; /* Color of the scrollbar thumb on hover */
}) }
)
.layout-page::-webkit-scrollbar-track { .layout-page::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgb(245,245,249); -webkit-box-shadow: inset 0 0 6px rgb(245,245,249);
} }
.layout-wrapper { .layout-wrapper {
overflow-y: auto; overflow-y: auto;
height: 100vh; height: 100vh;

20
Client/wwwroot/assets/vendor/css/theme-default.css

@ -100,7 +100,7 @@
} }
.text-body[href]:hover { .text-body[href]:hover {
color: #5f61e6 !important; color: #11726d !important;
} }
.bg-primary { .bg-primary {
@ -213,14 +213,14 @@ button.list-group-item-primary.active {
} }
.btn-primary:hover { .btn-primary:hover {
color: #fff; color: #fff;
background-color: #5f61e6; background-color: #11726d;
border-color: #5f61e6; border-color: #11726d;
transform: translateY(-1px); transform: translateY(-1px);
} }
.btn-check:focus + .btn-primary, .btn-primary:focus, .btn-primary.focus { .btn-check:focus + .btn-primary, .btn-primary:focus, .btn-primary.focus {
color: #fff; color: #fff;
background-color: #5f61e6; background-color: #11726d;
border-color: #5f61e6; border-color: #11726d;
transform: translateY(0); transform: translateY(0);
box-shadow: none; box-shadow: none;
} }
@ -243,15 +243,15 @@ button.list-group-item-primary.active {
} }
.btn-outline-primary:hover { .btn-outline-primary:hover {
color: #fff; color: #fff;
background-color: #5f61e6; background-color: #11726d;
border-color: #5f61e6; border-color: #11726d;
box-shadow: 0 0.125rem 0.25rem 0 rgba(105, 108, 255, 0.4); box-shadow: 0 0.125rem 0.25rem 0 rgba(105, 108, 255, 0.4);
transform: translateY(-1px); transform: translateY(-1px);
} }
.btn-check:focus + .btn-outline-primary, .btn-outline-primary:focus { .btn-check:focus + .btn-outline-primary, .btn-outline-primary:focus {
color: #fff; color: #fff;
background-color: #5f61e6; background-color: #11726d;
border-color: #5f61e6; border-color: #11726d;
box-shadow: none; box-shadow: none;
transform: translateY(0); transform: translateY(0);
} }
@ -296,7 +296,7 @@ button.list-group-item-primary.active {
} }
.nav .nav-link:hover, .nav .nav-link:focus { .nav .nav-link:hover, .nav .nav-link:focus {
color: #5f61e6; color: #11726d;
} }
.nav-pills .nav-link.active, .nav-pills .nav-link.active:hover, .nav-pills .nav-link.active:focus { .nav-pills .nav-link.active, .nav-pills .nav-link.active:hover, .nav-pills .nav-link.active:focus {

3
Server/Biskilog Accounting.Server.csproj

@ -9,6 +9,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Blazored.LocalStorage" Version="4.3.0" />
<PackageReference Include="Blazored.SessionStorage" Version="2.3.0" /> <PackageReference Include="Blazored.SessionStorage" Version="2.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.5" /> <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.5" />
<PackageReference Include="BCrypt.Net" Version="0.1.0" /> <PackageReference Include="BCrypt.Net" Version="0.1.0" />
@ -29,7 +30,7 @@
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.6" /> <PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.6" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" /> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.2" /> <PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

9
Server/Controllers/AnalyticsController.cs

@ -97,5 +97,14 @@ namespace Biskilog_Accounting.Server.Controllers
{ {
return m_analyticService.GetMostPurchasedItem(a_start, a_end); return m_analyticService.GetMostPurchasedItem(a_start, a_end);
} }
/// <summary>
/// Endpoint to return analysis on trade summary
/// </summary>
[Authorize]
[HttpGet, Route("tradesummary")]
public TradeSummary GetTradeSummary()
{
return m_analyticService.GetTradeSummary();
}
} }
} }

42
Server/Services/AnalyticalService.cs

@ -8,6 +8,7 @@ using Microsoft.Net.Http.Headers;
using MySqlConnector; using MySqlConnector;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data.Entity; using System.Data.Entity;
using System.Drawing.Drawing2D;
namespace Biskilog_Accounting.Server.Services namespace Biskilog_Accounting.Server.Services
{ {
@ -184,5 +185,46 @@ namespace Biskilog_Accounting.Server.Services
return m_context.Tblcarts.Where(t => t.Date >= a_start && t.Date <= a_end && accessiblebranches.Contains(t.BranchId)); return m_context.Tblcarts.Where(t => t.Date >= a_start && t.Date <= a_end && accessiblebranches.Contains(t.BranchId));
} }
public TradeSummary GetTradeSummary()
{
string token = m_httpContext.Request.Headers[HeaderNames.Authorization]!;
if (AuthEnums.Valid == m_tokenService.ValidateToken(token))
{
IEnumerable<string> accessiblebranches = m_tokenService.BranchIds(token);
using (var command = m_context.Database.GetDbConnection().CreateCommand())
{
command.CommandText = "CALL GetTradeSummary(@p0)";
command.Parameters.Add(new MySqlParameter("@p0", string.Join(", ", accessiblebranches.ToArray())));
m_context.Database.OpenConnection();
using (var reader = command.ExecuteReader())
{
int i = 0;
TradeSummary result = new TradeSummary();
while (reader.Read())
{
if (i == 0)
{
result.CurrentTradeDate = reader.GetDateTime(0);
result.CurrentTradeSales = reader.GetDouble(1);
}
else if (i == 1)
{
result.LastTradeDate = reader.GetDateTime(0);
result.LastTradeSales = reader.GetDouble(1);
}
i++;
}
return result;
}
}
}
return new TradeSummary();
}
} }
} }

1
Shared/Biskilog Accounting.Shared.csproj

@ -8,6 +8,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" /> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
<PackageReference Include="Blazored.LocalStorage" Version="4.3.0" />
<PackageReference Include="Blazored.SessionStorage" Version="2.3.0" /> <PackageReference Include="Blazored.SessionStorage" Version="2.3.0" />
<PackageReference Include="EntityFramework" Version="6.4.4" /> <PackageReference Include="EntityFramework" Version="6.4.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.5" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.5" />

16
Shared/CustomModels/TradeSummary.cs

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Biskilog_Accounting.Shared.CustomModels
{
public class TradeSummary
{
public DateTime CurrentTradeDate { get; set; } = DateTime.Now;
public DateTime LastTradeDate { get; set; } = DateTime.Now.AddDays(-1);
public double CurrentTradeSales { get; set; } = 0;
public double LastTradeSales { get; set; } = 0;
}
}

6
Shared/Interfaces/IAnalytics.cs

@ -49,6 +49,10 @@ namespace Biskilog_Accounting.Shared.Interfaces
/// <param name="a_end"></param> /// <param name="a_end"></param>
/// <returns></returns> /// <returns></returns>
IEnumerable<ProductPriceChange> GetPriceChanges(DateTime a_start, DateTime a_end); IEnumerable<ProductPriceChange> GetPriceChanges(DateTime a_start, DateTime a_end);
//void SetContraints(string a_token); /// <summary>
/// Fetch the trade summary which is made of the total sales made currently and previous trade
/// </summary>
/// <returns></returns>
TradeSummary GetTradeSummary();
} }
} }

4
Shared/Interfaces/ITokenService.cs

@ -14,5 +14,9 @@ namespace Biskilog_Accounting.Shared.Interfaces
bool? GetComparison(string a_token); bool? GetComparison(string a_token);
IEnumerable<string> BranchIds(string a_token); IEnumerable<string> BranchIds(string a_token);
string? GetAllBranch(string a_token); string? GetAllBranch(string a_token);
Task SetToken(string a_token, bool a_remember);
Task<string> GetToken();
Task ClearToken();
Task<bool> IsTokenSet();
} }
} }

43
Shared/ServiceRepo/TokenService.cs

@ -1,6 +1,8 @@
using Biskilog_Accounting.Shared.ClientContractModels; using Biskilog_Accounting.Shared.ClientContractModels;
using Biskilog_Accounting.Shared.Enums; using Biskilog_Accounting.Shared.Enums;
using Biskilog_Accounting.Shared.Interfaces; using Biskilog_Accounting.Shared.Interfaces;
using Blazored.LocalStorage;
using Blazored.SessionStorage;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
@ -12,11 +14,13 @@ namespace Biskilog_Accounting.ServiceRepo
public class TokenService : ITokenService public class TokenService : ITokenService
{ {
private IConfiguration m_configuration { get; } private IConfiguration m_configuration { get; }
private readonly Blazored.SessionStorage.ISessionStorageService m_sessionStorage; private readonly ISessionStorageService m_sessionStorage;
public TokenService(IConfiguration a_configuration, Blazored.SessionStorage.ISessionStorageService sessionStorage) private readonly ILocalStorageService m_localStorage;
public TokenService(IConfiguration a_configuration, ISessionStorageService a_sessionStorage = null, ILocalStorageService a_localStorage = null)
{ {
m_configuration = a_configuration; m_configuration = a_configuration;
m_sessionStorage = sessionStorage; m_sessionStorage = a_sessionStorage;
m_localStorage = a_localStorage;
} }
/// <summary> /// <summary>
@ -198,5 +202,38 @@ namespace Biskilog_Accounting.ServiceRepo
} }
return branchIds.AsEnumerable(); return branchIds.AsEnumerable();
} }
public async Task SetToken(string a_token, bool a_remember)
{
if (a_remember)
{
await m_localStorage.SetItemAsStringAsync("token", $"Bearer {a_token}");
}
else
{
await m_sessionStorage.SetItemAsStringAsync("token", $"Bearer {a_token}");
}
}
public async Task<string> GetToken()
{
string token = await m_localStorage.GetItemAsStringAsync("token");
if (String.IsNullOrEmpty(token))
{
token = await m_sessionStorage.GetItemAsStringAsync("token");
}
return token;
}
public async Task ClearToken()
{
await m_localStorage.ClearAsync();
await m_sessionStorage.ClearAsync();
}
public async Task<bool> IsTokenSet()
{
return await m_localStorage.ContainKeyAsync("token") || await m_sessionStorage.ContainKeyAsync("token");
}
} }
} }

Loading…
Cancel
Save