WIP user management

This commit is contained in:
Frede Hundewadt 2023-03-04 12:14:21 +01:00
parent 5feffbb068
commit fed3ea2112
39 changed files with 373 additions and 245 deletions

View file

@ -25,9 +25,9 @@
<div class="list-group mt-2"> <div class="list-group mt-2">
<div class="list-group-item"> <div class="list-group-item">
<div class="row"> <div class="row">
<div class="col-sm-4" style="cursor: pointer;" @onclick="() => SortProducts(PSort.Desc)"><i class="bi-sort-alpha-down"></i> Navn <i class="bi-sort-alpha-up-alt"></i></div> <div class="col-sm-4" style="cursor: pointer;" @onclick="() => SortProducts(ProductSort.Desc)"><i class="bi-sort-alpha-down"></i> Navn <i class="bi-sort-alpha-up-alt"></i></div>
<div class="col-sm-3" style="cursor: pointer;" @onclick="() => SortProducts(PSort.Sku)"><i class="bi-sort-alpha-down"></i> Varenr <i class="bi-sort-alpha-up-alt"></i></div> <div class="col-sm-3" style="cursor: pointer;" @onclick="() => SortProducts(ProductSort.Sku)"><i class="bi-sort-alpha-down"></i> Varenr <i class="bi-sort-alpha-up-alt"></i></div>
<div class="col-sm-2 text-center" style="cursor: pointer;" @onclick="() => SortProducts(PSort.Qty)"><i class="bi-sort-numeric-down"></i> Antal <i class="bi-sort-numeric-up-alt"></i></div> <div class="col-sm-2 text-center" style="cursor: pointer;" @onclick="() => SortProducts(ProductSort.Qty)"><i class="bi-sort-numeric-down"></i> Antal <i class="bi-sort-numeric-up-alt"></i></div>
<div class="col-sm-2"></div> <div class="col-sm-2"></div>
<div class="col-sm-1"></div> <div class="col-sm-1"></div>
</div> </div>

View file

