login-page-rework #13

Merged
barhen merged 6 commits from login-page-rework into dev 1 year ago
  1. 2
      Client/Biskilog Accounting.Client.csproj
  2. 2
      Client/Elements/Footer.razor
  3. 10
      Client/Elements/Footer.razor.cs
  4. 30
      Client/Elements/Headbar.razor
  5. 32
      Client/Elements/Headbar.razor.cs
  6. 42
      Client/Elements/Headbar.razor.css
  7. 2
      Client/Elements/Sidebar.razor
  8. 8
      Client/Elements/Sidebar.razor.cs
  9. 2
      Client/Layouts/AuthLayout.razor
  10. 9
      Client/Layouts/MainLayout.razor
  11. 13
      Client/Layouts/MainLayout.razor.cs
  12. 2
      Client/Models/NavItem.cs
  13. 24
      Client/Pages/Auth/Components/PhoneAuthTab.razor
  14. 214
      Client/Pages/Auth/Components/PhoneAuthTab.razor.css
  15. 24
      Client/Pages/Auth/Components/TabContainer.razor
  16. 153
      Client/Pages/Auth/Components/TabContainer.razor.css
  17. 29
      Client/Pages/Auth/Components/UsernameTab.razor
  18. 48
      Client/Pages/Auth/Components/UsernameTab.razor.cs
  19. 195
      Client/Pages/Auth/Components/UsernameTab.razor.css
  20. 157
      Client/Pages/Auth/Login.razor
  21. 185
      Client/Pages/Auth/Login.razor.css
  22. 74
      Client/Pages/Dashboard/Elements/ProductPriceHistory.razor.cs
  23. BIN
      Client/wwwroot/assets/img/google-image.png
  24. 1
      Client/wwwroot/css/signin.css
  25. BIN
      Client/wwwroot/fonts/AvenirLTStd-Black.otf
  26. BIN
      Client/wwwroot/fonts/AvenirLTStd-Book.otf
  27. BIN
      Client/wwwroot/fonts/AvenirLTStd-Roman.otf
  28. BIN
      Client/wwwroot/fonts/OstrichSans-Black.otf
  29. BIN
      Client/wwwroot/fonts/OstrichSans-Bold.otf
  30. BIN
      Client/wwwroot/fonts/Rockwill.otf
  31. 2
      Server/Services/AnalyticalService.cs
  32. 2
      Shared/Interfaces/ISearchService.cs
  33. 5
      Shared/ServiceRepo/SearchService.cs
  34. 1
      Shared/ServiceRepo/TokenService.cs

2
Client/Biskilog Accounting.Client.csproj

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>

2
Client/Elements/Footer.razor

@ -1,4 +1,4 @@
<footer class="content-footer footer bg-footer-theme" style="position:relative;">
<footer class="content-footer footer bg-footer-theme" style="position:relative;" @onclick=OnClickCallback>
<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">
Biskilog © @DateTime.Now.Year

10
Client/Elements/Footer.razor.cs

@ -0,0 +1,10 @@
using Microsoft.AspNetCore.Components;
namespace Biskilog_Accounting.Client.Elements
{
public partial class Footer
{
[Parameter]
public EventCallback OnClickCallback { get; set; }
}
}

30
Client/Elements/Headbar.razor

@ -1,6 +1,8 @@
@using Biskilog_Accounting.Shared.Interfaces;
@inject ISearchService m_searchService
@inject IJSRuntime JSRuntime;
@inject IJSRuntime JSRuntime
@inject ITokenService m_tokenService
@inject NavigationManager m_navigationManager
<!-- Navbar -->
<nav class="layout-navbar container-xxl navbar navbar-expand-xl navbar-detached align-items-center bg-navbar-theme"
@ -13,23 +15,29 @@
<div class="navbar-nav-right d-flex align-items-center" id="navbar-collapse">
<!-- Search -->
<div class="navbar-nav align-items-center" style="width:90%;">
<div class="nav-item d-flex align-items-center" style="width:90%;">
<div class="navbar-nav align-items-center" style="width:100%;">
<div class="nav-item d-flex align-items-center" style="width:100%;">
<i class="bx bx-search fs-4 lh-0"></i>
<input type="text"
id="mainSearch"
id="mainSearch"
class="form-control border-0 shadow-none"
placeholder="Search..."
aria-label="Search..."
@oninput="@(args => SearchInput(args.Value.ToString()))"/>
aria-label="Search..."
@oninput="@(args => SearchInput(args.Value.ToString()))" />
</div>
</div>
<!-- /Search -->
<div id="Profile" @onclick="@ToggleDropdown">
<i class="bx bxs-user fs-4 lh-0"></i>
<ul class="navbar-nav flex-row align-items-center ms-auto">
</ul>
<ul class=@m_dropDownClass style="width: 150px;">
<li><a href="#">Profile</a></li>
<li><a href="#">Account</a></li>
<li><a href="#">Settings</a></li>
<li><a id="logOutBtn" href="javascript:void(0)" @onclick=@LogOut>Log Out</a></li>
</ul>
</div>
</div>
</div>
</nav>
<!-- / Navbar -->

32
Client/Elements/Headbar.razor.cs

