wip v0.24

This commit is contained in:
Frede Hundewadt 2022-09-28 19:18:20 +02:00
parent c7165892aa
commit dd9f9136d8
29 changed files with 575 additions and 291 deletions

View file

@ -16,7 +16,7 @@
*@ *@
@* <img class="state the-draw rounded-circle me-1" src="state.png" alt="state"/> *@ @* <img class="state the-draw rounded-circle me-1" src="state.png" alt="state"/> *@
<img class="img-fluid float-start border border-secondary rounded-circle state @StateClass me-1" src="state.png" alt="state"/> <img class="img-fluid float-start border border-secondary rounded-circle state @StateClass-bg me-1" src="state.png" alt="state"/>
@code{ @code{
[Parameter] public string StateClass { get; set; } = ""; [Parameter] public string StateClass { get; set; } = "";
} }

View file

@ -1,26 +0,0 @@
/* begin state elements */
.state {
width: 16px;
height: 16px;
min-width: 16px;
min-height: 16px;
}
.the-good {
background-color: green;
}
.the-bad {
background-color: orange;
}
.the-ugly {
background-color: #ff0000;
}
.the-draw {
background-color: #9b02fc;
}
.no-vat {
background-color: red;
}
.the-dead {
background-color: #0dcaf0;
}
/* end state elements */

View file

@ -46,15 +46,15 @@
<td class="align-middle text-end">@(activity.StatusTypeEnum == "Quote" ? $"{0:N2}" : $"{activity.OrderAmount:N2}")</td> <td class="align-middle text-end">@(activity.StatusTypeEnum == "Quote" ? $"{0:N2}" : $"{activity.OrderAmount:N2}")</td>
<td class="align-middle text-center"> <td class="align-middle text-center">
@if (activity.OurRef.Contains("T:")) @if (activity.OurRef.Contains("T:"))
{<i class="oi oi-phone"></i>} {<i class="bi-phone"></i>}
</td> </td>
<td class="align-middle text-center"> <td class="align-middle text-center">
@if (activity.Express) @if (activity.Express)
{<i class="oi oi-flash"></i>} {<i class="bi-lightning"></i>}
</td> </td>
<td class="align-middle text-end"> <td class="align-middle text-end">
@if (activity.StatusTypeEnum == "Quote") @if (activity.StatusTypeEnum == "Quote")
{<i class="oi oi-calculator"></i>} {<i class="bi-calculator"></i>}
</td> </td>
<td class="align-middle state"> <td class="align-middle state">
@if (activity.Lines.Any()) @if (activity.Lines.Any())

View file

@ -27,10 +27,10 @@ public partial class OfficeActivityTableComponent
{ {
return processStatus.ToLower() switch return processStatus.ToLower() switch
{ {
"none" => "the-good-fg", "none" => "the-good",
"picked" => "the-bad-fg", "picked" => "the-bad",
"packed" => "the-ugly-fg", "packed" => "the-ugly",
"shipped" => "the-dead-fg", "shipped" => "the-dead",
_ => "the-draw" _ => "the-draw"
}; };
} }

View file

@ -25,10 +25,10 @@
{ {
_icon = StateClass switch _icon = StateClass switch
{ {
"the-good-fg" => "file-earmark", "the-good" => "file-earmark",
"the-bad-fg" => "file-earmark-check", "the-bad" => "file-earmark-check",
"the-ugly-fg" => "box2-fill", "the-ugly" => "box2-fill",
"the-dead-fg" => "truck", "the-dead" => "truck",
_ => "question-square" _ => "question-square"
}; };
} }

View file

@ -1,12 +0,0 @@
.the-good-fg {
color: black;
}
.the-bad-fg {
color: black;
}
.the-ugly-fg {
color: black;
}
.the-draw-fg {
color: black;
}

View file

