wip - transfer draft to customer

This commit is contained in:
Frede Hundewadt 2022-03-15 18:11:01 +01:00
parent 734dd2768a
commit bfb3b10019
16 changed files with 217 additions and 286 deletions

View file

@ -15,7 +15,7 @@
//
*@
<CascadingAuthenticationState>
<CartStateProvider>
<DraftStateProvider>
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
@ -31,6 +31,6 @@
</LayoutView>
</NotFound>
</Router>
</CartStateProvider>
</DraftStateProvider>
</CascadingAuthenticationState>

View file

@ -1,3 +1,3 @@
<div>
<img class="spinner px-3" src="loader.gif" alt="Afventer svar fra service ..."/>Afventer svar fra service ...
<img class="spinner px-3" src="loader.gif" alt="Afventer netværk svar ..."/>Afventer netværk svar ...
</div>

View file

@ -22,11 +22,11 @@ namespace Wonky.Client.Components;
public partial class CompanySearchColumn
{
[Parameter]
public EventCallback<string> OnFilterChanged { get; set; }
public EventCallback<string> OnColumnChanged { get; set; }
private string SearchColumn { get; set; } = "name";
private async Task ApplyFilter(ChangeEventArgs eventArgs)
{
await OnFilterChanged.InvokeAsync(SearchColumn);
await OnColumnChanged.InvokeAsync(SearchColumn);
}
}

View file

@ -26,44 +26,9 @@
<div class="col-sm-4">@company.Name</div>
<div class="col-sm-2">@company.Account</div>
<div class="col-sm-4">@company.City</div>
<div class="col-sm-1"><a class="btn btn-sm btn-primary mb-1" href="/company/account/@company.Account"><i class="oi oi-arrow-circle-right"></i></a></div>
<div class="col-sm-1"><a class="btn btn-sm btn-primary mb-1" href="/company/account/@company.Account">Vis</a></div>
</div>
}
@*
<table class="table">
<thead>
<tr>
<th scope="col">Status</th>
<th scope="col">Navn</th>
<th scope="col">Konto</th>
<th scope="col">Bynavn</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var company in Companies)
{
<tr>
<td class="align-middle">
<CvrStatus VatNumber="@company.VatNumber" />
</td>
<td class="align-middle">
@company.Name
</td>
<td class="align-middle">
@company.Account
</td>
<td class="align-middle">
@company.City
</td>
<td class="align-middle">
<a class="btn btn-primary mb-1" href="/company/@company.Account"><i class="oi oi-arrow-circle-right"></i></a>
</td>
</tr>
}
</tbody>
</table>
*@
}
else
{

View file

@ -25,20 +25,21 @@ namespace Wonky.Client.Components
{
[Parameter] public List<CompanyDto> Companies { get; set; } = new();
[Parameter] public EventCallback<string> OnDelete { get; set; }
[Parameter] public EventCallback<string> OnSelect { get; set; }
private Confirmation _confirmation = new ();
private string _companyIdToDelete = string.Empty;
private string _companyId = string.Empty;
private void CallConfirmationModal(string companyId)
{
_companyIdToDelete = companyId;
_companyId = companyId;
_confirmation.Show();
}
private async Task DeleteProduct()
private async Task DeleteCompany()
{
_confirmation.Hide();
await OnDelete.InvokeAsync(_companyIdToDelete);
await OnDelete.InvokeAsync(_companyId);
}
}
}

View file

@ -23,7 +23,7 @@
<div class="row mb-3">
<div class="col">
<CompanySearchColumn OnFilterChanged="FilterChanged" />
<CompanySearchColumn OnColumnChanged="FilterChanged" />
</div>
<div class="col">
<SearchPhrase OnSearchPhraseChanged="SearchChanged"/>

View file