@ -1,15 +1,40 @@
using Microsoft.JSInterop;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace Biskilog_Accounting.Client.Elements
{
public partial class Headbar
{
private const string c_show = "show";
private const string c_hide = "hidden";
private string m_dropDownClass = c_hide;
protected override void OnInitialized()
{
m_searchService.ClearTextBox += ClearTextBox;
m_searchService.CloseMenus += HideProfileDropdown;
base.OnInitialized();
}
private void HideProfileDropdown(string a_sender)
{
if (a_sender != "Profile")
{
m_dropDownClass = c_hide;
StateHasChanged();
}
}
private void ToggleDropdown()
{
if (m_dropDownClass == c_hide)
{
m_dropDownClass = c_show;
}
else
{
m_dropDownClass = c_hide;
}
}
private void ClearTextBox()
{
ClearInputField("mainSearch");
@ -22,5 +47,10 @@ namespace Biskilog_Accounting.Client.Elements
{
m_searchService.PerformSearch(a_key);
}
private async Task LogOut()
{
await m_tokenService.ClearToken();
m_navigationManager.NavigateTo("/login");
}
}
}

42
Client/Elements/Headbar.razor.css

@ -0,0 +1,42 @@
ul {
flex-direction: column;
background: #3f7672;
top: 1rem;
border-radius: 10px;
list-style-type: none;
margin: 0;
padding: 0;
position: absolute;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
z-index: 1;
}
li {
padding: 8px;
border-bottom: 1px solid #ffffff; /* Border between items */
}
li:last-child {
border-bottom: none; /* Remove border from the last item */
}
a {
color: #ffffff; /* Dropdown item text color */
text-decoration: none;
display: block;
padding: 8px;
cursor:pointer;
}
a:hover {
color:#003445; /* Hover background color */
}
/* Show the dropdown when the container is clicked */
.show ul {
display: block;
}
.hidden {
display: none;
}

2
Client/Elements/Sidebar.razor

@ -1,7 +1,7 @@
@using Biskilog_Accounting.Client.Models;
<aside id="layout-menu" class="@m_class menu-vertical menu bg-menu-theme">
<div class="app-brand">
<a href="index.html" class="app-brand-link">
<a href="index.html" class="app-brand-link" @onclick=OnClickCallback>
<span class="app-brand-logo">
<img src="../icon-512.png" alt="BISKILOG Logo" style="height:60px;">
</span>

8
Client/Elements/Sidebar.razor.cs

@ -1,17 +1,23 @@
namespace Biskilog_Accounting.Client.Elements
using Microsoft.AspNetCore.Components;
namespace Biskilog_Accounting.Client.Elements
{
public partial class Sidebar
{
[Parameter]
public EventCallback OnClickCallback { get; set; }
private double m_activeId = 1;
private string m_class { get; set; } = "layout-menu";
private void HandleMenuClick(double a_selectedId)
{
m_activeId = a_selectedId;
OnClickCallback.InvokeAsync();
StateHasChanged();
}
private void Collapse()
{
OnClickCallback.InvokeAsync();
//if (m_class == "layout-menu")
//{
// m_class = "layout-menu-collapsed";

2
Client/Layouts/AuthLayout.razor

@ -1,6 +1,6 @@
@namespace Biskilog_Accounting
@inherits LayoutComponentBase
<main style="background-color: #eee;height:100vh;">
<main style="background-color: #fff;height:100vh;">
@Body
</main>

9
Client/Layouts/MainLayout.razor

@ -11,6 +11,7 @@
@inject ICompanyInfo m_companyInfo
@inject IUser m_userService
@inject ICustomer m_customerService
@inject ISearchService m_searchSearch
<RadzenDialog />
<RadzenContextMenu />
@ -18,15 +19,15 @@
<div class="layout-wrapper layout-content-navbar">
<div class="layout-container">
<!-- Menu -->
<Sidebar />
<Sidebar OnClickCallback=@(() => Click("sidebar")) />
<!-- / Menu -->
<!-- Body Layout container -->
<div style="width: 100vh;display: flex;flex: 1 1 auto;flex-direction: column;flex-wrap: nowrap;">
<div class="layout-page">
<Headbar />
<Headbar/>
<!-- Content wrapper -->
<div class="content-wrapper">
<div class="container-xxl flex-grow-1 container-p-y">
<div class="container-xxl flex-grow-1 container-p-y" @onclick=@(() => Click("body"))>
<!-- Content -->
@Body
<!-- / Content -->
@ -35,7 +36,7 @@
<!-- Content wrapper -->
</div>
<!-- / Layout page -->
<Footer />
<Footer OnClickCallback=@(() => Click("footer")) />
</div>
</div>
</div>

13
Client/Layouts/MainLayout.razor.cs

@ -1,4 +1,5 @@
using Biskilog_Accounting.Client.Pages.Transactions.Elements;
using Biskilog_Accounting.Shared.Enums;
using Radzen;
using System.Net.Http.Headers;
@ -31,7 +32,7 @@ namespace Biskilog_Accounting.Client.Layouts
fetchBrands,
fetchCategories,
fetchRecentSale,
fetchUsers
fetchUsers
);
});
@ -55,6 +56,12 @@ namespace Biskilog_Accounting.Client.Layouts
else
{
string token = await m_tokenService.GetToken();
if (m_tokenService.ValidateToken(token) != AuthEnums.Valid)
{
m_navigationManager.NavigateTo("/login");
}
var authHeader = new AuthenticationHeaderValue("Bearer", token.Substring(6).Trim());
m_http.DefaultRequestHeaders.Authorization = authHeader;
}
@ -68,5 +75,9 @@ namespace Biskilog_Accounting.Client.Layouts
ShowMask = true
});
}
private void Click(string a_sender)
{
m_searchSearch.FireCloseMenus(a_sender);
}
}
}

2
Client/Models/NavItem.cs

@ -119,7 +119,7 @@
{
Id = 4,
Description = "Customers",
Icon = "bxs-user",
Icon = "bxs-group",
Link = "customers",
Title = "Customers"
},

24
Client/Pages/Auth/Components/PhoneAuthTab.razor

@ -0,0 +1,24 @@
@page "/phoneauthtab"
<form>
<div class="input-div">
<input type="tel" id="phone" name="telphone" placeholder="888 888 8888" pattern="[0-9]{3} [0-9]{3} [0-9]{4}" maxlength="12" class="input-box" /><br />
<span class="hidden">x</span>
</div>
<div class="input-div">
<input type="number" id="code" class="input-box" placeholder="Enter OTP" maxlength="6" /><br />
<span class="hidden">x</span>
</div>
</form>
<button type="button" class="sign-in-gbutton-colored" >
<div style="display: inline-flex;">
<i></i>
<span class="sign-in-gbutton-title">Submit</span>
</div>
</button>
@code {
}