@ -28,14 +28,14 @@
{ {
<h3 class="text-center">TILBUD</h3> <h3 class="text-center">TILBUD</h3>
} }
@if (ReportItem.Express)
{
<h2 class="fw-bold text-center"><i class="bi-lightning-charge text-dark" style="font-size: 3rem;"></i> HASTER</h2>
}
@if (ReportItem.VisitTypeEnum.ToLower() == "phone" || ReportItem.OurRef.Contains("T:")) @if (ReportItem.VisitTypeEnum.ToLower() == "phone" || ReportItem.OurRef.Contains("T:"))
{ {
<h5 class="text-center">TELEFONORDRE</h5> <h5 class="text-center">TELEFONORDRE</h5>
} }
@if (ReportItem.Express)
{
<h2 class="fw-bold text-center">HASTER</h2>
}
</div> </div>
</th> </th>
</tr> </tr>
@ -115,6 +115,15 @@
<td>Ordresum</td> <td>Ordresum</td>
<td class="text-end">@ReportItem.OrderAmount</td> <td class="text-end">@ReportItem.OrderAmount</td>
</tr> </tr>
@if (ReportItem.Express)
{
<tr>
<td colspan="4"></td>
<td colspan="2">
<h5 class="fw-bold text-center"><i class="bi-lightning-charge text-dark" style="font-size: 1rem;"></i> HASTER</h5>
</td>
</tr>
}
</tbody> </tbody>
</table> </table>
@if (!string.IsNullOrWhiteSpace(@ReportItem.OfficeNote)) @if (!string.IsNullOrWhiteSpace(@ReportItem.OfficeNote))
@ -127,8 +136,18 @@
} }
</div> </div>
@if (ReportItem.Express && ReportItem.ProcessStatusEnum.ToLower() == "none")
{
<div class="report-item-express">
<i class="bi-lightning-charge text-dark" style="font-size: 11rem;"></i>
</div>
}
@code{ @code{
[Parameter] public ReportItemView ReportItem { get; set; } = new();
[Parameter]
public ReportItemView ReportItem { get; set; } = new();
protected override void OnParametersSet() protected override void OnParametersSet()
{ {

View file

@ -0,0 +1,6 @@
.report-item-express {
position: relative;
z-index: 999;
top: 10px;
right: 0;
}

View file

@ -20,12 +20,12 @@ namespace Wonky.Client.HttpInterfaces;
public interface ICrmActivityHttpRepository public interface ICrmActivityHttpRepository
{ {
Task<ReportItemView> GetReportItem(string id); Task<ReportItemView> GetReportItem(string salesHeadId);
Task<ActivityDto> GetActivity(string id); Task<ActivityDto> GetActivity(string salesHeadId);
Task<ApiResponseView> CreateActivity(ActivityDto model); Task<ApiResponseView> CreateActivity(ActivityDto model);
Task<ApiResponseView> AcceptOffer(string id); Task<ApiResponseView> AcceptOffer(string id);
Task<ReportStatusView> GetActivities(string activityDate); Task<ReportStatusView> GetActivities(string activityDate);
Task<List<ReportItemView>> GetCustomerActivities(string customerId); Task<List<ReportItemView>> GetCustomerActivities(string customerId);
Task UpdateOfficeNote(ActivityOfficeNote model); Task UpdateOfficeNote(ActivityOfficeNote model);
Task UpdateExpressStatus(string id); Task<ApiResponseView> UpdateExpressStatus(string id);
} }

View file

@ -0,0 +1,9 @@
using Wonky.Entity.DTO;
using Wonky.Entity.Views;
namespace Wonky.Client.HttpInterfaces;
public interface ISendMailService
{
Task<ApiResponseView> SendMail(EmailMessage message);
}

View file

@ -55,9 +55,15 @@ public class CrmActivityHttpRepository : ICrmActivityHttpRepository
_api = configuration.Value; _api = configuration.Value;
} }
public async Task UpdateExpressStatus(string id) public async Task<ApiResponseView> UpdateExpressStatus(string id)
{ {
await _client.PostAsync($"{_api.CrmSales}/express/{id}/?status=express", null); var response = await _client.PostAsync($"{_api.CrmSales}/express/{id}/?status=express", null);
var content = await response.Content.ReadAsStringAsync();
var result = JsonSerializer.Deserialize<ApiResponseView>(content, _options);
if (result.IsSuccess) return result!;
var msg = JsonSerializer.SerializeToElement(result.Message, _options);
result.Message = msg.ToString();
return result!;
} }
public async Task UpdateOfficeNote(ActivityOfficeNote model) public async Task UpdateOfficeNote(ActivityOfficeNote model)
@ -88,24 +94,24 @@ public class CrmActivityHttpRepository : ICrmActivityHttpRepository
{ {
var response = await _client.PostAsJsonAsync($"{_api.CrmSales}", model, _options); var response = await _client.PostAsJsonAsync($"{_api.CrmSales}", model, _options);
var content = await response.Content.ReadAsStringAsync(); var content = await response.Content.ReadAsStringAsync();
_logger.LogDebug("ActivityRepo => CreateActivity => ResponseContent <= {}", content);
var result = JsonSerializer.Deserialize<ApiResponseView>(content, _options); var result = JsonSerializer.Deserialize<ApiResponseView>(content, _options);
var msg = JsonSerializer.SerializeToElement(result.Message); if (result.IsSuccess) return result!;
_logger.LogDebug("Message content <= {}", msg); var msg = JsonSerializer.SerializeToElement(result.Message, _options);
result.Message = msg.ToString();
return result!; return result!;
} }
public async Task<ReportItemView> GetReportItem(string id) public async Task<ReportItemView> GetReportItem(string salesHeadId)
{ {
var salesItem = await _client var salesItem = await _client
.GetFromJsonAsync<ReportItemView>($"{_api.CrmSales}/{id}"); .GetFromJsonAsync<ReportItemView>($"{_api.CrmSales}/{salesHeadId}");
return salesItem ?? new ReportItemView(); return salesItem ?? new ReportItemView();
} }
public async Task<ActivityDto> GetActivity(string id) public async Task<ActivityDto> GetActivity(string salesHeadId)
{ {
var salesItem = await _client var salesItem = await _client
.GetFromJsonAsync<ActivityDto>($"{_api.CrmSales}/{id}"); .GetFromJsonAsync<ActivityDto>($"{_api.CrmSales}/{salesHeadId}");
return salesItem ?? new ActivityDto(); return salesItem ?? new ActivityDto();
} }

View file

@ -0,0 +1,40 @@
using System.Net.Http.Json;
using System.Text.Json;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Options;
using Wonky.Client.HttpInterfaces;
using Wonky.Entity.Configuration;
using Wonky.Entity.DTO;
using Wonky.Entity.Views;
namespace Wonky.Client.HttpRepository;
public class SendMailService : ISendMailService
{
private readonly JsonSerializerOptions? _options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
private readonly NavigationManager _navigation;
private ILogger<SendMailService> _logger;
private readonly HttpClient _client;
private readonly ApiConfig _api;
public SendMailService(HttpClient client, ILogger<SendMailService> logger,
NavigationManager navigation, IOptions<ApiConfig> configuration)
{
_client = client;
_logger = logger;
_navigation = navigation;
_api = configuration.Value;
}
public async Task<ApiResponseView> SendMail(EmailMessage message)
{
var response = await _client.PostAsJsonAsync("", message, _options);
var content = await response.Content.ReadAsStringAsync();
_logger.LogDebug("SendMail => ResponseContent <= {}", content);
var result = JsonSerializer.Deserialize<ApiResponseView>(content, _options);
return result!;
}
}

View file

@ -22,33 +22,11 @@
} }
@if (ReportItem.Express) @if (ReportItem.Express)
{ {
<h2 class="fw-bold text-center"><i class="bi-lightning-charge text-dark" style="font-size: 3rem;"></i> HASTER</h2> <h2 class="fw-bold text-center"><i class="bi-lightning-charge text-dark" style="font-size: 2rem;"></i> HASTER</h2>
} }
</div> </div>
</th> </th>
</tr> </tr>
@if (ReportItem.Express && ReportItem.ProcessStatusEnum.ToLower() == "none")
{
<div id="watermark">
<i class="bi-lightning-charge text-dark" style="font-size: 11rem;"></i>
</div>
<AuthorizeView Roles="Admin,Office,Advisor,Warehouse">
<Authorized>
<tr class="d-print-none">
<th colspan="3"></th>
<th>
<button type="button" class="btn btn-warning">PRINT</button>
</th>
</tr>
</Authorized>
</AuthorizeView>
}
else
{
<div id="watermark">
<i class="bi-check text-dark" style="font-size: 12rem;"></i>
</div>
}
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
@ -123,6 +101,13 @@
<td>Ordresum</td> <td>Ordresum</td>
<td class="text-end">@ReportItem.OrderAmount</td> <td class="text-end">@ReportItem.OrderAmount</td>
</tr> </tr>
@if (ReportItem.Express)
{
<tr>
<td colspan="4"></td>
<td colspan="2"><h5 class="fw-bold text-center"><i class="bi-lightning-charge text-dark" style="font-size: 2rem;"></i> HASTER</h5></td>
</tr>
}
</tbody> </tbody>
</table> </table>
@* Office Note *@ @* Office Note *@
@ -152,3 +137,7 @@ else
</div> </div>
} }
} }
<div id="watermark">
<i class="bi-lightning-charge text-dark" style="font-size: 11rem;"></i>
</div>