@ -22,27 +22,8 @@
@attribute [Authorize(Roles = "Adviser")]
<div class="card">
<div class="card-header">
<div class="row">
<div class="col">
<RegStateVatNumber VatNumber="@CompanyDto.VatNumber"/>
</div>
<div class="col">
@CompanyDto.Name
</div>
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col">
<a class="btn btn-info" href="/company/account/@CompanyDto.Account/document">Tilbud</a>
<a class="btn btn-info" href="#">Ordre</a>
<a class="btn btn-info" href="#">Faktura</a>
<a class="btn btn-info" href="#">Produkter</a>
</div>
</div>
</div>
<div class="card-body">
<div class="card-header"><RegStateVatNumber VatNumber="@CompanyDto.VatNumber"/>@CompanyDto.Name</div>
<table class="table table-striped table-bordered">
<tbody>
<tr>
@ -78,8 +59,8 @@
</tr>
<tr>
<td>@CompanyDto.Interval</td>
<td>@($"{CompanyDto.LastVisitDate:dd-MM-yyyy}")</td>
<td>@($"{CompanyDto.NextVisitDate:dd-MM-yyyy}")</td>
<td>@($"{CompanyDto.LastVisitDate:yy-MM-dd}")</td>
<td>@($"{CompanyDto.NextVisitDate:yy-MM-dd}")</td>
</tr>
</tbody>
</table>

View file

@ -0,0 +1,152 @@
@using Wonky.Client.Components
@using System.Net.Http.Headers
@page "/OrdreKladde"
@* display product filter options *@
<div class="row mb-3">
<div class="col">
<ItemSearchColumn OnSearchColumnChanged="SearchColumnChanged"></ItemSearchColumn>
</div>
<div class="col">
<SearchPhrase OnSearchPhraseChanged="SearchPhraseChanged"></SearchPhrase>
</div>
<div class="col">
<ItemGroupFilter OnGroupFilterChanged="GroupFilterChanged"></ItemGroupFilter>
</div>
<div class="col">
</div>
</div>
@if (SalesItems.Any())
{
// display filtered product list
<table class="table table-hover table-striped justify-content-center">
<thead>
<tr>
<th scope="col" style="width: 50%;">Navn</th>
<th scope="col" style="width: 30%;" class="text-nowrap">Varenr</th>
<th scope="col" style="width: 20%">Stk / Pris</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
@foreach (var item in SalesItems)
{
<tr>
<td>@item.Name</td>
<td>@item.Sku</td>
<td><ul class="list-group">
@foreach (var rate in item.Rates)
{
<li class="list-group-item d-flex justify-content-between align-items-end">
<div class="text-sm-start px-2">@rate.Quantity</div>
<div class="text-sm-end">@rate.Rate</div>
</li>
}
</ul></td>
<td><button class="btn btn-primary" @onclick="@(() => SelectItem(item.ItemId))">Vælg</button></td>
</tr>
}
</tbody>
</table>
}
else
{
<AppSpinner/>
}
@* Display draft line *@
@if (_selectedItem != null && ShowItem)
{
<div class="card mb-3 mt-3">
<div class="card-header bg-info fw-bold text-white">Kladdelinje</div>
<div class="card-body">
<div class="row">
<div class="col col-md-4 fw-bold">
Varenavn
</div>
<div class="col fw-bold">
Varenr
</div>
<div class="col fw-bold">
Antal
</div>
<div class="col fw-bold">
Pris
</div>
<div class="col">
</div>
</div>
<div class="row">
<div class="col col-md-4">
@_selectedItem.Name
</div>
<div class="col">
@_selectedItem.Sku
</div>
<div class="col">
<input class="form-control" type="number" @bind-value="@Quantity"/>
</div>
<div class="col">
<input class="form-control" type="number" @bind-value="@Price"/>
</div>
<div class="col">
<button class="btn btn-info" @onclick="@(() => AddItem(_selectedItem))">Læg til</button>
</div>
</div>
</div>
</div>
}
@* Display draft lines *@
@if (DraftStateProvider != null && DraftStateProvider.Draft.Items.Count > 0)
{
<div class="card mt-3 mb-3">
<div class="card-header bg-success text-white fw-bold">Ordrekladde <span class="draft-expires-msg">(udløber efter @(DraftStateProvider.Draft.TimeToLiveInSeconds)s inaktivitet)</span></div>
<div class="card-body">
<table class="table table-hover table-striped justify-content-center">
<thead>
<tr>
<th scope="col">Navn</th>
<th scope="col" class="text-nowrap">Varenr</th>
<th scope="col" class="text-end">Antal</th>
<th scope="col" class="text-end">Enhedspris</th>
<th scope="col" class="text-end">Linjesum</th>
<th scope="col">&nbsp;</th>
</tr>
</thead>
<tbody>
@foreach (var cItem in DraftStateProvider.Draft.Items)
{
<tr>
<td>@cItem.Item.Name</td>
<td>@cItem.Item.Sku</td>
<td class="text-end">@cItem.Quantity</td>
<td class="text-end">@cItem.Price</td>
<td class="text-end">@cItem.Total</td>
<td><button class="btn btn-warning" @onclick="@(() => RemoveItem(@cItem))">Slet</button></td>
</tr>
}
<tr>
<td></td>
<td></td>
<td></td>
<td class="text-black text-end fw-bold">Total</td>
<td class="text-black text-end fw-bold">@DraftStateProvider.Draft.Total</td>
<td></td>
</tr>
</tbody>
</table>
</div>
<div class="card-footer">
<div class="row">
<div class="col">
<button class="btn btn-danger" @onclick="DeleteDraft">Slet kladde</button>
</div>
<div class="col">
<a class="btn btn-success" href="/Companies">Overfør</a>
</div>
</div>
</div>
</div>
}