214
Client/Pages/Auth/Components/PhoneAuthTab.razor.css

@ -0,0 +1,214 @@
@font-face {
font-family: "titleFont";
src: url("../fonts/Rockwill.otf");
font-weight: bold;
}
.form-section {
border: rgb(186, 190, 197) 0.5px solid;
min-height: 690px;
display: flex;
flex-direction: column;
align-items: center;
margin: auto;
max-width: 400px;
position: relative;
}
.form-section h2 {
font-size: 24pt;
font-weight: 800;
line-height: 32px;
margin-bottom: 17px;
margin-top: 20px;
color: rgb(57, 58, 61);
}
.title-section {
display: flex;
flex-direction: column;
align-items: center;
font-size: medium;
font-family: "titleFont";
color: rgb(186, 190, 197);
}
.title-section a {
font-size: 30pt;
text-decoration: none;
color: rgb(7, 7, 154);
}
.title-section a img {
margin: auto !important;
height: 80px !important;
max-width: 80px !important;
display: flex;
justify-content: space-between;
text-decoration: none;
}
.sign-in-gbutton {
padding: 0px 10px;
background: rgb(255, 255, 255);
border: 2px solid rgb(186, 190, 197);
border-radius: 2px;
white-space: nowrap;
width: 100%;
max-width: 310px;
height: inherit;
margin: 20px;
max-height: 45px;
cursor: pointer;
color: rgb(57, 58, 61);
}
.sign-in-gbutton-colored {
padding: 0px 10px;
background: rgb(7, 7, 154);
border: 1px solid rgb(186, 190, 197);
border-radius: 2px;
white-space: nowrap;
color: white;
width: 50%;
max-width: 310px;
height: 40px;
margin: 20px;
max-height: 45px;
cursor: pointer;
transition: width 1.5s ease;
}
.sign-in-gbutton-colored div > i {
width: 20px;
margin: 5px;
background-repeat: no-repeat;
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNOCAwYzIuNzk2IDAgNC44OTMgMi4xODQgNC45OTYgNS4yNjJMMTMgNS41VjdoMWEyIDIgMCAwIDEgMiAydjlhMiAyIDAgMCAxLTIgMkgyYTIgMiAwIDAgMS0yLTJWOWEyIDIgMCAwIDEgMi0yaDFWNS41QzMgMi4yOTggNS4xMzMgMCA4IDBabTYgOUgydjloMTJWOVptLTYgMmEyIDIgMCAwIDEgMS4wMDEgMy43MzJMOSAxNS4yYzAgLjQ0Mi0uNDQ4LjgtMSAuOC0uNTEzIDAtLjkzNi0uMzA5LS45OTMtLjcwN0w3IDE1LjJ2LS40NjhBMiAyIDAgMCAxIDggMTFabTAtOUM2LjMzMiAyIDUuMDg4IDMuMjc3IDUuMDA1IDUuMjgyTDUgNS41VjdoNlY1LjVDMTEgMy4zNyA5LjcyOCAyIDggMloiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==");
}
.sign-in-gbutton-colored:hover {
width: 100%;
}
.sign-in-gbutton-title {
font-size: 15px;
margin: 7px;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
}
.sign-in-gbutton-image {
height: 35px;
}
.line-styled-div ::before,
.line-styled-div ::after {
content: "";
position: absolute;
border-bottom: 2px solid rgb(186, 190, 197);
width: 150px;
}
.line-styled-div ::before {
margin: 7px 40px;
}
.line-styled-div ::after {
margin: 7px -190px;
}
.line-styled-div span {
position: relative;
display: inline-block;
color: rgb(186, 190, 197);
font: 1em sans-serif;
}
.tab button {
font-size: 13px;
font-weight: bold;
color: rgb(132, 134, 139);
background-color: inherit;
float: left;
border: none;
outline: none;
cursor: pointer;
padding: 14px 16px;
transition: 0.3s;
min-width: 180px;
}
.tab button :hover {
border-bottom: 1px solid rgb(7, 7, 154);
}
.tab button.active {
border-bottom: 3px solid rgb(7, 7, 154);
}
.tabcontent {
display: none;
padding: 6px 12px;
border: 1px solid #ccc;
border-top: none;
}
.input-box {
font-size: 15px;
width: 280px;
border: 0px;
outline: none;
background: transparent;
}
.input-box :active {
border: 0px;
}
.input-div {
margin-top: 28px;
border: 1.9px solid rgb(132, 134, 139);
min-height: 20px;
padding: 9px;
border-radius: 8px;
display: flex;
}
.input-div span {
width: 20px;
height: 20px;
border-radius: 30px;
color: #d52b1f;
border: 1px solid #d52b1f;
text-align: center;
font: 1rem sans-serif;
}
.input-div.error {
border: 1.9px solid #d52b1f;
background-color: #fbeceb;
}
.input-div.error input {
background-color: #fbeceb;
}
.input-div-check {
margin-top: 15px;
font-size: 18px;
display: flex;
text-align: center;
}
.input-div-check input {
width: 30px;
height: 25px;
display: flex;
margin-right: 25px;
}
.input-div-check label {
margin-top: 4.5px;
color: rgb(132, 134, 139);
text-align: center;
}

24
Client/Pages/Auth/Components/TabContainer.razor

@ -0,0 +1,24 @@