View file

@ -37,11 +37,6 @@ public partial class CrmViewActivityPage : IDisposable
_logger.LogDebug("ReportItem => \n {}", JsonSerializer.Serialize(ReportItem)); _logger.LogDebug("ReportItem => \n {}", JsonSerializer.Serialize(ReportItem));
} }
private async Task SetExpressState()
{
}
private void HandleFieldChanged(object sender, FieldChangedEventArgs e) private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
{ {
StateHasChanged(); StateHasChanged();

View file

@ -1,6 +1,6 @@
#watermark { #watermark {
position: fixed; position: fixed;
z-index: 999; z-index: 999;
top: 30px; top: 10px;
right: 0; right: 0;
} }

View file

@ -0,0 +1,133 @@
@using Microsoft.AspNetCore.Authorization
@using Wonky.Client.Components
@attribute [Authorize(Roles = "Admin,Office,Warehouse,Advisor")]
@page "/office/customers/{CompanyId}/orders/{OrderId}"
<table class="table table-sm table-striped d-print-table">
<thead>
<tr>
<th class="p-0" colspan="4">
<div class="bg-light text-dark border border-1 rounded-3 pt-3 mb-2">
<h2 class="fw-bold text-center">@ReportItem.Company.Name</h2>
@if (ReportItem.StatusTypeEnum.ToLower() is "quote")
{
<h3 class="text-center">TILBUD</h3>
}
@if (ReportItem.Express)
{
<h2 class="fw-bold text-center"><i class="bi-lightning-charge text-dark" style="font-size: 2rem;"></i> HASTER</h2>
}
@if (ReportItem.Express && ReportItem.ProcessStatusEnum == "None")
{
<div id="watermark">
<i class="bi-lightning-charge text-dark" style="font-size: 11rem;"></i>
</div>
<AuthorizeView Roles="Admin,Office,Warehouse">
<Authorized>
<div class="d-print-none">
<div class="row">
<div class="col-md-6">
<button type="button" class="btn btn-warning d-block" onclick="window.print();">PRINT</button>
</div>
<div class="col-md-6">
<button type="button" class="btn btn-warning d-block" @onclick="SetExpressState" >SÆT I GANG</button>
</div>
</div>
</div>
</Authorized>
</AuthorizeView>
}
@if (ReportItem.VisitTypeEnum.ToLower() == "phone" || ReportItem.OurRef.Contains("T:"))
{
<h5 class="text-center">TELEFONORDRE</h5>
}
</div>
</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Dato</th>
<td>@ReportItem.OrderDate</td>
<th scope="row">Konto</th>
<td>@ReportItem.Company.Account</td>
</tr>
<tr>
<th scope="col">Telefon</th>
<td>@ReportItem.Company.Phone</td>
<th scope="col">Køber</th>
<td>@ReportItem.YourRef</td>
</tr>
<tr>
<th scope="col">CVR/VAT</th>
<td>@ReportItem.Company.VatNumber</td>
<th scope="col">Rekvisition</th>
<td>@ReportItem.ReferenceNumber</td>
</tr>
<tr>
<th scope="col">Navn</th>
<td>@ReportItem.Company.Name</td>
<th scope="col">Lev.Navn</th>
<td>@ReportItem.DlvName</td>
</tr>
<tr>
<th scope="col">Adresse</th>
<td>@ReportItem.Company.Address1</td>
<th scope="col">Lev.Adresse</th>
<td>@ReportItem.DlvAddress1</td>
</tr>
<tr>
<th scope="col">Adresse</th>
<td>@ReportItem.Company.Address2</td>
<th scope="col">Lev.Adresse</th>
<td>@ReportItem.DlvAddress2</td>
</tr>
<tr>
<th scope="col">Postnr By</th>
<td>@ReportItem.Company.ZipCode @ReportItem.Company.City</td>
<th scope="col">Lev.Postnr By</th>
<td>@ReportItem.DlvZipCity</td>
</tr>
</tbody>
</table>
<table class="table table-sm table-striped table-bordered">
<thead>
<tr class="bg-light text-black">
<th scope="col">Antal</th>
<th scope="col">Varnr</th>
<th scope="col">Beskrivelse</th>
<th class="text-end" scope="col">Pris</th>
<th class="text-end" scope="col">R%</th>
<th class="text-end" scope="col">Beløb</th>
</tr>
</thead>
<tbody>
@foreach (var line in ReportItem.Lines)
{
<tr>
<td>@line.Quantity</td>
<td>@line.Sku</td>
<td>@line.Description</td>
<td class="text-end">@($"{line.Price:N2}")</td>
<td class="text-end">@($"{line.Discount:N2}")</td>
<td class="text-end">@($"{line.LineSum:N2}")</td>
</tr>
}
<tr>
<td colspan="4"></td>
<td>Ordresum</td>
<td class="text-end">@ReportItem.OrderAmount</td>
</tr>
@if (ReportItem.Express)
{
<td colspan="4"></td>
<td colspan="2"><h5 class="fw-bold text-center"><i class="bi-lightning-charge text-dark" style="font-size: 2rem;"></i> HASTER</h5></td>
}
</tbody>
</table>
@if (!string.IsNullOrWhiteSpace(ReportItem.OfficeNote))
{
<div class="alert alert-dark">
<h4 class="text-center">@ReportItem.OfficeNote</h4>
</div>
}

