Addet ToDaySales page
This commit is contained in:
@@ -1,15 +1,11 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Pos.Models
|
namespace Pos.Models;
|
||||||
|
|
||||||
|
public class TotalSaleDetail
|
||||||
{
|
{
|
||||||
public class TotalSaleDetail
|
public decimal TotalSale { get; set; }
|
||||||
{
|
public int TotalCustomer { get; set; }
|
||||||
public decimal TotalSale { get; set; }
|
public List<TotalSaleCategory> TotalSaleCategories { get; set; } = new();
|
||||||
public int TotalCustomer { get; set; }
|
public List<TotalSalePayment> PaymentBreakdown { get; set; } = new();
|
||||||
public List<TotalSaleCategory> TotalSaleCategories { get; set; } = new();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
7
PointOfSale/Pos.Models/TotalSalePayment.cs
Normal file
7
PointOfSale/Pos.Models/TotalSalePayment.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Pos.Models;
|
||||||
|
|
||||||
|
public class TotalSalePayment
|
||||||
|
{
|
||||||
|
public string PaymentMethod { get; set; } = string.Empty;
|
||||||
|
public decimal Amount { get; set; }
|
||||||
|
}
|
||||||
14
PointOfSale/Pos.Service/IProductService.cs
Normal file
14
PointOfSale/Pos.Service/IProductService.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Database.Models;
|
||||||
|
|
||||||
|
namespace Pos.Service;
|
||||||
|
|
||||||
|
public interface IProductService
|
||||||
|
{
|
||||||
|
List<ProductEntity> GetProducts(int productGroupId);
|
||||||
|
ProductEntity GetProduct(int productId);
|
||||||
|
void AddProduct(string name, int productGroupId);
|
||||||
|
void UpdateProduct(int productId, int productGroupId, string name);
|
||||||
|
void ArchiveProduct(int productId);
|
||||||
|
void UpdateOrder(int productGroupId, IReadOnlyList<int> orderedProductIds);
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Database.Models;
|
using Database.Models;
|
||||||
@@ -62,12 +62,12 @@ namespace Pos.Service
|
|||||||
|
|
||||||
if (orderedProductIds.Any(id => id <= 0))
|
if (orderedProductIds.Any(id => id <= 0))
|
||||||
throw new ArgumentException(
|
throw new ArgumentException(
|
||||||
"Rækkefølgen indeholder et ugyldigt id.",
|
"Rækkefølgen indeholder et ugyldigt id.",
|
||||||
nameof(orderedProductIds));
|
nameof(orderedProductIds));
|
||||||
|
|
||||||
if (orderedProductIds.Distinct().Count() != orderedProductIds.Count)
|
if (orderedProductIds.Distinct().Count() != orderedProductIds.Count)
|
||||||
throw new ArgumentException(
|
throw new ArgumentException(
|
||||||
"Rækkefølgen indeholder dubletter.",
|
"Rækkefølgen indeholder dubletter.",
|
||||||
nameof(orderedProductIds));
|
nameof(orderedProductIds));
|
||||||
|
|
||||||
_productRepository.SetOrder(
|
_productRepository.SetOrder(
|
||||||
@@ -93,3 +93,5 @@ namespace Pos.Service
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Configuration;
|
using System.Configuration;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -103,6 +103,34 @@ namespace Pos.Service
|
|||||||
foreach (SaleEntity sale in sales)
|
foreach (SaleEntity sale in sales)
|
||||||
{
|
{
|
||||||
List<SaleLineEntity> saleLineBySaleId = _saleRepository.GetSaleLineBySaleId(sale.Id);
|
List<SaleLineEntity> saleLineBySaleId = _saleRepository.GetSaleLineBySaleId(sale.Id);
|
||||||
|
List<PaymentEntity> paymentsBySaleId =
|
||||||
|
_saleRepository.GetPaymentBySaleId(sale.Id);
|
||||||
|
|
||||||
|
foreach (PaymentEntity payment in paymentsBySaleId)
|
||||||
|
{
|
||||||
|
string paymentMethod = string.IsNullOrWhiteSpace(payment.Type)
|
||||||
|
? "Ukendt"
|
||||||
|
: payment.Type;
|
||||||
|
|
||||||
|
TotalSalePayment? paymentSummary =
|
||||||
|
totalSaleDetail.PaymentBreakdown
|
||||||
|
.FirstOrDefault(item =>
|
||||||
|
item.PaymentMethod == paymentMethod);
|
||||||
|
|
||||||
|
if (paymentSummary is null)
|
||||||
|
{
|
||||||
|
totalSaleDetail.PaymentBreakdown.Add(
|
||||||
|
new TotalSalePayment
|
||||||
|
{
|
||||||
|
PaymentMethod = paymentMethod,
|
||||||
|
Amount = payment.Amount
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
paymentSummary.Amount += payment.Amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
foreach (SaleLineEntity saleLine in saleLineBySaleId)
|
foreach (SaleLineEntity saleLine in saleLineBySaleId)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -303,3 +331,4 @@ namespace Pos.Service
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<FluentButton Class="menu-button" Size="ButtonSize.Large">
|
<FluentButton Class="menu-button" Size="ButtonSize.Large">
|
||||||
Kassesalg
|
Kassesalg
|
||||||
</FluentButton>
|
</FluentButton>
|
||||||
<FluentButton Class="menu-button" Size="ButtonSize.Large">
|
<FluentButton Class="menu-button" Size="ButtonSize.Large" onclick="@(() => Nav.NavigateTo("/todaySales"))">
|
||||||
Dagens salg
|
Dagens salg
|
||||||
</FluentButton>
|
</FluentButton>
|
||||||
<FluentButton Class="menu-button" Size="ButtonSize.Large">
|
<FluentButton Class="menu-button" Size="ButtonSize.Large">
|
||||||
@@ -18,4 +18,4 @@
|
|||||||
<FluentButton Class="menu-button" Size="ButtonSize.Large" onclick="@(() => Nav.NavigateTo("/settings"))">
|
<FluentButton Class="menu-button" Size="ButtonSize.Large" onclick="@(() => Nav.NavigateTo("/settings"))">
|
||||||
Indstillinger
|
Indstillinger
|
||||||
</FluentButton>
|
</FluentButton>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
.home-container {
|
.home-container {
|
||||||
width: min(100%, 1100px);
|
width: min(100%, 1100px);
|
||||||
margin: 32px auto;
|
margin: 32px auto;
|
||||||
@@ -11,4 +12,12 @@
|
|||||||
min-height: 220px;
|
min-height: 220px;
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 680px) {
|
||||||
|
.home-container {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
margin: 24px 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,16 +5,14 @@
|
|||||||
<Header />
|
<Header />
|
||||||
<div class="settings-page">
|
<div class="settings-page">
|
||||||
<h1>Indstillinger</h1>
|
<h1>Indstillinger</h1>
|
||||||
|
|
||||||
<p>Administrer data, der bruges i kassesalget.</p>
|
<p>Administrer data, der bruges i kassesalget.</p>
|
||||||
|
|
||||||
<div class="settings-grid">
|
<div class="settings-grid">
|
||||||
<FluentButton Class="settings-button" onclick="@(() => Nav.NavigateTo("/settings/employees"))">
|
<FluentButton Class="settings-button" onclick="@(() => Nav.NavigateTo("/settings/employees"))">
|
||||||
Medarbejder
|
Medarbejder
|
||||||
</FluentButton>
|
</FluentButton>
|
||||||
|
|
||||||
<FluentButton Class="settings-button">
|
<FluentButton Class="settings-button">
|
||||||
Vare
|
Vare
|
||||||
</FluentButton>
|
</FluentButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
.settings-page {
|
.settings-page {
|
||||||
width: min(100%, 1100px);
|
width: min(100%, 1100px);
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
@@ -22,3 +23,14 @@
|
|||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 680px) {
|
||||||
|
.settings-page {
|
||||||
|
padding: 24px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
@page "/todaySales"
|
||||||
|
@rendermode InteractiveServer
|
||||||
|
@inject IJSRuntime Js
|
||||||
|
@inject SaleService SaleService
|
||||||
|
|
||||||
|
<Header />
|
||||||
|
<div class="page-container today-sales-page">
|
||||||
|
<div class="filters">
|
||||||
|
<label class="filter-control"><span>Dato</span><span class="filter-box"><svg viewBox="0 0 24 24"><rect x="3.5" y="5.5" width="17" height="15" rx="2"/><path d="M7 3.5v4M17 3.5v4M3.5 9.5h17"/></svg><input type="date" value="@_date.ToString("yyyy-MM-dd")" @onchange="DateChanged"/></span></label>
|
||||||
|
<label class="filter-control short"><span>Periode</span><span class="filter-box"><svg viewBox="0 0 24 24"><rect x="3.5" y="5.5" width="17" height="15" rx="2"/><path d="M7 3.5v4M17 3.5v4M3.5 9.5h17"/></svg><select @onchange="RangeChanged" value="@_range"><option value="1">1 dag</option><option value="7">7 dage</option><option value="30">30 dage</option></select></span></label>
|
||||||
|
</div>
|
||||||
|
<section class="ui-card summary-card">
|
||||||
|
<div class="summary-icon"><svg viewBox="0 0 64 64"><path d="M12 52h40M18 48V35h8v13M29 48V25h8v23M40 48V14h8v34"/></svg></div>
|
||||||
|
<div><p>Dagens salg</p><strong>@Currency(_summary.Total)</strong><small>@_summary.SaleCount salg</small></div>
|
||||||
|
</section>
|
||||||
|
<div class="sales-grid">
|
||||||
|
<UiCard Title="Salg pr. varegruppe" Class="sales-category-card">
|
||||||
|
<MetricList TItem="Metric" Items="@_summary.Categories">
|
||||||
|
<HeaderContent><span>Varegruppe</span><span>Salg</span></HeaderContent>
|
||||||
|
<RowTemplate Context="row"><div class="metric-row"><span>@row.Name</span><span>@Currency(row.Amount)</span></div></RowTemplate>
|
||||||
|
</MetricList>
|
||||||
|
</UiCard>
|
||||||
|
<div class="sales-right">
|
||||||
|
<UiCard Title="Betalingsfordeling">
|
||||||
|
<MetricList TItem="Metric" Items="@_summary.Payments">
|
||||||
|
<RowTemplate Context="row"><div class="metric-row"><span>@row.Name</span><span>@Currency(row.Amount)</span></div></RowTemplate>
|
||||||
|
</MetricList>
|
||||||
|
</UiCard>
|
||||||
|
<button class="print-button" type="button" @onclick="PrintAsync"><svg viewBox="0 0 24 24"><path d="M6 9V3h12v6M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2M6 14h12v7H6z"/></svg>Print salg</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.JSInterop;
|
||||||
|
using Pos.Models;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace Pos.Web.Components.Pages.TodaySales;
|
||||||
|
|
||||||
|
public partial class TodaySales
|
||||||
|
{
|
||||||
|
private static readonly CultureInfo Danish = CultureInfo.GetCultureInfo("da-DK");
|
||||||
|
private TotalSaleDetail? _totalSaleDetail;
|
||||||
|
private DateTime _date = DateTime.Now;
|
||||||
|
private string _range = "1";
|
||||||
|
private Summary _summary = new(0m, 0, Array.Empty<Metric>(), Array.Empty<Metric>());
|
||||||
|
|
||||||
|
private async Task DateChanged(ChangeEventArgs e)
|
||||||
|
{
|
||||||
|
if (DateTime.TryParse(e.Value?.ToString(), out var date))
|
||||||
|
{
|
||||||
|
_date = date;
|
||||||
|
await LoadAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
await LoadAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RangeChanged(ChangeEventArgs e)
|
||||||
|
{
|
||||||
|
_range = e.Value?.ToString() ?? "1";
|
||||||
|
await LoadAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task LoadAsync()
|
||||||
|
{
|
||||||
|
var result = SaleService.TotalSale(_date);
|
||||||
|
_totalSaleDetail = result;
|
||||||
|
|
||||||
|
_summary = new Summary(
|
||||||
|
result.TotalSale,
|
||||||
|
result.TotalCustomer,
|
||||||
|
result.TotalSaleCategories
|
||||||
|
.Select(category => new Metric(category.Category, category.Sale))
|
||||||
|
.ToList(),
|
||||||
|
result.PaymentBreakdown
|
||||||
|
.Select(payment => new Metric(payment.PaymentMethod, payment.Amount))
|
||||||
|
.ToList());
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task PrintAsync()
|
||||||
|
{
|
||||||
|
// TODO: Kobl til SaleService.PrintSaleOfDay(_date, _totalSaleDetail)
|
||||||
|
// eller POST /api/sales/print.
|
||||||
|
await Js.InvokeVoidAsync("window.print");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Currency(decimal amount) =>
|
||||||
|
$"{amount.ToString("N2", Danish)} kr.";
|
||||||
|
|
||||||
|
private sealed record Metric(string Name, decimal Amount);
|
||||||
|
|
||||||
|
private sealed record Summary(
|
||||||
|
decimal Total,
|
||||||
|
int SaleCount,
|
||||||
|
IReadOnlyList<Metric> Categories,
|
||||||
|
IReadOnlyList<Metric> Payments);
|
||||||
|
}
|
||||||
@@ -0,0 +1,307 @@
|
|||||||
|
|
||||||
|
.today-sales-page {
|
||||||
|
width: min(100%, 1420px);
|
||||||
|
min-height: calc(100dvh - 78px);
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 38px 32px 64px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
display: flex;
|
||||||
|
align-items: end;
|
||||||
|
gap: 16px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-control {
|
||||||
|
display: flex;
|
||||||
|
min-width: 230px;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
color: #3e444d;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-control.short {
|
||||||
|
min-width: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-box {
|
||||||
|
display: flex;
|
||||||
|
min-height: 56px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 0 14px;
|
||||||
|
border: 1px solid #bfc5cd;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-box svg {
|
||||||
|
width: 23px;
|
||||||
|
height: 23px;
|
||||||
|
flex: none;
|
||||||
|
fill: none;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
stroke-width: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-box input,
|
||||||
|
.filter-box select {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-card {
|
||||||
|
display: flex;
|
||||||
|
min-height: 170px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 32px;
|
||||||
|
padding: 28px 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-icon {
|
||||||
|
display: grid;
|
||||||
|
width: 114px;
|
||||||
|
height: 114px;
|
||||||
|
flex: none;
|
||||||
|
place-items: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: var(--pos-blue);
|
||||||
|
background: var(--pos-blue-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-icon svg {
|
||||||
|
width: 58px;
|
||||||
|
height: 58px;
|
||||||
|
fill: none;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
stroke-width: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-card p {
|
||||||
|
margin: 0 0 4px;
|
||||||
|
font-size: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-card strong {
|
||||||
|
display: block;
|
||||||
|
font-size: 48px;
|
||||||
|
line-height: 1.05;
|
||||||
|
letter-spacing: -.035em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-card small {
|
||||||
|
display: block;
|
||||||
|
margin-top: 6px;
|
||||||
|
color: var(--pos-muted);
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sales-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1.25fr) minmax(350px, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
align-items: start;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sales-right {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::deep .ui-card {
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid var(--pos-border);
|
||||||
|
border-radius: 12px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 1px 2px rgba(16, 24, 40, .03);
|
||||||
|
}
|
||||||
|
|
||||||
|
::deep .ui-card__header {
|
||||||
|
padding: 26px 32px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::deep .ui-card__header h2 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 650;
|
||||||
|
}
|
||||||
|
|
||||||
|
::deep .ui-card__body {
|
||||||
|
padding: 0 32px 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::deep .metric-list__header,
|
||||||
|
::deep .metric-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::deep .metric-list__header {
|
||||||
|
padding-bottom: 12px;
|
||||||
|
color: var(--pos-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
::deep .metric-row {
|
||||||
|
min-height: 58px;
|
||||||
|
align-items: center;
|
||||||
|
border-top: 1px solid var(--pos-border-soft);
|
||||||
|
font-size: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::deep .metric-row > :last-child,
|
||||||
|
::deep .metric-list__header > :last-child {
|
||||||
|
text-align: right;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.print-button {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 70px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 14px;
|
||||||
|
border: 1.5px solid var(--pos-blue);
|
||||||
|
border-radius: 9px;
|
||||||
|
color: var(--pos-blue);
|
||||||
|
background: #fff;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.print-button:hover {
|
||||||
|
background: var(--pos-blue-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.print-button svg {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
fill: none;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
stroke-width: 1.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 900px) and (max-height: 850px) {
|
||||||
|
.today-sales-page {
|
||||||
|
padding-top: 22px;
|
||||||
|
padding-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-box {
|
||||||
|
min-height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-card {
|
||||||
|
min-height: 130px;
|
||||||
|
gap: 24px;
|
||||||
|
padding: 20px 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-icon {
|
||||||
|
width: 88px;
|
||||||
|
height: 88px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-icon svg {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-card strong {
|
||||||
|
font-size: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-card small {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sales-grid {
|
||||||
|
gap: 20px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::deep .ui-card__header {
|
||||||
|
padding: 18px 28px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::deep .ui-card__body {
|
||||||
|
padding: 0 28px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::deep .metric-list__header {
|
||||||
|
padding-bottom: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::deep .metric-row {
|
||||||
|
min-height: 39px;
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.print-button {
|
||||||
|
min-height: 54px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.sales-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 680px) {
|
||||||
|
.today-sales-page {
|
||||||
|
padding: 24px 16px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-card {
|
||||||
|
gap: 18px;
|
||||||
|
padding: 22px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-icon {
|
||||||
|
width: 72px;
|
||||||
|
height: 72px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-icon svg {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-card strong {
|
||||||
|
font-size: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::deep .ui-card__header {
|
||||||
|
padding: 22px 20px 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::deep .ui-card__body {
|
||||||
|
padding: 0 20px 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::deep .metric-row {
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -9,4 +9,4 @@
|
|||||||
"dddd d. MMMM yyyy · HH:mm",
|
"dddd d. MMMM yyyy · HH:mm",
|
||||||
_danishCulture)
|
_danishCulture)
|
||||||
</time>
|
</time>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
|
||||||
namespace Pos.Web.Components.Shared
|
namespace Pos.Web.Components.Shared
|
||||||
{
|
{
|
||||||
|
|||||||
10
PointOfSale/Pos.Web/Components/Shared/MetricList.razor
Normal file
10
PointOfSale/Pos.Web/Components/Shared/MetricList.razor
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
@typeparam TItem
|
||||||
|
<div class="metric-list">
|
||||||
|
@if (HeaderContent is not null) { <div class="metric-list__header">@HeaderContent</div> }
|
||||||
|
@foreach (var item in Items) { @RowTemplate(item) }
|
||||||
|
</div>
|
||||||
|
@code {
|
||||||
|
[Parameter] public IReadOnlyList<TItem> Items { get; set; } = Array.Empty<TItem>();
|
||||||
|
[Parameter] public RenderFragment? HeaderContent { get; set; }
|
||||||
|
[Parameter] public RenderFragment<TItem> RowTemplate { get; set; } = default!;
|
||||||
|
}
|
||||||
12
PointOfSale/Pos.Web/Components/Shared/UiCard.razor
Normal file
12
PointOfSale/Pos.Web/Components/Shared/UiCard.razor
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<section class="ui-card @Class">
|
||||||
|
@if (!string.IsNullOrWhiteSpace(Title))
|
||||||
|
{
|
||||||
|
<div class="ui-card__header"><h2>@Title</h2></div>
|
||||||
|
}
|
||||||
|
<div class="ui-card__body">@ChildContent</div>
|
||||||
|
</section>
|
||||||
|
@code {
|
||||||
|
[Parameter] public string Title { get; set; } = string.Empty;
|
||||||
|
[Parameter] public string Class { get; set; } = string.Empty;
|
||||||
|
[Parameter] public RenderFragment? ChildContent { get; set; }
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
@using System.Net.Http
|
@using System.Net.Http
|
||||||
@using System.Net.Http.Json
|
@using System.Net.Http.Json
|
||||||
|
@using Microsoft.AspNetCore.Components
|
||||||
@using Microsoft.AspNetCore.Components.Forms
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
@using Microsoft.AspNetCore.Components.Routing
|
@using Microsoft.AspNetCore.Components.Routing
|
||||||
@using Microsoft.AspNetCore.Components.Web
|
@using Microsoft.AspNetCore.Components.Web
|
||||||
@@ -11,4 +12,7 @@
|
|||||||
@using Pos.Web
|
@using Pos.Web
|
||||||
@using Pos.Web.Components
|
@using Pos.Web.Components
|
||||||
@using Pos.Web.Components.Layout
|
@using Pos.Web.Components.Layout
|
||||||
@using Pos.Web.Components.Shared
|
@using Pos.Web.Components.Shared
|
||||||
|
@using Pos.Service
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +1,33 @@
|
|||||||
using Microsoft.FluentUI.AspNetCore.Components;
|
using Microsoft.FluentUI.AspNetCore.Components;
|
||||||
using Pos.Web.Components;
|
|
||||||
using Pos.Service;
|
using Pos.Service;
|
||||||
|
using Pos.Web.Components;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.AddServiceDefaults();
|
builder.AddServiceDefaults();
|
||||||
|
|
||||||
// Add services to the container.
|
|
||||||
builder.Services.AddRazorComponents()
|
builder.Services.AddRazorComponents()
|
||||||
.AddInteractiveServerComponents();
|
.AddInteractiveServerComponents();
|
||||||
|
|
||||||
builder.Services.AddFluentUIComponents();
|
builder.Services.AddFluentUIComponents();
|
||||||
builder.Services.AddScoped<EmployeeService>();
|
builder.Services.AddScoped<EmployeeService>();
|
||||||
|
builder.Services.AddScoped<SaleService>();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
app.MapDefaultEndpoints();
|
app.MapDefaultEndpoints();
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
|
||||||
if (!app.Environment.IsDevelopment())
|
if (!app.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
||||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
|
||||||
app.UseHsts();
|
app.UseHsts();
|
||||||
}
|
}
|
||||||
app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true);
|
|
||||||
app.UseHttpsRedirection();
|
|
||||||
|
|
||||||
|
app.UseStatusCodePagesWithReExecute(
|
||||||
|
"/not-found",
|
||||||
|
createScopeForStatusCodePages: true);
|
||||||
|
|
||||||
|
app.UseHttpsRedirection();
|
||||||
app.UseAntiforgery();
|
app.UseAntiforgery();
|
||||||
|
|
||||||
app.MapStaticAssets();
|
app.MapStaticAssets();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@import '/_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css';
|
@import '/_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css';
|
||||||
|
|
||||||
body {
|
body {
|
||||||
--body-font: "Segoe UI Variable", "Segoe UI", sans-serif;
|
--body-font: "Segoe UI Variable", "Segoe UI", sans-serif;
|
||||||
@@ -87,3 +87,51 @@ body {
|
|||||||
code {
|
code {
|
||||||
color: #c02d76;
|
color: #c02d76;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--pos-blue: #155eef;
|
||||||
|
--pos-blue-soft: #eaf1ff;
|
||||||
|
--pos-text: #171a1f;
|
||||||
|
--pos-muted: #707781;
|
||||||
|
--pos-border: #d9dde3;
|
||||||
|
--pos-border-soft: #e9ebef;
|
||||||
|
--pos-bg: #f7f8fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
background: var(--pos-bg);
|
||||||
|
color: var(--pos-text);
|
||||||
|
font-family: "Segoe UI Variable", "Segoe UI", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
select {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
min-height: 100dvh;
|
||||||
|
background: var(--pos-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
color: #c02d76;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user