<div class="tab">
<button class="@(GetTabClass(0))" onclick="@(() => SwitchTabs(0))">Email or User ID</button>
<button class="@(GetTabClass(1))" onclick="@(() => SwitchTabs(1))">Phone</button>
</div>
@if (activeTab == 0)
{
<UsernameTab />
}
else if (activeTab == 1)
{
<PhoneAuthTab />
}
@code {
private int activeTab = 0;
private string GetTabClass(int tabId) => tabId == activeTab ? "tablinks active" : "tablinks";
private void SwitchTabs(int tabId)
{
activeTab = tabId;
}
}

153
Client/Pages/Auth/Components/TabContainer.razor.css

@ -0,0 +1,153 @@
@font-face {
font-family: "titleFont";
src: url("../fonts/Rockwill.otf");
font-weight: bold;
}
.form-section {
border: rgb(186, 190, 197) 0.5px solid;
min-height: 690px;
display: flex;
flex-direction: column;
align-items: center;
margin: auto;
max-width: 400px;
position: relative;
}
.form-section h2 {
font-size: 24pt;
font-weight: 800;
line-height: 32px;
margin-bottom: 17px;
margin-top: 20px;
color: rgb(57, 58, 61);
}
.title-section {
display: flex;
flex-direction: column;
align-items: center;
font-size: medium;
font-family: "titleFont";
color: rgb(186, 190, 197);
}
.title-section a {
font-size: 30pt;
text-decoration: none;
color: rgb(7, 7, 154);
}
.title-section a img {
margin: auto !important;
height: 80px !important;
max-width: 80px !important;
display: flex;
justify-content: space-between;
text-decoration: none;
}
.sign-in-gbutton {
padding: 0px 10px;
background: rgb(255, 255, 255);
border: 2px solid rgb(186, 190, 197);
border-radius: 2px;
white-space: nowrap;
width: 100%;
max-width: 310px;
height: inherit;
margin: 20px;
max-height: 45px;
cursor: pointer;
color: rgb(57, 58, 61);
}
.sign-in-gbutton-colored {
padding: 0px 10px;
background: rgb(7, 7, 154);
border: 1px solid rgb(186, 190, 197);
border-radius: 2px;
white-space: nowrap;
color: white;
width: 50%;
max-width: 310px;
height: 40px;
margin: 20px;
max-height: 45px;
cursor: pointer;
transition: width 1.5s ease;
}
.sign-in-gbutton-colored div > i {
width: 20px;
margin: 5px;
background-repeat: no-repeat;
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNOCAwYzIuNzk2IDAgNC44OTMgMi4xODQgNC45OTYgNS4yNjJMMTMgNS41VjdoMWEyIDIgMCAwIDEgMiAydjlhMiAyIDAgMCAxLTIgMkgyYTIgMiAwIDAgMS0yLTJWOWEyIDIgMCAwIDEgMi0yaDFWNS41QzMgMi4yOTggNS4xMzMgMCA4IDBabTYgOUgydjloMTJWOVptLTYgMmEyIDIgMCAwIDEgMS4wMDEgMy43MzJMOSAxNS4yYzAgLjQ0Mi0uNDQ4LjgtMSAuOC0uNTEzIDAtLjkzNi0uMzA5LS45OTMtLjcwN0w3IDE1LjJ2LS40NjhBMiAyIDAgMCAxIDggMTFabTAtOUM2LjMzMiAyIDUuMDg4IDMuMjc3IDUuMDA1IDUuMjgyTDUgNS41VjdoNlY1LjVDMTEgMy4zNyA5LjcyOCAyIDggMloiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==");
}
.sign-in-gbutton-colored:hover {
width: 100%;
}
.sign-in-gbutton-title {
font-size: 15px;
margin: 7px;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
}
.sign-in-gbutton-image {
height: 35px;
}
.line-styled-div ::before,
.line-styled-div ::after {
content: "";
position: absolute;
border-bottom: 2px solid rgb(186, 190, 197);
width: 150px;
}
.line-styled-div ::before {
margin: 7px 40px;
}
.line-styled-div ::after {
margin: 7px -190px;
}
.line-styled-div span {
position: relative;
display: inline-block;
color: rgb(186, 190, 197);
font: 1em sans-serif;
}
.tab button {
font-size: 13px;
font-weight: bold;
color: rgb(132, 134, 139);
background-color: inherit;
float: left;
border: none;
outline: none;
cursor: pointer;
padding: 14px 16px;
transition: 0.3s;
min-width: 180px;
}
.tab button :hover {
border-bottom: 1px solid rgb(7, 7, 154);
}
.tab button.active {
border-bottom: 3px solid rgb(7, 7, 154);
}
.tabcontent {
display: none;
padding: 6px 12px;
border: 1px solid #ccc;
border-top: none;
}

29
Client/Pages/Auth/Components/UsernameTab.razor

@ -0,0 +1,29 @@
@using Biskilog_Accounting.Shared.Interfaces
@using Biskilog_Accounting.Client.Pages.Auth.Components
@inject NavigationManager m_navigationManager
@layout AuthLayout
@inject HttpClient m_http
@inject ITokenService m_tokenService
<form>
<div class="input-div">
<input type="text" id="emailId" class="input-box" placeholder="Email or User ID" autocomplete="username email" @oninput="@(args => usernameInput(args.Value.ToString()))" @onkeydown="@Enter" /><br />
<span class="@m_usernameErrorClass">x</span>
</div>
<div class="input-div">
<input type="password" id="password" class="input-box" placeholder="Password" autocomplete="current-password" @oninput="@(args => passwordInput(args.Value.ToString()))" @onkeydown="@Enter" /><br />
<span class="@m_passwordErrorClass">x</span>
</div>
<div class="input-div-check">
<input type="checkbox" id="rememberme-checkbox" name="rememberme-checkbox" class="input-box" /><br />
<label for="rememberme-checkbox">Remember me</label>
</div>
</form>
<button type="button" class="sign-in-gbutton-colored" @onclick="Authenticate">
<div style="display: inline-flex;">
<i></i>
<span class="sign-in-gbutton-title">Sign In</span>
</div>
</button>
<a class="reset-link">Forgot your password?</a>