View file

@ -0,0 +1,86 @@
using System.Text;
using System.Text.Json;
using Blazored.Toast.Services;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Wonky.Client.HttpInterceptors;
using Wonky.Client.HttpInterfaces;
using Wonky.Client.HttpRepository;
using Wonky.Entity.DTO;
using Wonky.Entity.Views;
using System.Security.Claims;
using Blazored.LocalStorage;
using Wonky.Client.Services;
namespace Wonky.Client.Pages;
public partial class OfficeViewActivityPage : IDisposable
{
[Parameter] public string CompanyId { get; set; } = "";
[Parameter] public string OrderId { get; set; } = "";
[Inject] public HttpInterceptorService Interceptor { get; set; }
[Inject] public ICrmActivityHttpRepository ActivityRepo { get; set; }
[Inject] public ISendMailService SendMailService { get; set; }
[Inject] public ILocalStorageService Storage { get; set; }
[Inject] public IOfficeUserHttpRepository UserRepo { get; set; }
[Inject] public ILogger<OfficeViewActivityPage> Logger { get; set; }
[Inject] public IToastService Toast { get; set; }
private ReportItemView ReportItem { get; set; } = new();
protected override async Task OnInitializedAsync()
{
Interceptor.RegisterEvent();
Interceptor.RegisterBeforeSendEvent();
ReportItem = await ActivityRepo.GetReportItem(OrderId);
Logger.LogDebug("ReportItem => \n {}", JsonSerializer.Serialize(ReportItem));
}
private async Task SetExpressState()
{
var responseView = await ActivityRepo.UpdateExpressStatus(ReportItem.ActivityId);
if (responseView.IsSuccess)
{
var user = await Storage.GetItemAsync<UserInfoView>("_ux");
var salesRep = await UserRepo.GetAdvisorInfo(responseView.Id);
var body = new StringBuilder();
body.AppendLine($"Kvittering for behandling af hasteordre {ReportItem.ESalesNumber}");
body.AppendLine($"Konto : {ReportItem.Company.Account}");
body.AppendLine($"Navn : {ReportItem.Company.Name}");
body.AppendLine(
$"Post Bynavn: {salesRep.CountryCode.ToLower()}-{ReportItem.Company.ZipCode} {ReportItem.Company.City}");
body.AppendLine();
body.AppendLine("Med venlig hilsen");
body.AppendLine($"{user.FullName}");
body.AppendLine($"{user.PhoneNumber}");
var msg = new EmailMessage
{
Body = body.ToString(),
Subject = $"{ReportItem.ESalesNumber} {ReportItem.Company.Name} er registreret.",
To = { new EmailContact
{
Email = salesRep.Email,
Name = $"{salesRep.FirstName} {salesRep.LastName}"
}}
};
var sendMail = await SendMailService.SendMail(msg);
if (sendMail.IsSuccess)
{
Toast.ShowSuccess($"Status er opdateret og notifikation sendt til {salesRep.FirstName}.", "OK");
}
else
{
Toast.ShowWarning($"Notifikation til {salesRep.FirstName} kunne ikke sendes. {sendMail.Message}", "ADVARSEL");
}
return;
}
Toast.ShowError($"{responseView.Message}", "FEJL");
}
public void Dispose()
{
Interceptor.DisposeEvent();
}
}