View file

@ -8,18 +8,21 @@ using Wonky.Entity.Requests;
namespace Wonky.Client.Pages;
public partial class OrderDraft : IDisposable
public partial class DraftDocument : IDisposable
{
[CascadingParameter] CartStateProvider CartStateProvider { get; set; }
[CascadingParameter] DraftStateProvider DraftStateProvider { get; set; }
private bool ShowItem;
private bool ShowSearchView;
private string Quantity = "1";
private string Price = "0";
private SalesItemDto _selectedItem { get; set; } = new();
private List<SalesItemDto> SalesItems { get; set; } = new();
private SalesItemDto SelectedItem { get; set; } = new();
private List<CompanyDto> CompanyList { get; set; } = new();
// private SalesItemDto SelectedItem { get; set; } = new();
private MetaData _meta { get; set; } = new();
private PagingParams _paging { get; set; } = new();
[Inject] private ISalesItemHttpRepository ItemRepo { get; set; }
[Inject] private ICompanyHttpRepository CompanyRepo { get; set; }
[Inject] private HttpInterceptorService Interceptor { get; set; }
protected override async Task OnInitializedAsync()
@ -34,8 +37,13 @@ public partial class OrderDraft : IDisposable
SalesItems = response.Items!;
_meta = response.MetaData;
}
private void ItemSelect(string itemId)
private async Task DeleteDraft()
{
await DraftStateProvider.DeleteDraftAsync();
}
private void SelectItem(string itemId)
{
ShowItem = true;
_selectedItem = (from x in SalesItems where x.ItemId == itemId select x).First();
@ -43,11 +51,11 @@ public partial class OrderDraft : IDisposable
Quantity = "1";
}
private async Task AddToCart(SalesItemDto salesItem)
private async Task AddItem(SalesItemDto salesItem)
{
ShowItem = false;
// create a new cart item
var item = new CartItem
var item = new DraftItem
{
Item = salesItem,
Quantity = Convert.ToInt32(Quantity),
@ -55,22 +63,18 @@ public partial class OrderDraft : IDisposable
};
// add it to the cart
CartStateProvider.ShoppingCart.Items.Add(item);
// reset variables
_selectedItem = new();
Quantity = "1";
Price = "0";
DraftStateProvider.Draft.Items.Add(item);
// save the item using the CartStateProvider's save method
await CartStateProvider.SaveChangesAsync();
await DraftStateProvider.SaveChangesAsync();
}
private async Task RemoveItem(CartItem item)
private async Task RemoveItem(DraftItem item)
{
// click on remove
CartStateProvider.ShoppingCart.Items.Remove(item);
// save the cart
await CartStateProvider.SaveChangesAsync();
// remove item
DraftStateProvider.Draft.Items.Remove(item);
// save the remaining draft
await DraftStateProvider.SaveChangesAsync();
}
private async Task GroupFilterChanged(string groupFilter)
{

View file

@ -1,159 +0,0 @@
@using Wonky.Client.Components
@using System.ComponentModel.DataAnnotations
@page "/OrdreKladde"
<div class="row mb-3">
<div class="col">
<ItemSearchColumn OnSearchColumnChanged="SearchColumnChanged"></ItemSearchColumn>
</div>
<div class="col">
<SearchPhrase OnSearchPhraseChanged="SearchPhraseChanged"></SearchPhrase>
</div>
<div class="col">
<ItemGroupFilter OnGroupFilterChanged="GroupFilterChanged"></ItemGroupFilter>
</div>
<div class="col">
</div>
</div>
@if (SalesItems.Any())
{
<table class="table table-hover table-striped justify-content-center">
<thead>
<tr>
<th scope="col" style="width: 50%;">Navn</th>
<th scope="col" style="width: 30%;" class="text-nowrap">Varenr</th>
<th scope="col" style="width: 20%">Stk / Pris</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
@foreach (var item in SalesItems)
{
<tr>
<td>
@item.Name
</td>
<td>
@item.Sku
</td>
<td>
<ul class="list-group">
@foreach (var rate in item.Rates)
{
<li class="list-group-item d-flex justify-content-between align-items-end">
<div class="text-sm-start px-2">@rate.Quantity</div>
<div class="text-sm-end">@rate.Rate</div>
</li>
}
</ul>
</td>
<td>
<button class="btn btn-primary" @onclick="@(() => ItemSelect(item.ItemId))">Vælg</button>
</td>
</tr>
}
</tbody>
</table>
}
else
{
<AppSpinner/>
}
@if (_selectedItem != null && ShowItem)
{
<div class="card mb-3 mt-3">
<div class="card-header bg-info fw-bold text-white">Kladdelinje</div>
<div class="card-body">
<div class="row">
<div class="col col-md-4 fw-bold">
Varenavn
</div>
<div class="col fw-bold">
Varenr
</div>
<div class="col fw-bold">
Antal
</div>
<div class="col fw-bold">
Pris
</div>
<div class="col">
</div>
</div>
<div class="row">
<div class="col col-md-4">
@_selectedItem.Name
</div>
<div class="col">
@_selectedItem.Sku
</div>
<div class="col">
<input class="form-control" type="number" @bind-value="@Quantity"/>
</div>
<div class="col">
<input class="form-control" type="number" @bind-value="@Price"/>
</div>
<div class="col">
<button class="btn btn-info" @onclick="@(() => AddToCart(_selectedItem))">Læg til</button>
</div>
</div>
</div>
</div>
}
@* Show the cart contents if there are items in it. *@
@if (CartStateProvider != null && CartStateProvider.ShoppingCart.Items.Count > 0)
{
<div class="card mt-3 mb-3">
<div class="card-header bg-success text-white fw-bold">Ordrekladde</div>
<div class="card-body">
<table class="table table-hover table-striped justify-content-center">
<thead>
<tr>
<th scope="col">Navn</th>
<th scope="col" class="text-nowrap">Varenr</th>
<th scope="col" class="text-end">Antal</th>
<th scope="col" class="text-end">Enhedspris</th>
<th scope="col" class="text-end">Linjesum</th>
<th scope="col">&nbsp;</th>
</tr>
</thead>
<tbody>
@foreach (var cItem in CartStateProvider.ShoppingCart.Items)
{
<tr>
<td>
@cItem.Item.Name
</td>
<td>
@cItem.Item.Sku
</td>
<td class="text-end">
@cItem.Quantity
</td>
<td class="text-end">
@cItem.Price
</td>
<td class="text-end">
@cItem.Total
</td>
<td>
<button class="btn btn-warning" @onclick="@(() => RemoveItem(@cItem))">Slet</button>
</td>
</tr>
}
<tr>
<td></td>
<td></td>
<td></td>
<td class="text-black text-end fw-bold">Total</td>
<td class="text-black text-end fw-bold">@CartStateProvider.ShoppingCart.Total</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
}

View file

@ -21,10 +21,10 @@ using Wonky.Entity.Models;
namespace Wonky.Client.Shared;
public partial class CartStateProvider
public partial class DraftStateProvider
{
[Parameter] public RenderFragment ChildContent { get; set; }
public Cart ShoppingCart { get; set; }
public Draft Draft { get; set; }
[Parameter] public string Account { get; set; } = "MyCart";
[Inject] public ILocalStorageService LocalStorageService { get; set; }
@ -32,26 +32,30 @@ public partial class CartStateProvider
protected override async Task OnParametersSetAsync()
{
ShoppingCart = await LocalStorageService.GetItemAsync<Cart>(Account);
Draft = await LocalStorageService.GetItemAsync<Draft>(Account);
if (ShoppingCart == null || ShoppingCart.Items.Count == 0)
if (Draft == null || Draft.Items.Count == 0)
{
ShoppingCart = new Cart();
Draft = new Draft();
}
else
{
if (DateTime.Now > ShoppingCart.LastAccessed.AddSeconds(ShoppingCart.TimeToLiveInSeconds))
if (DateTime.Now > Draft.LastAccessed.AddSeconds(Draft.TimeToLiveInSeconds))
{
ShoppingCart = new Cart();
Draft = new Draft();
}
}
ShoppingCart.LastAccessed = DateTime.Now;
Draft.LastAccessed = DateTime.Now;
_hasLoaded = true;
}
public async Task SaveChangesAsync()
{
await LocalStorageService.SetItemAsync(Account, ShoppingCart);
await LocalStorageService.SetItemAsync(Account, Draft);
}
public async Task DeleteDraftAsync()
{
await LocalStorageService.RemoveItemAsync(Account);
}
}

View file

@ -1,21 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Debug",
"Microsoft": "Information"
}
},
"apiConfig": {
"baseAddress": "https://api.innotec.dk",
"tokenPath": "token",
"userInfo": "api/auth/userinfo",
"crmCompanies": "api/v2/crm/companies",
"priceCatalog": "api/v2/crm/catalog",
"cvrLookup": "/api/v2/services/virk",
"krvProducts": "api/v2/admin/q/products",
"krvVariants": "api/v2/admin/q/variants",
"imageUpload": "api/v2/admin/upload2",
"userRegistration": "api/auth/register"
}
}