48
Client/Pages/Auth/Login.razor.cs → Client/Pages/Auth/Components/UsernameTab.razor.cs

@ -1,17 +1,19 @@
using Microsoft.AspNetCore.Components.Web;
using Biskilog_Accounting.Shared.ClientContractModels;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components;
using static System.Net.WebRequestMethods;
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
namespace Biskilog_Accounting.Client.Pages.Auth.Components
{
public partial class Login
public partial class UsernameTab
{
private string m_email, m_password;
private bool m_remember { get; set; }
protected bool IsVisible { get; set; }
private string m_usernameErrorClass = "hidden";
private string m_passwordErrorClass = "hidden";
//NotificationMessage notificationMessage = new NotificationMessage();
private Userauth authenticatedUser;
/// <summary>
@ -22,15 +24,43 @@ namespace Biskilog_Accounting.Client.Pages.Auth
{
if (e.Code == "Enter" || e.Code == "NumpadEnter")
{
await pagaAuth();
FieldValidation();
if (!string.IsNullOrEmpty(m_email) && !string.IsNullOrEmpty(m_password))
{
await Authenticate();
}
}
}
/// <summary>
/// Validates the username and password input fields and shows the appropriate indicators
/// </summary>
private void FieldValidation()
{
if (string.IsNullOrEmpty(m_email))
{
m_usernameErrorClass = "";
}
else
{
m_usernameErrorClass = "hidden";
}
if (string.IsNullOrEmpty(m_password))
{
m_passwordErrorClass = "";
}
else
{
m_passwordErrorClass = "hidden";
}
}
/// <summary>
/// Authenticates the user and determines the type of page layout to show
/// </summary>
/// <returns></returns>
async Task pagaAuth()
async Task Authenticate()
{
FieldValidation();
ShowSpinner();
try
@ -57,7 +87,7 @@ namespace Biskilog_Accounting.Client.Pages.Auth
{
}
}
catch (Exception ex)
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
@ -109,4 +139,4 @@ namespace Biskilog_Accounting.Client.Pages.Auth
StateHasChanged();
}
}
}
}

195
Client/Pages/Auth/Components/UsernameTab.razor.css

@ -0,0 +1,195 @@
@font-face {
font-family: "titleFont";
src: url("../fonts/Rockwill.otf");
font-weight: bold;
}
.form-section {
border: rgb(186, 190, 197) 0.5px solid;
min-height: 690px;
display: flex;
flex-direction: column;
align-items: center;
margin: auto;
max-width: 400px;
position: relative;
}
.form-section h2 {
font-size: 24pt;
font-weight: 800;
line-height: 32px;
margin-bottom: 17px;
margin-top: 20px;
color: rgb(57, 58, 61);
}
.title-section {
display: flex;
flex-direction: column;
align-items: center;
font-size: medium;
font-family: "titleFont";
color: rgb(186, 190, 197);
}
.title-section a {
font-size: 30pt;
text-decoration: none;
color: rgb(7, 7, 154);
}
.title-section a img {
margin: auto !important;
height: 80px !important;
max-width: 80px !important;
display: flex;
justify-content: space-between;
text-decoration: none;
}
.sign-in-gbutton {
padding: 0px 10px;
background: rgb(255, 255, 255);
border: 2px solid rgb(186, 190, 197);
border-radius: 2px;
white-space: nowrap;
width: 100%;
max-width: 310px;
height: inherit;
margin: 20px;
max-height: 45px;
cursor: pointer;
color: rgb(57, 58, 61);
}
.sign-in-gbutton-colored {
padding: 0px 10px;
background: rgb(7, 7, 154);
border: 1px solid rgb(186, 190, 197);
border-radius: 2px;
white-space: nowrap;
color: white;
width: 50%;
max-width: 310px;
height: 40px;
margin: 20px;
max-height: 45px;
cursor: pointer;
transition: width 1.5s ease;
}
.sign-in-gbutton-colored div > i {
width: 20px;
margin: 5px;
background-repeat: no-repeat;
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNOCAwYzIuNzk2IDAgNC44OTMgMi4xODQgNC45OTYgNS4yNjJMMTMgNS41VjdoMWEyIDIgMCAwIDEgMiAydjlhMiAyIDAgMCAxLTIgMkgyYTIgMiAwIDAgMS0yLTJWOWEyIDIgMCAwIDEgMi0yaDFWNS41QzMgMi4yOTggNS4xMzMgMCA4IDBabTYgOUgydjloMTJWOVptLTYgMmEyIDIgMCAwIDEgMS4wMDEgMy43MzJMOSAxNS4yYzAgLjQ0Mi0uNDQ4LjgtMSAuOC0uNTEzIDAtLjkzNi0uMzA5LS45OTMtLjcwN0w3IDE1LjJ2LS40NjhBMiAyIDAgMCAxIDggMTFabTAtOUM2LjMzMiAyIDUuMDg4IDMuMjc3IDUuMDA1IDUuMjgyTDUgNS41VjdoNlY1LjVDMTEgMy4zNyA5LjcyOCAyIDggMloiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==");
}
.sign-in-gbutton-colored:hover {
width: 100%;
}
.sign-in-gbutton-title {
font-size: 15px;
margin: 7px;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
}
.sign-in-gbutton-image {
height: 35px;
}
.line-styled-div ::before,
.line-styled-div ::after {
content: "";
position: absolute;
border-bottom: 2px solid rgb(186, 190, 197);
width: 150px;
}
.line-styled-div ::before {
margin: 7px 40px;
}
.line-styled-div ::after {
margin: 7px -190px;
}
.line-styled-div span {
position: relative;
display: inline-block;
color: rgb(186, 190, 197);
font: 1em sans-serif;
}
.input-box {
font-size: 15px;
width: 280px;
border: 0px;
outline: none;
background: transparent;
}
.input-box :active {
border: 0px;
}
.input-div {
margin-top: 28px;
border: 1.9px solid rgb(132, 134, 139);
min-height: 20px;
padding: 9px;
border-radius: 8px;
display: flex;
}
.input-div span {
width: 20px;
height: 20px;
border-radius: 30px;
color: #d52b1f;
border: 1px solid #d52b1f;
text-align: center;
font: 1rem sans-serif;
}
.input-div.error {
border: 1.9px solid #d52b1f;
background-color: #fbeceb;
}
.input-div.error input {
background-color: #fbeceb;
}
.input-div-check {
margin-top: 15px;
font-size: 18px;
display: flex;
text-align: center;
}
.input-div-check input {
width: 30px;
height: 25px;
display: flex;
margin-right: 25px;
}
.input-div-check label {
margin-top: 4.5px;
color: rgb(132, 134, 139);
text-align: center;
}
.auth-success {
color: green;
}
.auth-failure {
color: red;
}
.hidden {
display: none;
}