View file

@ -0,0 +1,6 @@
#watermark {
position: fixed;
z-index: 999;
top: 10px;
right: 0;
}

View file

@ -61,7 +61,7 @@
@if (_items.Any()) @if (_items.Any())
{ {
@foreach (var item in _items.Where(item => item.StatusTypeEnum.ToLower() != "offer" || item.ProcessStatusEnum.ToLower() == "none")) @foreach (var item in _items.Where(item => item.StatusTypeEnum != "Offer" && item.ProcessStatusEnum == "None"))
{ {
<ReportItemComponent ReportItem="@item"></ReportItemComponent> <ReportItemComponent ReportItem="@item"></ReportItemComponent>
} }

View file

@ -1,6 +0,0 @@
@using Microsoft.AspNetCore.Authorization
@using Wonky.Client.Components
@attribute [Authorize(Roles = "Admin,Office,Warehouse,Advisor")]
@page "/office/customers/{CompanyId}/orders/{OrderId}"
<ReportItemComponent ReportItem="@_item" />

View file

@ -1,25 +0,0 @@
using System.Text.Json;
using Microsoft.AspNetCore.Components;
using Wonky.Client.HttpInterceptors;
using Wonky.Client.HttpInterfaces;
using Wonky.Client.HttpRepository;
using Wonky.Entity.DTO;
using Wonky.Entity.Views;
namespace Wonky.Client.Pages;
public partial class OfficeViewSalesActivityPage
{
[Parameter] public string CompanyId { get; set; } = "";
[Parameter] public string OrderId { get; set; } = "";
[Inject] public HttpInterceptorService _interceptor { get; set; }
[Inject] public ICrmActivityHttpRepository CrmActivityRepo { get; set; }
private ReportItemView _item { get; set; } = new();
protected override async Task OnParametersSetAsync()
{
_interceptor.RegisterEvent();
_interceptor.RegisterBeforeSendEvent();
_item = await CrmActivityRepo.GetReportItem(OrderId);
}
}

View file

@ -67,6 +67,7 @@ builder.Services.AddScoped<IOfficeReportHttpRepository, OfficeReportHttpReposito
builder.Services.AddScoped<IOfficeCustomerHttpRepository, OfficeCustomerHttpRepository>(); builder.Services.AddScoped<IOfficeCustomerHttpRepository, OfficeCustomerHttpRepository>();
builder.Services.AddScoped<IWarehouseHttpRepository, WarehouseHttpRepository>(); builder.Services.AddScoped<IWarehouseHttpRepository, WarehouseHttpRepository>();
builder.Services.AddScoped<ISendMailService, SendMailService>();
builder.Services.AddScoped<HttpInterceptorService>(); builder.Services.AddScoped<HttpInterceptorService>();
builder.Services.AddBlazoredLocalStorage(); builder.Services.AddBlazoredLocalStorage();

View file

@ -1,13 +1,13 @@
{ {
"appInfo": { "appInfo": {
"name": "Wonky Client", "name": "Wonky Client",
"version": "0.23.1", "version": "0.24.1",
"rc": true, "rc": false,
"sandBox": true, "sandBox": false,
"image": "grumpy-coder.png" "image": "grumpy-coder.png"
}, },
"apiConfig": { "apiConfig": {
"innoBaseUrl": "https://dev.innotec.dk", "innoBaseUrl": "https://eta.innotec.dk",
"glsTrackUrl": "https://www.gls-group.eu/276-I-PORTAL-WEB/content/GLS/DK01/DA/5004.htm?txtAction=71000&txtRefNo=", "glsTrackUrl": "https://www.gls-group.eu/276-I-PORTAL-WEB/content/GLS/DK01/DA/5004.htm?txtAction=71000&txtRefNo=",
"glsId": "", "glsId": "",
"serviceVirk": "api/v2/services/virk", "serviceVirk": "api/v2/services/virk",
@ -29,7 +29,8 @@
"officeUserPasswd": "api/v2/office/users/passwd", "officeUserPasswd": "api/v2/office/users/passwd",
"officeCustomers": "api/v2/office/customers", "officeCustomers": "api/v2/office/customers",
"officeReports": "api/v2/office/reports", "officeReports": "api/v2/office/reports",
"warehouse": "api/v2/warehouse/packages" "warehouse": "api/v2/warehouse/packages",
"sendMail": "api/v2/"
}, },
"Logging": { "Logging": {
"LogLevel": { "LogLevel": {

View file

@ -1,154 +0,0 @@
@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');
body {
font-size: 16px;
}
.draft-expires-msg {
font-size: 0.8em;
}
.spinner {
height: 48px;
}
.workDate {
font-variant: small-caps;
}
.btn.btn-edit {
color: #030303;
background-color: #a2a2ec;
}
html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
h1:focus {
outline: none;
}
a, .btn-link {
color: #0071c1;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.text-inno {
color: #ffaa00;
}
.content {
padding-top: 1.1rem;
}
.valid.modified:not([type=checkbox]) {
outline: 1px solid #26b050;
}
.invalid {
outline: 1px solid red;
}
.validation-message {
color: red;
}
#blazor-error-ui {
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}
.blazor-error-boundary {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
padding: 1rem 1rem 1rem 3.7rem;
color: white;
}
.blazor-error-boundary::after {
content: "An error has occurred."
}
.footer {
border-top: 2px solid orange;
position: relative;
margin-top: -150px;
height: 150px;
width: 100%;
clear: both;
padding-top: 20px;
}
footer.version {
display: block;
font-weight: bold;
padding: 0 10px 0 0;
}
.back-to-top {
position: fixed;
bottom: 25px;
right: 25px;
display: none;
}
/* printer classes */
@media print {
@page {
size: a4;
}
@font-face {
font-family: 'Montserrat';
src: url('https://fonts.googleapis.com/css2?family=Montserrat&display=swap');
}
html, body {
height: 290mm;
width: 210mm;
-webkit-print-color-adjust: exact;
color-adjust: exact;
}
.report-main {
font-size: 10px;
break-after: page;
height: initial;
border: initial;
border-radius: initial;
box-shadow: initial;
margin: 0;
width: initial;
-webkit-print-color-adjust: exact;
color-adjust: exact;
}
.report-visit {
break-before: page;
break-inside: avoid-page;
height: initial;
border: initial;
border-radius: initial;
box-shadow: initial;
margin: 0;
width: initial;
-webkit-print-color-adjust: exact;
color-adjust: exact;
}
.distance-ledger {}
.report-ledger {
page-break-inside: avoid;
}
}

View file

@ -0,0 +1,196 @@
@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');
body {
font-size: 16px;
}
.draft-expires-msg {
font-size: 0.8em;
}
.spinner {
height: 48px;
}
.workDate {
font-variant: small-caps;
}
.btn.btn-edit {
color: #030303;
background-color: #a2a2ec;
}
html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
h1:focus {
outline: none;
}
a, .btn-link {
color: #0071c1;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.text-inno {
color: #ffaa00;
}
.content {
padding-top: 1.1rem;
}
.valid.modified:not([type=checkbox]) {
outline: 1px solid #26b050;
}
.invalid {
outline: 1px solid red;
}
.validation-message {
color: red;
}
/* begin state elements */
.state {
width: 16px;
height: 16px;
min-width: 16px;
min-height: 16px;
}
.the-good-bg {
background-color: green;
}
.the-bad-bg {
background-color: orange;
}
.the-ugly-bg {
background-color: #ff0000;
}
.the-draw-bg {
background-color: #9b02fc;
}
.no-vat-bg {
background-color: red;
}
.the-dead-bg {
background-color: #0dcaf0;
}
.the-good {
color: green;
}
.the-bad {
color: orange;
}
.the-ugly {
color: #ff0000;
}
.the-dead {
color: #0dcaf0;
}
/* end state elements */
/*#blazor-error-ui {*/
/* background: lightyellow;*/
/* bottom: 0;*/
/* box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);*/
/* display: none;*/
/* left: 0;*/
/* padding: 0.6rem 1.25rem 0.7rem 1.25rem;*/
/* position: fixed;*/
/* width: 100%;*/
/* z-index: 1000;*/
/*}*/
/* #blazor-error-ui .dismiss {*/
/* cursor: pointer;*/
/* position: absolute;*/
/* right: 0.75rem;*/
/* top: 0.5rem;*/
/* }*/
/*.blazor-error-boundary {*/
/* background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;*/
/* padding: 1rem 1rem 1rem 3.7rem;*/
/* color: white;*/
/*}*/
/* .blazor-error-boundary::after {*/
/* content: "An error has occurred."*/
/* }*/
.footer {
border-top: 2px solid orange;
position: relative;
margin-top: -150px;
height: 150px;
width: 100%;
clear: both;
padding-top: 20px;
}
footer.version {
display: block;
font-weight: bold;
padding: 0 10px 0 0;
}
.back-to-top {
position: fixed;
bottom: 25px;
right: 25px;
display: none;
}
/* printer classes */
@media print {
@page {
size: a4;
}
@font-face {
font-family: 'Montserrat';
src: url('https://fonts.googleapis.com/css2?family=Montserrat&display=swap');
}
html, body {
height: 290mm;
width: 210mm;
-webkit-print-color-adjust: exact;
color-adjust: exact;
}
.report-main {
font-size: 10px;
break-after: page;
height: initial;
border: initial;
border-radius: initial;
box-shadow: initial;
margin: 0;
width: initial;
-webkit-print-color-adjust: exact;
color-adjust: exact;
}
.report-visit {
break-before: page;
break-inside: avoid-page;
height: initial;
border: initial;
border-radius: initial;
box-shadow: initial;
margin: 0;
width: initial;
-webkit-print-color-adjust: exact;
color-adjust: exact;
}
.distance-ledger {}
.report-ledger {
page-break-inside: avoid;
}
}

View file

@ -18,7 +18,7 @@
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet" /> <link href="bootstrap/css/bootstrap.min.css" rel="stylesheet" />
<link href="bootstrap/css/bootstrap-icons.css" rel="stylesheet" /> <link href="bootstrap/css/bootstrap-icons.css" rel="stylesheet" />
<link href="flag-icons/flag-icons.css" rel="stylesheet" /> <link href="flag-icons/flag-icons.css" rel="stylesheet" />
<link href="css/app-v0.22.0.css" rel="stylesheet" /> <link href="css/app-v0.23.0.css" rel="stylesheet" />
<link href="Wonky.Client.styles.css" rel="stylesheet" /> <link href="Wonky.Client.styles.css" rel="stylesheet" />
<link href="_content/Blazored.Toast/blazored-toast.min.css" rel="stylesheet" /> <link href="_content/Blazored.Toast/blazored-toast.min.css" rel="stylesheet" />
</head> </head>
@ -30,7 +30,7 @@
</div> </div>
</div> </div>
<script src="/bootstrap/js/bootstrap.bundle.js"></script> <script src="/bootstrap/js/bootstrap.bundle.js"></script>
<script src="/scripts/bsTooltip.js"></script> <!--<script src="/scripts/bsTooltip.js"></script>-->
<script src="_framework/blazor.webassembly.js"></script> <script src="_framework/blazor.webassembly.js"></script>
</body> </body>
</html> </html>

View file

@ -131,7 +131,10 @@ public class ApiConfig
/// </summary> /// </summary>
public string Warehouse { get; set; } = ""; public string Warehouse { get; set; } = "";
/// <summary>
/// url for sending mail message
/// </summary>
public string SendMail { get; set; } = "";
// /// <summary> // /// <summary>
// /// Application uri for reading salesReports // /// Application uri for reading salesReports
// /// </summary> // /// </summary>

View file

@ -0,0 +1,15 @@
namespace Wonky.Entity.DTO;
public class EmailMessage
{
public List<EmailContact> To { get; set; } = new();
public string Subject { get; set; } = "";
public string Body { get; set; } = "";
public bool IsBodyHtml { get; set; }
}
public class EmailContact
{
public string Name { get; set; } = "";
public string Email { get; set; } = "";
}

View file

@ -25,6 +25,8 @@ public class ReportItemView
/// Lines /// Lines
/// </summary> /// </summary>
public List<ReportItemLine> Lines { get; set; } = new(); public List<ReportItemLine> Lines { get; set; } = new();
public string SalesRepId { get; set; }
/// <summary> /// <summary>
/// Activity entity id /// Activity entity id
/// </summary> /// </summary>