@ -37,12 +37,12 @@ public partial class CustomerInventoryListComponent
if(Inventory.Any()) if(Inventory.Any())
Inventory = Inventory.OrderBy(x => x.Description).ToList(); Inventory = Inventory.OrderBy(x => x.Description).ToList();
} }
private void SortProducts(PSort column) private void SortProducts(ProductSort column)
{ {
Descending = !Descending; Descending = !Descending;
switch (column) switch (column)
{ {
case PSort.Desc: case ProductSort.Desc:
if (Descending) if (Descending)
{ {
Inventory = Inventory.OrderByDescending(x => x.Description).ToList(); Inventory = Inventory.OrderByDescending(x => x.Description).ToList();
@ -50,7 +50,7 @@ public partial class CustomerInventoryListComponent
} }
Inventory = Inventory.OrderBy(x => x.Description).ToList(); Inventory = Inventory.OrderBy(x => x.Description).ToList();
break; break;
case PSort.Sku: case ProductSort.Sku:
if (Descending) if (Descending)
{ {
Inventory = Inventory.OrderByDescending(x => x.Sku).ToList(); Inventory = Inventory.OrderByDescending(x => x.Sku).ToList();
@ -58,7 +58,7 @@ public partial class CustomerInventoryListComponent
} }
Inventory = Inventory.OrderBy(x => x.Sku).ToList(); Inventory = Inventory.OrderBy(x => x.Sku).ToList();
break; break;
case PSort.Qty: case ProductSort.Qty:
if (Descending) if (Descending)
{ {
Inventory = Inventory.OrderByDescending(x => x.Quantity).ToList(); Inventory = Inventory.OrderByDescending(x => x.Quantity).ToList();
@ -66,10 +66,10 @@ public partial class CustomerInventoryListComponent
} }
Inventory = Inventory.OrderBy(x => x.Quantity).ToList(); Inventory = Inventory.OrderBy(x => x.Quantity).ToList();
break; break;
case PSort.None: case ProductSort.None:
Inventory = Inventory.OrderBy(x => x.Description).ToList(); Inventory = Inventory.OrderBy(x => x.Description).ToList();
break; break;
case PSort.Abbr: case ProductSort.Abbr:
break; break;
default: default:
Inventory = Inventory.OrderBy(x => x.Description).ToList(); Inventory = Inventory.OrderBy(x => x.Description).ToList();

View file

@ -24,9 +24,9 @@
<div class="list-group mt-2"> <div class="list-group mt-2">
<div class="list-group-item"> <div class="list-group-item">
<div class="row"> <div class="row">
<div class="col-sm-4" style="cursor: pointer;" @onclick="() => SortProducts(PSort.Desc)"><i class="bi-sort-alpha-down"></i> Navn <i class="bi-sort-alpha-up-alt"></i></div> <div class="col-sm-4" style="cursor: pointer;" @onclick="() => SortProducts(ProductSort.Desc)"><i class="bi-sort-alpha-down"></i> Navn <i class="bi-sort-alpha-up-alt"></i></div>
<div class="col-sm-3" style="cursor: pointer;" @onclick="() => SortProducts(PSort.Sku)"><i class="bi-sort-alpha-down"></i> Varenr <i class="bi-sort-alpha-up-alt"></i></div> <div class="col-sm-3" style="cursor: pointer;" @onclick="() => SortProducts(ProductSort.Sku)"><i class="bi-sort-alpha-down"></i> Varenr <i class="bi-sort-alpha-up-alt"></i></div>
<div class="col-sm-2 text-center" style="cursor: pointer;" @onclick="() => SortProducts(PSort.Qty)"><i class="bi-sort-numeric-down"></i> Antal <i class="bi-sort-numeric-up-alt"></i></div> <div class="col-sm-2 text-center" style="cursor: pointer;" @onclick="() => SortProducts(ProductSort.Qty)"><i class="bi-sort-numeric-down"></i> Antal <i class="bi-sort-numeric-up-alt"></i></div>
<div class="col-sm-2"></div> <div class="col-sm-2"></div>
<div class="col-sm-1"></div> <div class="col-sm-1"></div>
</div> </div>

View file

@ -34,12 +34,12 @@ public partial class CustomerProductCheckListComponent
Inventory = Inventory.OrderBy(x => x.Description).ToList(); Inventory = Inventory.OrderBy(x => x.Description).ToList();
} }
private void SortProducts(PSort column) private void SortProducts(ProductSort column)
{ {
Descending = !Descending; Descending = !Descending;
switch (column) switch (column)
{ {
case PSort.Desc: case ProductSort.Desc:
if (Descending) if (Descending)
{ {
Inventory = Inventory.OrderByDescending(x => x.Description).ToList(); Inventory = Inventory.OrderByDescending(x => x.Description).ToList();
@ -47,7 +47,7 @@ public partial class CustomerProductCheckListComponent
} }
Inventory = Inventory.OrderBy(x => x.Description).ToList(); Inventory = Inventory.OrderBy(x => x.Description).ToList();
break; break;
case PSort.Sku: case ProductSort.Sku:
if (Descending) if (Descending)
{ {
Inventory = Inventory.OrderByDescending(x => x.Sku).ToList(); Inventory = Inventory.OrderByDescending(x => x.Sku).ToList();
@ -55,7 +55,7 @@ public partial class CustomerProductCheckListComponent
} }
Inventory = Inventory.OrderBy(x => x.Sku).ToList(); Inventory = Inventory.OrderBy(x => x.Sku).ToList();
break; break;
case PSort.Qty: case ProductSort.Qty:
if (Descending) if (Descending)
{ {
Inventory = Inventory.OrderByDescending(x => x.Quantity).ToList(); Inventory = Inventory.OrderByDescending(x => x.Quantity).ToList();
@ -63,9 +63,9 @@ public partial class CustomerProductCheckListComponent
} }
Inventory = Inventory.OrderBy(x => x.Quantity).ToList(); Inventory = Inventory.OrderBy(x => x.Quantity).ToList();
break; break;
case PSort.None: case ProductSort.None:
break; break;
case PSort.Abbr: case ProductSort.Abbr:
break; break;
default: default:
Inventory = Inventory.OrderByDescending(x => x.Quantity).ToList(); Inventory = Inventory.OrderByDescending(x => x.Quantity).ToList();

View file

@ -61,7 +61,7 @@ public partial class OfficeCountryCustomerListComponent
private async Task ShowInvoiceList(string companyId) private async Task ShowInvoiceList(string companyId)
{ {
// check for console manipulation // check for console manipulation
if (!Utils.Validate(VType.Id, companyId)) return; if (!Utils.Validate(ValidateType.Id, companyId)) return;
SelectedCompany = CompanyList.First(x => x.CompanyId == companyId); SelectedCompany = CompanyList.First(x => x.CompanyId == companyId);
// call erp to crm sync before requesting invoices // call erp to crm sync before requesting invoices
var newSyncDate = await HistoryRepo.RequestErpToCrmSync(CountryCode, companyId, SelectedCompany.HistorySync); var newSyncDate = await HistoryRepo.RequestErpToCrmSync(CountryCode, companyId, SelectedCompany.HistorySync);
@ -74,7 +74,7 @@ public partial class OfficeCountryCustomerListComponent
private async Task ShowActivityList(string companyId) private async Task ShowActivityList(string companyId)
{ {
// check for console manipulation // check for console manipulation
if (!Utils.Validate(VType.Id, companyId)) return; if (!Utils.Validate(ValidateType.Id, companyId)) return;
SelectedCompany = CompanyList.First(x => x.CompanyId == companyId); SelectedCompany = CompanyList.First(x => x.CompanyId == companyId);
ActivityList = await ActivityRepo.RequestActivityList(companyId); ActivityList = await ActivityRepo.RequestActivityList(companyId);
ActivityListOverlay.Show(); ActivityListOverlay.Show();
@ -83,7 +83,7 @@ public partial class OfficeCountryCustomerListComponent
private async Task ShowInventory(string companyId) private async Task ShowInventory(string companyId)
{ {
// check for console manipulation // check for console manipulation
if (!Utils.Validate(VType.Id, companyId)) return; if (!Utils.Validate(ValidateType.Id, companyId)) return;
SelectedCompany = CompanyList.First(x => x.CompanyId == companyId); SelectedCompany = CompanyList.First(x => x.CompanyId == companyId);
// call erp to crm sync before requesting products // call erp to crm sync before requesting products
var newSyncDate = await HistoryRepo.RequestErpToCrmSync(CountryCode, companyId, SelectedCompany.HistorySync); var newSyncDate = await HistoryRepo.RequestErpToCrmSync(CountryCode, companyId, SelectedCompany.HistorySync);
@ -96,7 +96,7 @@ public partial class OfficeCountryCustomerListComponent
private async Task ShowOrder(string companyId) private async Task ShowOrder(string companyId)
{ {
// check for console manipulation // check for console manipulation
if (!Utils.Validate(VType.Id, companyId)) return; if (!Utils.Validate(ValidateType.Id, companyId)) return;
SelectedCompany = CompanyList.First(x => x.CompanyId == companyId); SelectedCompany = CompanyList.First(x => x.CompanyId == companyId);
} }
} }

View file

@ -25,9 +25,9 @@
<div class="list-group mt-2"> <div class="list-group mt-2">
<div class="list-group-item"> <div class="list-group-item">
<div class="row"> <div class="row">
<div class="col-sm-4" style="cursor: pointer;" @onclick="() => SortProducts(PSort.Desc)"><i class="bi-sort-alpha-down"></i> Navn <i class="bi-sort-alpha-up-alt"></i></div> <div class="col-sm-4" style="cursor: pointer;" @onclick="() => SortProducts(ProductSort.Desc)"><i class="bi-sort-alpha-down"></i> Navn <i class="bi-sort-alpha-up-alt"></i></div>
<div class="col-sm-3" style="cursor: pointer;" @onclick="() => SortProducts(PSort.Sku)"><i class="bi-sort-alpha-down"></i> Varenr <i class="bi-sort-alpha-up-alt"></i></div> <div class="col-sm-3" style="cursor: pointer;" @onclick="() => SortProducts(ProductSort.Sku)"><i class="bi-sort-alpha-down"></i> Varenr <i class="bi-sort-alpha-up-alt"></i></div>
<div class="col-sm-2 text-center" style="cursor: pointer;" @onclick="() => SortProducts(PSort.Qty)"><i class="bi-sort-numeric-down"></i> Antal <i class="bi-sort-numeric-up-alt"></i></div> <div class="col-sm-2 text-center" style="cursor: pointer;" @onclick="() => SortProducts(ProductSort.Qty)"><i class="bi-sort-numeric-down"></i> Antal <i class="bi-sort-numeric-up-alt"></i></div>
<div class="col-sm-2"></div> <div class="col-sm-2"></div>
<div class="col-sm-1"></div> <div class="col-sm-1"></div>
</div> </div>

View file

@ -41,12 +41,12 @@ public partial class OfficeInventoryListComponent
Inventory = Inventory.OrderBy(x => x.Description).ToList(); Inventory = Inventory.OrderBy(x => x.Description).ToList();
} }
private void SortProducts(PSort column) private void SortProducts(ProductSort column)
{ {
Descending = !Descending; Descending = !Descending;
switch (column) switch (column)
{ {
case PSort.Desc: case ProductSort.Desc:
if (Descending) if (Descending)
{ {
Inventory = Inventory.OrderByDescending(x => x.Description).ToList(); Inventory = Inventory.OrderByDescending(x => x.Description).ToList();
@ -54,7 +54,7 @@ public partial class OfficeInventoryListComponent
} }
Inventory = Inventory.OrderBy(x => x.Description).ToList(); Inventory = Inventory.OrderBy(x => x.Description).ToList();
break; break;
case PSort.Sku: case ProductSort.Sku:
if (Descending) if (Descending)
{ {
Inventory = Inventory.OrderByDescending(x => x.Sku).ToList(); Inventory = Inventory.OrderByDescending(x => x.Sku).ToList();
@ -62,7 +62,7 @@ public partial class OfficeInventoryListComponent
} }
Inventory = Inventory.OrderBy(x => x.Sku).ToList(); Inventory = Inventory.OrderBy(x => x.Sku).ToList();
break; break;
case PSort.Qty: case ProductSort.Qty:
if (Descending) if (Descending)
{ {
Inventory = Inventory.OrderByDescending(x => x.Quantity).ToList(); Inventory = Inventory.OrderByDescending(x => x.Quantity).ToList();
@ -70,9 +70,9 @@ public partial class OfficeInventoryListComponent
} }
Inventory = Inventory.OrderBy(x => x.Quantity).ToList(); Inventory = Inventory.OrderBy(x => x.Quantity).ToList();
break; break;
case PSort.None: case ProductSort.None:
break; break;
case PSort.Abbr: case ProductSort.Abbr:
break; break;
default: default:
Inventory = Inventory.OrderByDescending(x => x.Quantity).ToList(); Inventory = Inventory.OrderByDescending(x => x.Quantity).ToList();

View file

@ -51,18 +51,18 @@
@quote.OrderDate @quote.OrderDate
</div> </div>
<div class="col-sm-3"> <div class="col-sm-3">
<button type="button" class="btn btn-outline-dark me-2" @onclick="() => SetQuote(quote.ESalesNumber, QStatus.Lose)"> <button type="button" class="btn btn-outline-dark me-2" @onclick="() => SetQuote(quote.ESalesNumber, QuoteStatus.Lose)">
<i class="bi-trash-fill"></i> <i class="bi-trash-fill"></i>
</button> </button>
<button type="button" class="btn btn-outline-dark me-2" @onclick="() => SetQuote(quote.ESalesNumber, QStatus.Archive)"> <button type="button" class="btn btn-outline-dark me-2" @onclick="() => SetQuote(quote.ESalesNumber, QuoteStatus.Archive)">
<i class="bi-archive-fill"></i> <i class="bi-archive-fill"></i>
</button> </button>
<button type="button" class="btn btn-outline-dark me-2" @onclick="() => SetQuote(quote.ESalesNumber, QStatus.Note)"> <button type="button" class="btn btn-outline-dark me-2" @onclick="() => SetQuote(quote.ESalesNumber, QuoteStatus.Note)">
<i class="bi-tag-fill"></i> <i class="bi-tag-fill"></i>
</button> </button>
@if (quote.QuoteStatusEnum == "None") @if (quote.QuoteStatusEnum == "None")
{ {
<button type="button" class="btn btn-success" @onclick="() => SetQuote(quote.ESalesNumber, QStatus.Win)"> <button type="button" class="btn btn-success" @onclick="() => SetQuote(quote.ESalesNumber, QuoteStatus.Order)">
<i class="bi-cart-fill"></i> <i class="bi-cart-fill"></i>
</button> </button>
} }

View file

@ -25,7 +25,7 @@ public partial class QuoteListComponent
public List<ReportItemView> Quotes { get; set; } = new(); public List<ReportItemView> Quotes { get; set; } = new();
[Parameter] public EventCallback<QuoteCallbackArgs> OnChangedCallback { get; set; } [Parameter] public EventCallback<QuoteCallbackArgs> OnChangedCallback { get; set; }
private async Task SetQuote(string eSalesNumber, QStatus status) private async Task SetQuote(string eSalesNumber, QuoteStatus status)
{ {
var args = new QuoteCallbackArgs() var args = new QuoteCallbackArgs()
{ {

View file

@ -21,13 +21,13 @@
<div class="row mb-3"> <div class="row mb-3">
<div class="col-sm-4"> <div class="col-sm-4">
<div class="btn-group" role="group" aria-label="Ordre status"> <div class="btn-group" role="group" aria-label="Ordre status">
<input type="radio" class="btn-check" name="btn-order" id="btn-order1" @onchange="() => GetWithStatus(PStatus.None)" autocomplete="off" checked="checked"/> <input type="radio" class="btn-check" name="btn-order" id="btn-order1" @onchange="() => GetWithStatus(ProcessStatus.None)" autocomplete="off" checked="checked"/>
<label class="btn btn-success" for="btn-order1">Ubehandlet</label> <label class="btn btn-success" for="btn-order1">Ubehandlet</label>
<input type="radio" class="btn-check" name="btn-order" id="btn-order2" @onchange="() => GetWithStatus(PStatus.Picked)" autocomplete="off"/> <input type="radio" class="btn-check" name="btn-order" id="btn-order2" @onchange="() => GetWithStatus(ProcessStatus.Picked)" autocomplete="off"/>
<label class="btn btn-warning" for="btn-order2">Plukket</label> <label class="btn btn-warning" for="btn-order2">Plukket</label>
<input type="radio" class="btn-check" name="btn-order" id="btn-order3" @onchange="() => GetWithStatus(PStatus.Packed)" autocomplete="off"/> <input type="radio" class="btn-check" name="btn-order" id="btn-order3" @onchange="() => GetWithStatus(ProcessStatus.Packed)" autocomplete="off"/>
<label class="btn btn-danger" for="btn-order3">Pakket</label> <label class="btn btn-danger" for="btn-order3">Pakket</label>
</div> </div>
</div> </div>

View file

@ -24,7 +24,7 @@ public partial class WarehouseListComponent
[Parameter] public string Header { get; set; } = ""; [Parameter] public string Header { get; set; } = "";
[Parameter] public List<WarehouseOrderView> OrderList { get; set; } = new(); [Parameter] public List<WarehouseOrderView> OrderList { get; set; } = new();
[Parameter] public bool ReadyToShip { get; set; } [Parameter] public bool ReadyToShip { get; set; }
[Parameter] public EventCallback<PStatus> OnGetStatus { get; set; } [Parameter] public EventCallback<ProcessStatus> OnGetStatus { get; set; }
[Parameter] public EventCallback OnSetShipped { get; set; } [Parameter] public EventCallback OnSetShipped { get; set; }
[Parameter] public EventCallback<string> OnQPak { get; set; } [Parameter] public EventCallback<string> OnQPak { get; set; }
@ -43,7 +43,7 @@ public partial class WarehouseListComponent
await OnSetShipped.InvokeAsync(); await OnSetShipped.InvokeAsync();
} }
private async Task GetWithStatus(PStatus status) private async Task GetWithStatus(ProcessStatus status)
{ {
await OnGetStatus.InvokeAsync(status); await OnGetStatus.InvokeAsync(status);
} }

View file

@ -23,14 +23,14 @@ namespace Wonky.Client.Helpers;
/// </summary> /// </summary>
public static class Utils public static class Utils
{ {
public static bool Validate(VType vType, string toValidate) public static bool Validate(ValidateType validateType, string toValidate)
{ {
return vType switch return validateType switch
{ {
VType.ISODate => toValidate.Length == 10 && DateTime.TryParse(toValidate, out _), ValidateType.IsoDate => toValidate.Length == 10 && DateTime.TryParse(toValidate, out _),
VType.Id => Squid.TryDecode(toValidate, out _), ValidateType.Id => Squid.TryDecode(toValidate, out _),
VType.Passwd => IsValidPasswd(toValidate), ValidateType.Passwd => IsValidPasswd(toValidate),
VType.Email => IsValidEmail(toValidate), ValidateType.Email => IsValidEmail(toValidate),
_ => false _ => false
}; };
} }
@ -51,16 +51,19 @@ public static class Utils
{ {
validConditions++; validConditions++;
} }
else return false;
if (toValidate.Any(c => c is >= 'A' and <= 'Z')) if (toValidate.Any(c => c is >= 'A' and <= 'Z'))
{ {
validConditions++; validConditions++;
} }
else return false;
if (toValidate.Any(c => c is >= '0' and <= '9')) if (toValidate.Any(c => c is >= '0' and <= '9'))
{ {
validConditions++; validConditions++;
} }
else return false;
return validConditions == 3; return validConditions == 3;
} }

View file

@ -41,7 +41,7 @@ public interface ISystemUserRepository
/// </summary> /// </summary>
/// <param name="model"></param> /// <param name="model"></param>
/// <returns></returns> /// <returns></returns>
Task<UserManagerEditView> CreateUser(UserManagerEditView model); Task<UserManagerEditView> CreateUser(UserManagerCreate model);
/// <summary> /// <summary>
/// Update Advisor using userId and updated data /// Update Advisor using userId and updated data

View file

@ -69,7 +69,7 @@ public class SystemUserRepository : ISystemUserRepository
/// </summary> /// </summary>
/// <param name="model"></param> /// <param name="model"></param>
/// <returns></returns> /// <returns></returns>
public async Task<UserManagerEditView> CreateUser(UserManagerEditView model) public async Task<UserManagerEditView> CreateUser(UserManagerCreate model)
{ {
var result = await _client.PostAsJsonAsync($"{_api.UserManager}", model, _options); var result = await _client.PostAsJsonAsync($"{_api.UserManager}", model, _options);
if (!result.IsSuccessStatusCode) if (!result.IsSuccessStatusCode)

View file

@ -0,0 +1,13 @@
namespace Wonky.Client.Models;
public class AssignedRoles
{
public bool Admin { get; set; }
public bool Advisor { get; set; }
public bool EShop { get; set; }
public bool EDoc { get; set; }
public bool Management { get; set; }
public bool Office { get; set; }
public bool Supervisor { get; set; }
public bool Warehouse { get; set; }
}

View file

@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations;
namespace Wonky.Client.Models;
public class PasswordInput
{
/// <summary>
/// New password
/// </summary>
[Required(ErrorMessage = "Kode skal udfyldes")][MinLength(10, ErrorMessage = "Password skal være mindst 10 tegn og indeholde a-z, A-Z, 0-9")]
public string NewPassword { get; set; } = "";
/// <summary>
/// Password confirmation
/// </summary>
[Compare(nameof(NewPassword), ErrorMessage = "Koderne er ikke ens.")]
public string ConfirmPassword { get; set; } = "";
}

View file

@ -16,7 +16,7 @@
namespace Wonky.Client.Models; namespace Wonky.Client.Models;
public enum PTarget public enum PrintTarget
{ {
None, None,
All, All,

View file

@ -16,7 +16,7 @@
namespace Wonky.Client.Models; namespace Wonky.Client.Models;
public enum PStatus public enum ProcessStatus
{ {
None, None,
Picked, Picked,
@ -24,5 +24,5 @@ public enum PStatus
Shipped, Shipped,
All, All,
Express, Express,
Accepted Printed
} }

View file

@ -16,7 +16,7 @@
namespace Wonky.Client.Models; namespace Wonky.Client.Models;
public enum PSort public enum ProductSort
{ {
None, None,
Desc, Desc,

View file

@ -25,5 +25,5 @@ public class QuoteCallbackArgs
/// <summary> /// <summary>
/// QStatus /// QStatus
/// </summary> /// </summary>
public QStatus Status { get; set; } public QuoteStatus Status { get; set; }
} }

View file

@ -16,10 +16,10 @@
namespace Wonky.Client.Models; namespace Wonky.Client.Models;
public enum QStatus public enum QuoteStatus
{ {
None, None,
Win, Order,
Lose, Lose,
Archive, Archive,
Note, Note,

View file

@ -1,9 +1,9 @@
namespace Wonky.Client.Models; namespace Wonky.Client.Models;
public enum VType public enum ValidateType
{ {
Id, Id,
ISODate, IsoDate,
Passwd, Passwd,
Email Email
} }

View file

@ -30,9 +30,9 @@
<div class="list-group mt-2"> <div class="list-group mt-2">
<div class="list-group-item bg-dark text-white"> <div class="list-group-item bg-dark text-white">
<div class="row"> <div class="row">
<div class="col-sm-4" style="cursor: pointer;" @onclick="() => SortProducts(PSort.Desc)"><i class="bi-sort-alpha-down"></i> Navn <i class="bi-sort-alpha-up-alt"></i></div> <div class="col-sm-4" style="cursor: pointer;" @onclick="() => SortProducts(ProductSort.Desc)"><i class="bi-sort-alpha-down"></i> Navn <i class="bi-sort-alpha-up-alt"></i></div>
<div class="col-sm-3" style="cursor: pointer;" @onclick="() => SortProducts(PSort.Sku)"><i class="bi-sort-alpha-down"></i> Varenr <i class="bi-sort-alpha-up-alt"></i></div> <div class="col-sm-3" style="cursor: pointer;" @onclick="() => SortProducts(ProductSort.Sku)"><i class="bi-sort-alpha-down"></i> Varenr <i class="bi-sort-alpha-up-alt"></i></div>
<div class="col-sm-2 text-center" style="cursor: pointer;" @onclick="() => SortProducts(PSort.Qty)"><i class="bi-sort-numeric-down"></i> Antal <i class="bi-sort-numeric-up-alt"></i></div> <div class="col-sm-2 text-center" style="cursor: pointer;" @onclick="() => SortProducts(ProductSort.Qty)"><i class="bi-sort-numeric-down"></i> Antal <i class="bi-sort-numeric-up-alt"></i></div>
<div class="col-sm-2"></div> <div class="col-sm-2"></div>
<div class="col-sm-1"></div> <div class="col-sm-1"></div>
</div> </div>

View file

@ -58,12 +58,12 @@ public partial class OfficeCustomerProductListOverlay : IDisposable
ReorderOverlay.Show(); ReorderOverlay.Show();
} }
private void SortProducts(PSort column) private void SortProducts(ProductSort column)
{ {
Descending = !Descending; Descending = !Descending;
switch (column) switch (column)
{ {
case PSort.Desc: case ProductSort.Desc:
if (Descending) if (Descending)
{ {
Inventory = Inventory.OrderByDescending(x => x.Description).ToList(); Inventory = Inventory.OrderByDescending(x => x.Description).ToList();
@ -72,7 +72,7 @@ public partial class OfficeCustomerProductListOverlay : IDisposable
Inventory = Inventory.OrderBy(x => x.Description).ToList(); Inventory = Inventory.OrderBy(x => x.Description).ToList();
break; break;
case PSort.Sku: case ProductSort.Sku:
if (Descending) if (Descending)
{ {
Inventory = Inventory.OrderByDescending(x => x.Sku).ToList(); Inventory = Inventory.OrderByDescending(x => x.Sku).ToList();
@ -81,7 +81,7 @@ public partial class OfficeCustomerProductListOverlay : IDisposable
Inventory = Inventory.OrderBy(x => x.Sku).ToList(); Inventory = Inventory.OrderBy(x => x.Sku).ToList();
break; break;
case PSort.Qty: case ProductSort.Qty:
if (Descending) if (Descending)
{ {
Inventory = Inventory.OrderByDescending(x => x.Quantity).ToList(); Inventory = Inventory.OrderByDescending(x => x.Quantity).ToList();
@ -90,9 +90,9 @@ public partial class OfficeCustomerProductListOverlay : IDisposable
Inventory = Inventory.OrderBy(x => x.Quantity).ToList(); Inventory = Inventory.OrderBy(x => x.Quantity).ToList();
break; break;
case PSort.None: case ProductSort.None:
break; break;
case PSort.Abbr: case ProductSort.Abbr:
break; break;
default: default:
Inventory = Inventory.OrderByDescending(x => x.Quantity).ToList(); Inventory = Inventory.OrderByDescending(x => x.Quantity).ToList();

View file

@ -27,22 +27,22 @@
<div class="col-sm-4 text-end"> <div class="col-sm-4 text-end">
<div class="btn-group btn-group" role="group" aria-label="Filter tilbud"> <div class="btn-group btn-group" role="group" aria-label="Filter tilbud">
<input type="radio" class="btn-check" name="btn-filter" id="btn-filter4" autocomplete="off" @onchange="() => FilterQuotes(QStatus.All)"/> <input type="radio" class="btn-check" name="btn-filter" id="btn-filter4" autocomplete="off" @onchange="() => FilterQuotes(QuoteStatus.All)"/>
<label class="btn btn-dark" for="btn-filter4"><i class="bi-list"style="font-size: 1.3rem"></i></label> <label class="btn btn-dark" for="btn-filter4"><i class="bi-list"style="font-size: 1.3rem"></i></label>
<input type="radio" class="btn-check" name="btn-filter" id="btn-filter1" autocomplete="off" @onchange="() => FilterQuotes(QStatus.Lose)"/> <input type="radio" class="btn-check" name="btn-filter" id="btn-filter1" autocomplete="off" @onchange="() => FilterQuotes(QuoteStatus.Lose)"/>
<label class="btn btn-dark" for="btn-filter1"><i class="bi-trash-fill"style="font-size: 1.3rem"></i></label> <label class="btn btn-dark" for="btn-filter1"><i class="bi-trash-fill"style="font-size: 1.3rem"></i></label>
<input type="radio" class="btn-check" name="btn-filter" id="btn-filter2" autocomplete="off" @onchange="() => FilterQuotes(QStatus.Archive)"/> <input type="radio" class="btn-check" name="btn-filter" id="btn-filter2" autocomplete="off" @onchange="() => FilterQuotes(QuoteStatus.Archive)"/>
<label class="btn btn-dark" for="btn-filter2"><i class="bi-archive-fill"style="font-size: 1.3rem"></i></label> <label class="btn btn-dark" for="btn-filter2"><i class="bi-archive-fill"style="font-size: 1.3rem"></i></label>
<input type="radio" class="btn-check" name="btn-filter" id="btn-filter5" autocomplete="off" @onchange="() => FilterQuotes(QStatus.None)"/> <input type="radio" class="btn-check" name="btn-filter" id="btn-filter5" autocomplete="off" @onchange="() => FilterQuotes(QuoteStatus.None)"/>
<label class="btn btn-dark" for="btn-filter5"><i class="bi-question-circle-fill"style="font-size: 1.3rem"></i></label> <label class="btn btn-dark" for="btn-filter5"><i class="bi-question-circle-fill"style="font-size: 1.3rem"></i></label>
<input type="radio" class="btn-check" name="btn-filter" id="btn-filter3" autocomplete="off" @onchange="() => FilterQuotes(QStatus.Note)"/> <input type="radio" class="btn-check" name="btn-filter" id="btn-filter3" autocomplete="off" @onchange="() => FilterQuotes(QuoteStatus.Note)"/>
<label class="btn btn-dark" for="btn-filter3"><i class="bi-tag-fill"style="font-size: 1.3rem"></i></label> <label class="btn btn-dark" for="btn-filter3"><i class="bi-tag-fill"style="font-size: 1.3rem"></i></label>
<input type="radio" class="btn-check" name="btn-filter" id="btn-filter6" autocomplete="off" @onchange="() => FilterQuotes(QStatus.NoteOpen)" checked/> <input type="radio" class="btn-check" name="btn-filter" id="btn-filter6" autocomplete="off" @onchange="() => FilterQuotes(QuoteStatus.NoteOpen)" checked/>
<label class="btn btn-dark" for="btn-filter6"><i class="bi-activity"style="font-size: 1.3rem"></i></label> <label class="btn btn-dark" for="btn-filter6"><i class="bi-activity"style="font-size: 1.3rem"></i></label>
</div> </div>

View file

@ -36,7 +36,7 @@ public partial class AdvisorQuoteListPage : IDisposable
private List<ReportItemView> QuoteList { get; set; } = new(); private List<ReportItemView> QuoteList { get; set; } = new();
private List<ReportItemView> FilteredQuotes { get; set; } = new(); private List<ReportItemView> FilteredQuotes { get; set; } = new();
private bool Working { get; set; } private bool Working { get; set; }
private QStatus QFilter { get; set; } = QStatus.NoteOpen; private QuoteStatus QuoteFilter { get; set; } = QuoteStatus.NoteOpen;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
@ -48,20 +48,20 @@ public partial class AdvisorQuoteListPage : IDisposable
Working = false; Working = false;
// filter quotes - if any - default to QStatus.NoteOpen // filter quotes - if any - default to QStatus.NoteOpen
if (QuoteList.Any()) if (QuoteList.Any())
await FilterQuotes(QFilter); await FilterQuotes(QuoteFilter);
} }
private async Task FilterQuotes(QStatus status) private async Task FilterQuotes(QuoteStatus status)
{ {
QFilter = status; QuoteFilter = status;
QuoteList = await Storage.GetItemAsync<List<ReportItemView>>("quotes"); QuoteList = await Storage.GetItemAsync<List<ReportItemView>>("quotes");
FilteredQuotes = QFilter switch FilteredQuotes = QuoteFilter switch
{ {
QStatus.None => QuoteList.Where(x => x.QuoteStatusEnum is "None").ToList(), QuoteStatus.None => QuoteList.Where(x => x.QuoteStatusEnum is "None").ToList(),
QStatus.Lose => QuoteList.Where(x => x.QuoteStatusEnum is "Lose").ToList(), QuoteStatus.Lose => QuoteList.Where(x => x.QuoteStatusEnum is "Lose").ToList(),
QStatus.Archive => QuoteList.Where(x => x.QuoteStatusEnum is "Archive").ToList(), QuoteStatus.Archive => QuoteList.Where(x => x.QuoteStatusEnum is "Archive").ToList(),
QStatus.Note => QuoteList.Where(x => x.QuoteStatusEnum is "Note").ToList(), QuoteStatus.Note => QuoteList.Where(x => x.QuoteStatusEnum is "Note").ToList(),
QStatus.NoteOpen => QuoteList.Where(x => x.QuoteStatusEnum is "Note" or "None").ToList(), QuoteStatus.NoteOpen => QuoteList.Where(x => x.QuoteStatusEnum is "Note" or "None").ToList(),
_ => QuoteList.ToList() _ => QuoteList.ToList()
}; };
} }
@ -71,7 +71,7 @@ public partial class AdvisorQuoteListPage : IDisposable
Working = true; Working = true;
// find the quote to update // find the quote to update
var quote = QuoteList.First(x => x.ESalesNumber == args.ESalesNumber); var quote = QuoteList.First(x => x.ESalesNumber == args.ESalesNumber);
if (args.Status == QStatus.Win) if (args.Status == QuoteStatus.Order)
quote.OrderDate = $"{DateTime.Now:yyyy-MM-dd}"; quote.OrderDate = $"{DateTime.Now:yyyy-MM-dd}";
quote.QuoteStatusEnum = Utils.EnumToString(args.Status); quote.QuoteStatusEnum = Utils.EnumToString(args.Status);
// send update request to backend // send update request to backend
@ -85,7 +85,7 @@ public partial class AdvisorQuoteListPage : IDisposable
await Storage.SetItemAsync("quotes", QuoteList.OrderBy(x => x.Company.Name)); await Storage.SetItemAsync("quotes", QuoteList.OrderBy(x => x.Company.Name));
// filter quotes - if any - based on active filter // filter quotes - if any - based on active filter
if(QuoteList.Any()) if(QuoteList.Any())
await FilterQuotes(QFilter); await FilterQuotes(QuoteFilter);
Working = false; Working = false;
// signal page state changed // signal page state changed
StateHasChanged(); StateHasChanged();

View file

@ -26,10 +26,10 @@
<WorkDateComponent OnChangedCallback="FetchReport"/> <WorkDateComponent OnChangedCallback="FetchReport"/>
</div> </div>
<div class="col-sm-2 d-grid"> <div class="col-sm-2 d-grid">
<button class="btn btn-warning" @onclick="() => Print(PTarget.FrontPage)"><i class="bi-printer"></i> Forside</button> <button class="btn btn-warning" @onclick="() => Print(PrintTarget.FrontPage)"><i class="bi-printer"></i> Forside</button>
</div> </div>
<div class="col-sm-2 d-grid"> <div class="col-sm-2 d-grid">
<button class="btn btn-success" @onclick="() => Print(PTarget.OrderPage)"><i class="bi-printer"></i> Ordrer</button> <button class="btn btn-success" @onclick="() => Print(PrintTarget.OrderPage)"><i class="bi-printer"></i> Ordrer</button>
</div> </div>
<div class="col-sm-2 d-grid"> <div class="col-sm-2 d-grid">
<button class="btn btn-primary" type="button" onclick="window.print();"><i class="bi-printer"></i> Rapport</button> <button class="btn btn-primary" type="button" onclick="window.print();"><i class="bi-printer"></i> Rapport</button>

View file

@ -58,21 +58,21 @@ public partial class AdvisorReportViewPage : IDisposable
await FetchReport(ReportDate); await FetchReport(ReportDate);
} }
private void Print(PTarget target) private void Print(PrintTarget target)
{ {
ReturnUrl = new Uri(Navigator.Uri).AbsolutePath; ReturnUrl = new Uri(Navigator.Uri).AbsolutePath;
switch (target) switch (target)
{ {
case PTarget.OrderPage: case PrintTarget.OrderPage:
Navigator.NavigateTo($"/report/print/orders/{XUserInfo.CountryCode.ToLower()}/{XUserInfo.UserId}/{ReportDate}?returnUrl={ReturnUrl}"); Navigator.NavigateTo($"/report/print/orders/{XUserInfo.CountryCode.ToLower()}/{XUserInfo.UserId}/{ReportDate}?returnUrl={ReturnUrl}");
break; break;
case PTarget.FrontPage: case PrintTarget.FrontPage:
Navigator.NavigateTo($"/report/print/summary/{XUserInfo.CountryCode.ToLower()}/{XUserInfo.UserId}/{ReportDate}?returnUrl={ReturnUrl}"); Navigator.NavigateTo($"/report/print/summary/{XUserInfo.CountryCode.ToLower()}/{XUserInfo.UserId}/{ReportDate}?returnUrl={ReturnUrl}");
break; break;
case PTarget.None: case PrintTarget.None:
break; break;
case PTarget.All: case PrintTarget.All:
break; break;
default: default:
throw new ArgumentOutOfRangeException(nameof(target), target, null); throw new ArgumentOutOfRangeException(nameof(target), target, null);

View file

@ -74,7 +74,7 @@ public partial class CommonReportPrintOrderPage
await ProcessRepo.UpdateWarehouseOrderStatus(new OrderProcessState await ProcessRepo.UpdateWarehouseOrderStatus(new OrderProcessState
{ {
OrderId = item.ActivityId, OrderId = item.ActivityId,
ProcessStatusEnum = Utils.EnumToString(PStatus.Accepted) ProcessStatusEnum = Utils.EnumToString(ProcessStatus.Printed)
}); });
} }
Toaster.ClearAll(); Toaster.ClearAll();

View file

@ -26,10 +26,10 @@
<WorkDateComponent OnChangedCallback="FetchUserReport"/> <WorkDateComponent OnChangedCallback="FetchUserReport"/>
</div> </div>
<div class="col-sm-2 d-grid"> <div class="col-sm-2 d-grid">
<button class="btn btn-warning" @onclick="() => Print(PTarget.FrontPage)"><i class="bi-printer"></i> Forside</button> <button class="btn btn-warning" @onclick="() => Print(PrintTarget.FrontPage)"><i class="bi-printer"></i> Forside</button>
</div> </div>
<div class="col-sm-2 d-grid"> <div class="col-sm-2 d-grid">
<button class="btn btn-success" @onclick="() => Print(PTarget.OrderPage)"><i class="bi-printer"></i> Ordrer</button> <button class="btn btn-success" @onclick="() => Print(PrintTarget.OrderPage)"><i class="bi-printer"></i> Ordrer</button>
</div> </div>
<div class="col-sm-2 d-grid"> <div class="col-sm-2 d-grid">
<button class="btn btn-primary" type="button" @onclick="Print"><i class="bi-printer"></i> Rapport</button> <button class="btn btn-primary" type="button" @onclick="Print"><i class="bi-printer"></i> Rapport</button>

View file

@ -69,20 +69,20 @@ public partial class OfficeAdvisorReportViewPage : IDisposable
.InvokeAsync<IJSObjectReference>("import", "/scripts/print-invoke.js"); .InvokeAsync<IJSObjectReference>("import", "/scripts/print-invoke.js");
} }
} }
private void Print(PTarget target) private void Print(PrintTarget target)
{ {
_returnUrl = new Uri(Navigator.Uri).AbsolutePath; _returnUrl = new Uri(Navigator.Uri).AbsolutePath;
switch (target) switch (target)
{ {
case PTarget.OrderPage: case PrintTarget.OrderPage:
Navigator.NavigateTo($"/report/print/orders/{CountryCode}/{UserId}/{ReportDate}?returnUrl={_returnUrl}"); Navigator.NavigateTo($"/report/print/orders/{CountryCode}/{UserId}/{ReportDate}?returnUrl={_returnUrl}");
break; break;
case PTarget.FrontPage: case PrintTarget.FrontPage:
Navigator.NavigateTo($"/report/print/summary/{CountryCode}/{UserId}/{ReportDate}?returnUrl={_returnUrl}"); Navigator.NavigateTo($"/report/print/summary/{CountryCode}/{UserId}/{ReportDate}?returnUrl={_returnUrl}");
break; break;
case PTarget.None: case PrintTarget.None:
break; break;
case PTarget.All: case PrintTarget.All:
break; break;
default: default:
throw new ArgumentOutOfRangeException(nameof(target), target, null); throw new ArgumentOutOfRangeException(nameof(target), target, null);
@ -101,7 +101,7 @@ public partial class OfficeAdvisorReportViewPage : IDisposable
await ProcessRepo.UpdateWarehouseOrderStatus(new OrderProcessState await ProcessRepo.UpdateWarehouseOrderStatus(new OrderProcessState
{ {
OrderId = item.ActivityId, OrderId = item.ActivityId,
ProcessStatusEnum = Utils.EnumToString(PStatus.Accepted) ProcessStatusEnum = Utils.EnumToString(ProcessStatus.Printed)
}); });
} }
Toaster.ClearAll(); Toaster.ClearAll();

View file

@ -22,113 +22,116 @@
<div class="card"> <div class="card">
<div class="card-header bg-dark text-white"> <div class="card-header bg-dark text-white">
<h3>Bruger info</h3> <div class="card-title">
<h3>Bruger oprettelse</h3>
</div>
</div> </div>
<div class="card-body"> <div class="card-body">
@if (!string.IsNullOrWhiteSpace(UserInfo.UserId)) <EditForm EditContext="NewUserContext" OnValidSubmit="CreateUser">
{
<EditForm EditContext="UserEditContext" OnValidSubmit="CreateUser">
<DataAnnotationsValidator/> <DataAnnotationsValidator/>
<div class="row"> <div class="row g-3 mb-3">
<div class="col"> <label for="firstName" class="col-sm-2 col-form-label">Fornavn</label>
<table class="table"> <div class="col-sm-4">
<tbody> <InputText id="firstName" class="form-control" @bind-Value="NewUserInfo.FirstName"/>
<tr class="align-middle"> <ValidationMessage For="@(() => NewUserInfo.FirstName)"></ValidationMessage>
<th scope="col"> </div>
Fornavn
</th> <label for="lastName" class="col-sm-2 col-form-label">Efternavn</label>
<td> <div class="col-sm-4">
<InputText id="firstName" class="form-control" @bind-Value="UserInfo.FirstName" readonly="@ReadOnly" /> <InputText id="lastName" class="form-control" @bind-Value="NewUserInfo.LastName"/>
<ValidationMessage For="@(() => UserInfo.FirstName)"></ValidationMessage> <ValidationMessage For="@(() => NewUserInfo.LastName)"></ValidationMessage>
</td> </div>
<th scope="col">
Efternavn <label for="email" class="col-sm-2 col-form-label">Email</label>
</th> <div class="col-sm-4">
<td> <InputText id="email" class="form-control" @bind-Value="NewUserInfo.Email"/>
<InputText id="lastName" class="form-control" @bind-Value="UserInfo.LastName" readonly="@ReadOnly" /> <ValidationMessage For="@(() => NewUserInfo.Email)"></ValidationMessage>
<ValidationMessage For="@(() => UserInfo.LastName)"></ValidationMessage> </div>
</td>
</tr> <label for="phoneNumber" class="col-sm-2 col-form-label">Mobil</label>
<tr class="align-middle"> <div class="col-sm-4">
<th scope="col"> <InputText id="phoneNumber" class="form-control" @bind-Value="NewUserInfo.PhoneNumber"/>
Email <ValidationMessage For="@(() => NewUserInfo.PhoneNumber)"></ValidationMessage>
</th> </div>
<td>
<InputText id="email" class="form-control" @bind-Value="UserInfo.Email" readonly="@ReadOnly" /> <label for="salesRep" class="col-sm-2 col-form-label">MedarbejderId</label>
<ValidationMessage For="@(() => UserInfo.Email)"></ValidationMessage> <div class="col-sm-4">
</td> <InputText id="salesRep" class="form-control" @bind-Value="NewUserInfo.SalesRep"/>
<th scope="col"> <ValidationMessage For="@(() => NewUserInfo.SalesRep)"></ValidationMessage>
Mobilnummer </div>
</th>
<td> <label for="countryCode" class="col-sm-2 col-form-label">Landekode</label>
<InputText id="phoneNumber" class="form-control" @bind-Value="UserInfo.PhoneNumber" readonly="@ReadOnly" /> <div class="col-sm-4">
<ValidationMessage For="@(() => UserInfo.PhoneNumber)"></ValidationMessage> <InputText id="countryCode" class="form-control" @bind-Value="NewUserInfo.CountryCode"/>
</td> <ValidationMessage For="@(() => NewUserInfo.CountryCode)"></ValidationMessage>
</tr> </div>
<tr class="align-middle">
<th scope="col"> @*
Sælgernr. <div class="ms-2 col-sm-4 form-check form-switch">
</th> <label for="lockoutEnabled" class="form-check-label">Spærret</label>
<td> <InputCheckbox id="lockoutEnabled" class="form-check-input" @bind-Value="NewUserInfo.LockoutEnabled"/>
<InputText id="salesRep" class="form-control" @bind-Value="UserInfo.SalesRep" readonly="@ReadOnly" /> <ValidationMessage For="@(() => NewUserInfo.LockoutEnabled)"></ValidationMessage>
<ValidationMessage For="@(() => UserInfo.SalesRep)"></ValidationMessage> </div>
</td> *@
<th scope="col">
Landekode <div class="ms-2 col-sm-2 form-check form-switch">
</th> <InputCheckbox id="admin" class="form-check-input" @bind-Value="AssignedRoles.Admin"/>
<td> <label for="admin" class="form-check-label">Administrator</label>
<InputText id="countryCode" class="form-control" @bind-Value="UserInfo.CountryCode" readonly="@ReadOnly" /> </div>
<ValidationMessage For="@(() => UserInfo.CountryCode)"></ValidationMessage> <div class="ms-2 col-sm-2 form-check form-switch">
</td> <InputCheckbox id="office" class="form-check-input" @bind-Value="AssignedRoles.Office"/>
</tr> <label for="office" class="form-check-label">Kontor</label>
<tr class="align-middle"> </div>
<th scope="col"> <div class="ms-2 col-sm-2 form-check form-switch">
Spærret <InputCheckbox id="eshop" class="form-check-input" @bind-Value="AssignedRoles.EShop"/>
</th> <label for="eshop" class="form-check-label">Kunde (EShop)</label>
<td colspan="3"> </div>
<InputCheckbox id="lockoutEnabled" class="form-check" @bind-Value="UserInfo.LockoutEnabled"/> <div class="ms-2 col-sm-2 form-check form-switch">
<ValidationMessage For="@(() => UserInfo.LockoutEnabled)"></ValidationMessage> <InputCheckbox id="warehouse" class="form-check-input" @bind-Value="AssignedRoles.Warehouse"/>
</td> <label for="warehouse" class="form-check-label">Forsendelse</label>
</tr> </div>
</tbody> <div class="ms-2 col-sm-2 form-check form-switch">
</table> <InputCheckbox id="management" class="form-check-input" @bind-Value="AssignedRoles.Management"/>
<label for="management" class="form-check-label">Ledelse</label>
</div>
<div class="ms-2 col-sm-2 form-check form-switch">
<InputCheckbox id="supervisor" class="form-check-input" @bind-Value="AssignedRoles.Supervisor"/>
<label for="supervisor" class="form-check-label">Supervisor</label>
</div>
<div class="ms-2 col-sm-2 form-check form-switch">
<InputCheckbox id="advisor" class="form-check-input" @bind-Value="AssignedRoles.Advisor"/>
<label for="advisor" class="form-check-label">Sælger</label>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<button type="button" class="btn btn-danger" @onclick="() => ReadOnly = !ReadOnly">Rediger</button>
</div>
<div class="col">
<button type="submit" class="btn btn-primary">Gem</button>
</div> </div>
<div class="col"> <div class="col">
<a class="btn btn-primary" href="/system/users">Tilbage</a> <a class="btn btn-primary" href="/system/users">Tilbage</a>
</div> </div>
<div class="col text-end">
<button type="submit" class="btn btn-primary" disabled="@ContextInvalid">Gem</button>
</div> </div>
</EditForm> </div>
<EditForm EditContext="PasswdContext" class="mt-5" > <div class="alert alert-info">
<DataAnnotationsValidator />
<h3>NULSTIL ADGANGSKODE</h3>
<div class="alert-info">
<h4>Password politik</h4> <h4>Password politik</h4>
<p>Mindst 10 tegn bestående af store og små bogstaver samt tal. Du kan teste pasword og danne stærke password på <a href="https://pw.nix.dk">pw.nix.dk</a></p> <p>Mindst 10 tegn bestående af store og små bogstaver samt tal. Du kan teste pasword og danne stærke password på <a href="https://pw.nix.dk">pw.nix.dk</a></p>
</div> </div>
<div class="row mb-3"> <div class="row mb-3">
<label for="newPasswd" class="col-md-2 col-form-label">Ny</label> <label for="newPasswd" class="col-md-2 col-form-label">Ny</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="newPasswd" class="form-control" @bind-Value="@Passwords.NewPassword"/> <InputText id="newPasswd" class="form-control" @bind-Value="@PasswdInput.NewPassword"/>
<ValidationMessage For="@(() => Passwords.NewPassword)"></ValidationMessage> <ValidationMessage For="@(() => PasswdInput.NewPassword)"></ValidationMessage>
</div> </div>
</div> </div>
<div class="row mb-3"> <div class="row mb-3">
<label for="verifyPasswd" class="col-md-2 col-form-label">Bekræft</label> <label for="verifyPasswd" class="col-md-2 col-form-label">Bekræft</label>
<div class="col-md-10"> <div class="col-md-10">
<InputText id="verifyPasswd" class="form-control" @bind-Value="@Passwords.ConfirmPassword"/> <InputText id="verifyPasswd" class="form-control" @bind-Value="@PasswdInput.ConfirmPassword"/>
<ValidationMessage For="@(() => Passwords.ConfirmPassword)"></ValidationMessage> <ValidationMessage For="@(() => PasswdInput.ConfirmPassword)"></ValidationMessage>
</div> </div>
</div> </div>
</EditForm> </EditForm>
}
</div> </div>
</div> </div>

View file

@ -21,6 +21,7 @@ using Microsoft.AspNetCore.Components.Forms;
using Wonky.Client.Helpers; using Wonky.Client.Helpers;
using Wonky.Client.HttpInterceptors; using Wonky.Client.HttpInterceptors;
using Wonky.Client.HttpRepository; using Wonky.Client.HttpRepository;
using Wonky.Client.Models;
using Wonky.Entity.DTO; using Wonky.Entity.DTO;
#pragma warning disable CS8618 #pragma warning disable CS8618
@ -33,13 +34,13 @@ public partial class SystemUserCreatePage : IDisposable
[Inject] public ISystemUserRepository UserRepo { get; set; } [Inject] public ISystemUserRepository UserRepo { get; set; }
[Inject] public ILogger<SystemUserCreatePage> Logger { get; set; } [Inject] public ILogger<SystemUserCreatePage> Logger { get; set; }
[Inject] public IToastService Toaster { get; set; } [Inject] public IToastService Toaster { get; set; }
private UserManagerEditView UserInfo { get; set; } = new(); private UserManagerCreate NewUserInfo { get; set; } = new();
private EditContext UserEditContext { get; set; } private EditContext NewUserContext { get; set; }
private ResetPasswordDto Passwords { get; set; } = new(); private bool ContextInvalid { get; set; } = true;
private EditContext PasswdContext { get; set; }
private bool PwInvalid { get; set; } = true;
private bool Working { get; set; } = true; private bool Working { get; set; } = true;
private bool ReadOnly { get; set; } = true; private bool ReadOnly { get; set; } = true;
private PasswordInput PasswdInput { get; set; } = new();
private AssignedRoles AssignedRoles { get; set; } = new();
private readonly JsonSerializerOptions _options = new JsonSerializerOptions private readonly JsonSerializerOptions _options = new JsonSerializerOptions
{ {
@ -51,48 +52,65 @@ public partial class SystemUserCreatePage : IDisposable
Interceptor.RegisterEvent(); Interceptor.RegisterEvent();
Interceptor.RegisterBeforeSendEvent(); Interceptor.RegisterBeforeSendEvent();
UserEditContext = new EditContext(UserInfo); NewUserContext = new EditContext(NewUserInfo);
PasswdContext = new EditContext(Passwords);
PasswdContext.OnFieldChanged += PwHandleFieldChanged!; NewUserContext.OnFieldChanged += ContextHandleFieldChanged!;
PasswdContext.OnValidationStateChanged += PwValidationChanged; NewUserContext.OnValidationStateChanged += ContextValidationChanged;
Working = false; Working = false;
} }
private async Task CreateUser() private async Task CreateUser()
{ {
// NewUserInfo.AssignedRoles = new List<UserRoleAssignment>()
// {
// new (){ Name = "Admin", Assigned = false },
// new (){ Name = "Advisor", Assigned = false },
// new (){ Name = "Management", Assigned = false },
// new (){ Name = "Office", Assigned = false },
// new (){ Name = "Supervisor", Assigned = false },
// new (){ Name = "Warehouse", Assigned = false },
// };
ReadOnly = true; ReadOnly = true;
Working = true; Working = true;
Toaster.ShowInfo("Sender data til server ..."); Toaster.ShowInfo("Sender data til server ...");
await UserRepo.CreateUser(UserInfo); await UserRepo.CreateUser(NewUserInfo);
Working = false; Working = false;
Toaster.ShowInfo("Bruger er oprettet ..."); Toaster.ShowInfo("Bruger er oprettet ...");
} }
private void PwHandleFieldChanged(object sender, FieldChangedEventArgs e) private void ContextHandleFieldChanged(object sender, FieldChangedEventArgs e)
{ {
PwInvalid = !PasswdContext.Validate(); Logger.LogDebug("contextHandleFieldChanged => e.FieldIdentifier.FieldName {}", e.FieldIdentifier.FieldName);
if (e.FieldIdentifier.FieldName == "NewPassword")
{
if (!Utils.IsValidPasswd(PasswdInput.NewPassword))
{
ContextInvalid = true;
return;
}
}
NewUserInfo.Passwd = PasswdInput.NewPassword;
ContextInvalid = !NewUserContext.Validate();
StateHasChanged(); StateHasChanged();
} }
private void PwValidationChanged(object? sender, ValidationStateChangedEventArgs e) private void ContextValidationChanged(object? sender, ValidationStateChangedEventArgs e)
{ {
PwInvalid = true; ContextInvalid = true;
if (!Utils.IsValidPasswd(Passwords.NewPassword))
return;
PasswdContext.OnFieldChanged -= PwHandleFieldChanged!; NewUserContext.OnFieldChanged -= ContextHandleFieldChanged!;
PasswdContext.OnValidationStateChanged -= PwValidationChanged; NewUserContext.OnValidationStateChanged -= ContextValidationChanged;
PasswdContext = new EditContext(Passwords); NewUserContext = new EditContext(NewUserInfo);
PasswdContext.OnFieldChanged += PwHandleFieldChanged!; NewUserContext.OnFieldChanged += ContextHandleFieldChanged!;
PasswdContext.OnValidationStateChanged += PwValidationChanged; NewUserContext.OnValidationStateChanged += ContextValidationChanged;
} }
public void Dispose() public void Dispose()
{ {
Interceptor.DisposeEvent(); Interceptor.DisposeEvent();
PasswdContext.OnFieldChanged -= PwHandleFieldChanged!; NewUserContext.OnFieldChanged -= ContextHandleFieldChanged!;
PasswdContext.OnValidationStateChanged -= PwValidationChanged; NewUserContext.OnValidationStateChanged -= ContextValidationChanged;
} }
} }

View file

@ -19,9 +19,18 @@
@attribute [Authorize(Roles = "Admin")] @attribute [Authorize(Roles = "Admin")]
@page "/system/users" @page "/system/users"
<PageTitle>Admin User List Page</PageTitle> <PageTitle>Administrativ - Bruger liste</PageTitle>
<div class="row">
<div class="col">
<h3>Administrativ - Bruger liste</h3>
</div>
<div class="col">
<div class="text-end">
<a class="btn btn-primary" href="/system/users/create"><i class="bi-plus-lg"></i>Opret bruger</a>
</div>
</div>
</div>
<h3>Admin User List Page</h3>
<div class="list-group"> <div class="list-group">
<div class="list-group-item"> <div class="list-group-item">

View file

@ -44,29 +44,29 @@ public partial class WarehouseOrderListPage : IDisposable
OrderList = await FetchOrders(Status); OrderList = await FetchOrders(Status);
} }
private async Task GetStatusCallback(PStatus status) private async Task GetStatusCallback(ProcessStatus status)
{ {
Working = true; Working = true;
OrderList = new List<WarehouseOrderView>(); OrderList = new List<WarehouseOrderView>();
switch (status) switch (status)
{ {
case PStatus.None or PStatus.Accepted: case ProcessStatus.None or ProcessStatus.Printed:
Header = "Ubehandlede ordrer"; Header = "Ubehandlede ordrer";
ReadyToShip = false; ReadyToShip = false;
break; break;
case PStatus.Picked: case ProcessStatus.Picked:
Header = "Plukkede ordrer"; Header = "Plukkede ordrer";
ReadyToShip = false; ReadyToShip = false;
break; break;
case PStatus.Packed: case ProcessStatus.Packed:
Header = "Pakkede ordrer"; Header = "Pakkede ordrer";
ReadyToShip = true; ReadyToShip = true;
break; break;
case PStatus.Shipped: case ProcessStatus.Shipped:
break; break;
case PStatus.All: case ProcessStatus.All:
break; break;
case PStatus.Express: case ProcessStatus.Express:
break; break;
default: default:
throw new ArgumentOutOfRangeException(nameof(status), status, null); throw new ArgumentOutOfRangeException(nameof(status), status, null);

View file

@ -9,7 +9,7 @@
"Logging": { "Logging": {
"LogLevel": { "LogLevel": {
"Default": "Debug", "Default": "Debug",
"System": "Information", "System": "Debug",
"Microsoft": "Information" "Microsoft": "Information"
}, },
"Debug": { "Debug": {

View file

@ -0,0 +1,24 @@
// Copyright (C) 2022 FCS Frede's Computer Services.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see [https://www.gnu.org/licenses/agpl-3.0.en.html]
//
namespace Wonky.Entity.DTO;
public class SubjectAssignment
{
public string Name { get; set; }
public string UserId { get; set; }
public bool Assigned { get; set; }
}

View file

@ -0,0 +1,39 @@
// Copyright (C) 2022 FCS Frede's Computer Services.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see [https://www.gnu.org/licenses/agpl-3.0.en.html]
//
using System.ComponentModel.DataAnnotations;
namespace Wonky.Entity.DTO;
public class UserManagerCreate
{
[MaxLength(128)] public string CompanyId { get; set; } = "";
[MaxLength(128)] public string ContactId { get; set; } = "";
[Required(ErrorMessage = "Landekode skal udfyldes")][MaxLength(2, ErrorMessage = "Landekode er 2 bogstaver")] public string CountryCode { get; set; } = "";
[MaxLength(128, ErrorMessage = "Kort beskrivelse på højst 128 tegn.")] public string Description { get; set; } = "";
[Required(ErrorMessage = "Email adresse skal udfyldes")][MaxLength(255, ErrorMessage = "Der er afsat 255 tegn til email adressen")] public string Email { get; set; } = "";
public bool EmailConfirmed { get; set; }
public bool EShop { get; set; }
[Required(ErrorMessage = "Fornavn skal udfyldes")][MaxLength(50, ErrorMessage = "Der er afsat 50 tegn til fornavn")] public string FirstName { get; set; } = "";
[Required(ErrorMessage = "Efternavn skal udfyldes")][MaxLength(50, ErrorMessage = "Der er afsat 50 tegn til efternavn")] public string LastName { get; set; } = "";
public bool LockoutEnabled { get; set; }
public string Passwd { get; set; } = "";
[Required(ErrorMessage = "Telefon nummer skal udfyldes")][MaxLength(20, ErrorMessage = "Der er afsat 20 tegn til telefon nummber")] public string PhoneNumber { get; set; } = "";
[Required(ErrorMessage = "Medarbejder ID skal udfyldes")][MaxLength(20, ErrorMessage = "Der er afsat 20 tegn til medarbejder ID")] public string SalesRep { get; set; } = "";
public string UserId { get; set; } = "";
public List<UserRoleAssignment> AssignedRoles { get; set; } = new();
public List<SubjectAssignment> AssignedSubjects { get; set; } = new();
}

View file

@ -22,17 +22,18 @@ public class UserManagerEditView
{ {
[MaxLength(128)] public string CompanyId { get; set; } = ""; [MaxLength(128)] public string CompanyId { get; set; } = "";
[MaxLength(128)] public string ContactId { get; set; } = ""; [MaxLength(128)] public string ContactId { get; set; } = "";
[Required][MaxLength(3)] public string CountryCode { get; set; } = ""; [Required(ErrorMessage = "Landekode skal udfyldes")][MaxLength(2, ErrorMessage = "Landekode er 2 bogstaver")] public string CountryCode { get; set; } = "";
[MaxLength(128)] public string Description { get; set; } = ""; [MaxLength(128, ErrorMessage = "Kort beskrivelse på højst 128 tegn.")] public string Description { get; set; } = "";
[Required][MaxLength(255)] public string Email { get; set; } = ""; [Required(ErrorMessage = "Email adresse skal udfyldes")][MaxLength(255, ErrorMessage = "Der er afsat 255 tegn til email adressen")] public string Email { get; set; } = "";
public bool EmailConfirmed { get; set; } public bool EmailConfirmed { get; set; }
public bool EShop { get; set; } public bool EShop { get; set; }
[Required][MaxLength(50)] public string FirstName { get; set; } = ""; [Required(ErrorMessage = "Fornavn skal udfyldes")][MaxLength(50, ErrorMessage = "Der er afsat 50 tegn til fornavn")] public string FirstName { get; set; } = "";
[Required][MaxLength(50)] public string LastName { get; set; } = ""; [Required(ErrorMessage = "Efternavn skal udfyldes")][MaxLength(50, ErrorMessage = "Der er afsat 50 tegn til efternavn")] public string LastName { get; set; } = "";
public bool LockoutEnabled { get; set; } public bool LockoutEnabled { get; set; }
public string Passwd { get; set; } = ""; public string Passwd { get; set; } = "";
[Required][MaxLength(20)] public string PhoneNumber { get; set; } = ""; [Required(ErrorMessage = "Telefon nummer skal udfyldes")][MaxLength(20, ErrorMessage = "Der er afsat 20 tegn til telefon nummber")] public string PhoneNumber { get; set; } = "";
[Required][MaxLength(20)] public string SalesRep { get; set; } = ""; [Required(ErrorMessage = "Medarbejder ID skal udfyldes")][MaxLength(20, ErrorMessage = "Der er afsat 20 tegn til medarbejder ID")] public string SalesRep { get; set; } = "";
public string UserId { get; set; } = ""; public string UserId { get; set; } = "";
public List<UserRoleAssignment> AssignedRoles { get; set; } = new(); public List<UserRoleAssignment> AssignedRoles { get; set; } = new();
public List<SubjectAssignment> AssignedSubjects { get; set; } = new();
} }