View file

@ -0,0 +1 @@
../../../appsettings.json

View file

@ -1,7 +1,10 @@
@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');
.draft-expires-msg {
font-size: 0.8em;
}
.spinner {
height: 32px;
height: 48px;
}
html, body {

View file

@ -25,9 +25,9 @@ public class CompanyDto
[Required(ErrorMessage = "Postnummer skal udfyldes")] public string ZipCode { get; set; } = "";
[Required(ErrorMessage = "Bynavn skal udfyldes")] public string City { get; set; } = "";
[Required(ErrorMessage = "CVR nummer skal udfyldes")] public string VatNumber { get; set; } = "";
public string CompanyId { get; set; } = "";
public string SalesRepId { get; set; } = "";
public string BcId { get; set; } = "";
public string Address1 { get; set; } = "";
public string Address2 { get; set; } = "";
public string Account { get; set; } = "";
@ -35,7 +35,7 @@ public class CompanyDto
public string Mobile { get; set; } = "";
public string Email { get; set; } = "";
public string Attention { get; set; } = "";
[JsonPropertyName("LastVisitTimestamp")] public DateTime LastVisitDate { get; set; }
[JsonPropertyName("NextVisitTimestamp")] public DateTime NextVisitDate { get; set; }
public string LastVisitDate { get; set; } = "";
public string NextVisitDate { get; set; } = "";
public int Interval { get; set; } = 12;
}

View file

@ -2,7 +2,7 @@ using Wonky.Entity.DTO;
namespace Wonky.Entity.Models;
public class CartItem
public class DraftItem
{
public int Quantity { get; set; }
public SalesItemDto Item { get; set; }
@ -21,9 +21,9 @@ public class CartItem
}
}
public class Cart
public class Draft
{
public List<CartItem> Items { get; set; } = new List<CartItem>();
public List<DraftItem> Items { get; set; } = new List<DraftItem>();
public decimal Total
{
@ -33,5 +33,5 @@ public class Cart
}
}
public DateTime LastAccessed { get; set; }
public int TimeToLiveInSeconds { get; set; } = 60; // default
public int TimeToLiveInSeconds { get; set; } = 300; // default
}