157
Client/Pages/Auth/Login.razor

@ -1,139 +1,38 @@
@using Biskilog_Accounting.Shared.Interfaces
@using Biskilog_Accounting.Client.Pages.Auth.Components
@inject NavigationManager m_navigationManager
@layout AuthLayout
@inject HttpClient m_http
@inject ITokenService m_tokenService
@page "/login"
<style>
.gradient-custom-2 {
/* fallback for old browsers */
background: #fccb90;
/* Chrome 10-25, Safari 5.1-6 */
background: -webkit-linear-gradient(to right, rgb(20 158 132 / 76%), rgb(10 10 56/100%));
/* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
background: linear-gradient(to right, rgb(20 158 132 / 76%), rgb(10 10 56/100%));
}
.login-buttons {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.button-row {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin-bottom: 10px;
}
.facebook-button, .twitter-button, .google-button, .apple-button, .microsoft-button {
display: flex;
justify-content: center;
align-items: center;
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 10px;
text-decoration: none;
color: #fff;
}
.facebook-button {
background-color: #3B5998;
}
.twitter-button {
background-color: #1DA1F2;
}
.google-button {
background-color: #DB4437;
}
.apple-button {
background-color: #000;
}
.microsoft-button {
background-color: #00A4EF;
}
</style>
<section class="h-100 gradient-form" style="background-color: #eee;">
<div class="container py-5 h-100">
<div class="row d-flex justify-content-center align-items-center h-100">
<div class="col-xl-10">
<div class="card rounded-3 text-black">
<div class="row g-0">
<div class="col-lg-6">
<div class="card-body p-md-5 mx-md-4">
<div class="text-center">
<img src="icon-512.png"
style="width: 185px;" alt="logo">
<h4 class="mt-1 mb-5 pb-1">Biskilog Accounting</h4>
</div>
<form>
<p>Please login to your account</p>
<div class="form-outline mb-4">
<input type="email" id="form2Example11" class="form-control"
placeholder="Phone number or email address" @oninput="@(args => usernameInput(args.Value.ToString()))" @onkeydown="@Enter" />
<label class="form-label" for="form2Example11">Username</label>
</div>
<div class="form-outline mb-4">
<input type="password" id="form2Example22" class="form-control" @oninput="@(args => passwordInput(args.Value.ToString()))" @onkeydown="@Enter" />
<label class="form-label" for="form2Example22">Password</label>
</div>
<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" @onclick="pagaAuth">
Log
in
</button>
<a class="text-muted" href="#!">Forgot password?</a>
</div>
<div class="d-flex align-items-center justify-content-center pb-4">
<div class="login-buttons">
<div class="button-row">
<a href="#" class="facebook-button"><i class="fab fa-facebook-f"></i></a>
<a href="#" class="twitter-button"><i class="fab fa-twitter"></i></a>
<a href="#" class="google-button"><i class="fab fa-google"></i></a>
</div>
<div class="button-row">
<a href="#" class="apple-button"><i class="fab fa-apple"></i></a>
<a href="#" class="microsoft-button"><i class="fab fa-windows"></i></a>
</div>
</div>
</div>
</form>
</div>
</div>
<div class="col-lg-6 d-flex align-items-center gradient-custom-2">
<div class="text-white px-3 py-4 p-md-5 mx-md-4">
<h4 class="mb-4">Simplify Your Business Management with Biskilog's Suite of Tools</h4>
<p class="small mb-0">
Stay on top of your business operations from anywhere with Biskilog - now a user-friendly web application
that not only provides a comprehensive operational summary of all records from the BISKILOG POS software
but also allows you to make sales and access new features. Monitor operations, make informed decisions and simplify
your business management with Biskilog's powerful and convenient web app. Say goodbye to manual record-keeping
and hello to streamlined efficiency with Biskilog.
</p>
</div>
</div>
</div>
</div>
</div>
<section class="title-section">
<a href="javascrivpt:void(0)">
<img class="logo-image" src="../icon-512.png" alt="The logo of Biskilog pos" />
<span>BISKILOG</span>
</a>
</section>
<section class="form-section">
<h2>Sign in</h2>
<span style="font-family: sans-serif; color: rgb(141, 144, 150);">
Enter your credentials to use <a href="javascrivpt:void(0)">Biskilog</a> accounting
</span>
<button type="button" class="sign-in-gbutton">
<div style="display: inline-flex;">
<img src="../assets/img/google-image.png" class="sign-in-gbutton-image" alt="Google logo" />
<span class="sign-in-gbutton-title">Sign in with Google</span>
</div>
</button>
<div class="line-styled-div">
<span>OR</span>
</div>
<TabContainer />
<hr style="margin-top: 15px; width: 320px; margin-bottom: 15px; color: #babec5; border-style: solid;" />
<label class="policy-statement">
By selecting Sign In or Sign in with Google, you agree to our <a href="#">Terms</a> and acknowledge our <a href="#">Privacy Statement</a>.
</label>
<div class="RecaptchaSignIn">
Invisible reCAPTCHA by Google <a href="https://www.google.com/intl/en/policies/privacy/">Privacy Policy</a> and <a href="https://www.google.com/intl/en/policies/terms/">Terms of Use</a>.
</div>
</section>

185
Client/Pages/Auth/Login.razor.css

@ -0,0 +1,185 @@
@font-face {
font-family: "titleFont";
src: url("../../fonts/Rockwill.otf");
font-weight: bold;
}
.form-section {
border: rgb(186, 190, 197) 0.5px solid;
min-height: 690px;
display: flex;
flex-direction: column;
align-items: center;
margin: auto;
max-width: 400px;
position: relative;
}
.form-section h2 {
font-size: 24pt;
font-weight: 800;
line-height: 32px;
margin-bottom: 17px;
margin-top: 20px;
color: rgb(57, 58, 61);
}
.title-section {
display: flex;
flex-direction: column;
align-items: center;
font-size: medium;
font-family: "titleFont";
color: rgb(186, 190, 197);
}
.title-section a {
font-size: 30pt;
text-decoration: none;
color: rgb(7, 7, 154);
}
.title-section a img {
margin: auto !important;
height: 80px !important;
max-width: 80px !important;
display: flex;
justify-content: space-between;
text-decoration: none;
}
.sign-in-gbutton {
padding: 0px 10px;
background: rgb(255, 255, 255);
border: 2px solid rgb(186, 190, 197);
border-radius: 2px;
white-space: nowrap;
width: 100%;
max-width: 310px;
height: inherit;
margin: 20px;
max-height: 45px;
cursor: pointer;
color: rgb(57, 58, 61);
}
.sign-in-gbutton-colored {
padding: 0px 10px;
background: rgb(7, 7, 154);
border: 1px solid rgb(186, 190, 197);
border-radius: 2px;
white-space: nowrap;
color: white;
width: 50%;
max-width: 310px;
height: 40px;
margin: 20px;
max-height: 45px;
cursor: pointer;
transition: width 1.5s ease;
}
.sign-in-gbutton-colored div > i {
width: 20px;
margin: 5px;
background-repeat: no-repeat;
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIyMCIgZmlsbD0ibm9uZSI+PHBhdGggZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNOCAwYzIuNzk2IDAgNC44OTMgMi4xODQgNC45OTYgNS4yNjJMMTMgNS41VjdoMWEyIDIgMCAwIDEgMiAydjlhMiAyIDAgMCAxLTIgMkgyYTIgMiAwIDAgMS0yLTJWOWEyIDIgMCAwIDEgMi0yaDFWNS41QzMgMi4yOTggNS4xMzMgMCA4IDBabTYgOUgydjloMTJWOVptLTYgMmEyIDIgMCAwIDEgMS4wMDEgMy43MzJMOSAxNS4yYzAgLjQ0Mi0uNDQ4LjgtMSAuOC0uNTEzIDAtLjkzNi0uMzA5LS45OTMtLjcwN0w3IDE1LjJ2LS40NjhBMiAyIDAgMCAxIDggMTFabTAtOUM2LjMzMiAyIDUuMDg4IDMuMjc3IDUuMDA1IDUuMjgyTDUgNS41VjdoNlY1LjVDMTEgMy4zNyA5LjcyOCAyIDggMloiIGNsaXAtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==");
}
.sign-in-gbutton-colored:hover {
width: 100%;
}
.sign-in-gbutton-title {
font-size: 15px;
margin: 7px;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
}
.sign-in-gbutton-image {
height: 35px;
}
.line-styled-div ::before,
.line-styled-div ::after {
content: "";
position: absolute;
border-bottom: 2px solid rgb(186, 190, 197);
width: 150px;
}
.line-styled-div ::before {
margin: 7px 40px;
}
.line-styled-div ::after {
margin: 7px -190px;
}
.line-styled-div span {
position: relative;
display: inline-block;
color: rgb(186, 190, 197);
font: 1em sans-serif;
}
.tab button {
font-size: 13px;
font-weight: bold;
color: rgb(132, 134, 139);
background-color: inherit;
float: left;
border: none;
outline: none;
cursor: pointer;
padding: 14px 16px;
transition: 0.3s;
min-width: 180px;
}
.tab button :hover {
border-bottom: 1px solid rgb(7, 7, 154);
}
.tab button.active {
border-bottom: 3px solid rgb(7, 7, 154);
}
.tabcontent {
display: none;
padding: 6px 12px;
border: 1px solid #ccc;
border-top: none;
}
.RecaptchaSignIn {
width: 100%;
background-color: rgb(236, 238, 241);
font-size: 14px;
color: rgb(141, 144, 150);
padding: 16px;
text-align: center;
position: absolute;
bottom: 0px;
font: 0.8em sans-serif;
}
.policy-statement {
margin-left: 40px;
margin-right: 40px;
font: 0.9em sans-serif;
color: rgb(141, 144, 150);
}
.RecaptchaSignIn > a,
label > a,
.reset-link,
span > a {
color: royalblue;
cursor: pointer;
text-decoration: none;
}
.reset-link {
font: 0.9em sans-serif;
}

74
Client/Pages/Dashboard/Elements/ProductPriceHistory.razor.cs

@ -25,6 +25,7 @@ namespace Biskilog_Accounting.Client.Pages.Dashboard.Elements
private Timer m_timer { get; set; }
private ApexChart<ProductPriceChange> chart;
private Random m_random = new Random();
private int tries = 10;
protected override void OnInitialized()
{
base.OnInitialized();
@ -37,35 +38,61 @@ namespace Biskilog_Accounting.Client.Pages.Dashboard.Elements
private void TimerElapsed(object sender, ElapsedEventArgs e)
{
tries = 0;
// Code to execute every 10 seconds
InvokeAsync(async () =>
{
if (m_productHistory.Count > 0)
{
int randomNumber = m_random.Next(ProductHistory.Count);
m_currentProduct = ProductHistory[randomNumber].Pcode;
m_productHistory = ProductHistory.Where(t => t.Pcode == m_currentProduct).ToList();
await GetPriceChange();
StateHasChanged();
await chart.RenderAsync();
});
}
private async Task GetPriceChange()
{
tries++;
if (m_productHistory.Count > 0)
{
int randomNumber = m_random.Next(ProductHistory.Count);
ProductPriceChange change = m_productHistory.First();
m_productHistory = ProductHistory.Where(t => t.Pcode == m_currentProduct).ToList();
ProductPriceChange change = m_productHistory.First();
double actualChange = (double)((change.CurrentPrice - change.PreviousPrice));
if (actualChange == 0)
{
// if randomly selected doesn't have any product change history
if (!string.IsNullOrEmpty(m_currentProduct))
{
return;
}
else
{
if (tries < 3)
{
GetPriceChange();
}
return;
}
}
else
{
m_percentage = (double)((change.CurrentPrice - change.PreviousPrice) / change.PreviousPrice > 0 ? change.PreviousPrice * 100 : 1);
m_increase = change.CurrentPrice > change.PreviousPrice;
m_currentPrice = (double)change.CurrentPrice;
m_productName = change.ProductName;
m_subtitle = $"Price :{m_calculator.GetCurrencyCode().CurrencySymbol}";
m_title = $"{m_productName} Price Changes";
StateHasChanged();
await chart.RenderAsync();
}
});
}
m_increase = change.CurrentPrice > change.PreviousPrice;
m_currentPrice = (double)change.CurrentPrice;
m_productName = change.ProductName;
m_subtitle = $"Price :{m_calculator.GetCurrencyCode().CurrencySymbol}";
m_title = $"{m_productName} Price Changes";
}
}
public void Dispose()
{
// Dispose of the timer when the component is disposed
m_timer?.Stop();
m_timer?.Dispose();
}
protected override void OnParametersSet()
protected override async void OnParametersSet()
{
if (!IsLoading)
{
@ -101,21 +128,8 @@ namespace Biskilog_Accounting.Client.Pages.Dashboard.Elements
}
}
};
Random random = new Random();
int randomNumber = random.Next(ProductHistory.Count);
m_currentProduct = ProductHistory[randomNumber].Pcode;
m_productHistory = ProductHistory.Where(t => t.Pcode == m_currentProduct).ToList();
if (m_productHistory.Count > 0)
{
ProductPriceChange change = m_productHistory.First();
m_percentage = (double)((change.CurrentPrice - change.PreviousPrice) / change.PreviousPrice > 0 ? change.PreviousPrice * 100 : 1);
m_increase = change.CurrentPrice > change.PreviousPrice;
m_currentPrice = (double)change.CurrentPrice;
m_productName = change.ProductName;
m_subtitle = $"Price :{m_calculator.GetCurrencyCode().CurrencySymbol}";
m_title = $"{m_productName} Price Changes";
}
await GetPriceChange();
}
base.OnParametersSet();
}

BIN
Client/wwwroot/assets/img/google-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

1
Client/wwwroot/css/signin.css

@ -0,0 +1 @@


BIN
Client/wwwroot/fonts/AvenirLTStd-Black.otf

Binary file not shown.

BIN
Client/wwwroot/fonts/AvenirLTStd-Book.otf

Binary file not shown.

BIN
Client/wwwroot/fonts/AvenirLTStd-Roman.otf

Binary file not shown.

BIN
Client/wwwroot/fonts/OstrichSans-Black.otf

Binary file not shown.

BIN
Client/wwwroot/fonts/OstrichSans-Bold.otf

Binary file not shown.

BIN
Client/wwwroot/fonts/Rockwill.otf

Binary file not shown.

2
Server/Services/AnalyticalService.cs

@ -89,7 +89,7 @@ namespace Biskilog_Accounting.Server.Services
yield return new CustomerAccounts
{
Customer = m_context.Tblcustomers.FirstOrDefault(i => i.CustomerId == customerId),
Debt = m_context.Customeraccounts.OrderByDescending(d => d.Date).FirstOrDefault(t => t.Balance < 0 && t.CustomerId == customerId).Balance,
Debt = m_context.Customeraccounts.AsEnumerable().OrderByDescending(d => d.Date).FirstOrDefault(t => t.Balance < 0 && t.CustomerId == customerId).Balance,
};
}
}

2
Shared/Interfaces/ISearchService.cs

@ -10,7 +10,9 @@ namespace Biskilog_Accounting.Shared.Interfaces
{
public event Action<string> SearchValueChanged;
public event Action ClearTextBox;
public event Action<string> CloseMenus;
void PerformSearch(string a_searchKey);
void FireCloseMenus(string a_sender);
void Clear();
}
}

5
Shared/ServiceRepo/SearchService.cs

@ -11,6 +11,7 @@ namespace Biskilog_Accounting.Shared.ServiceRepo
{
public event Action<string> SearchValueChanged;
public event Action ClearTextBox;
public event Action<string> CloseMenus;
// Method that raises the event
protected virtual void OnSearchValueChanged(string a_searchKey)
@ -28,6 +29,10 @@ namespace Biskilog_Accounting.Shared.ServiceRepo
{
ClearTextBox?.Invoke();
}
public void FireCloseMenus(string a_sender)
{
CloseMenus?.Invoke(a_sender);
}
}
}

1
Shared/ServiceRepo/TokenService.cs

@ -41,7 +41,6 @@ namespace Biskilog_Accounting.ServiceRepo
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return AuthEnums.Invalid;
}
}

Loading…
Cancel
Save