Implentata scansione barcode e lista articoli nel form scheda

This commit is contained in:
2026-02-26 15:46:49 +01:00
parent a5e27bcf06
commit e027d8e5cf
20 changed files with 770 additions and 19 deletions

View File

@@ -0,0 +1,23 @@
<MudDialog OnBackdropClick="Cancel">
<DialogContent>
<div class="exception-header mb-2">
<i class="ri-emotion-unhappy-line"></i>
<span>Ops</span>
</div>
<div class="text">
<code>@ErrorMessage</code>
</div>
</DialogContent>
<DialogActions>
<MudButton Variant="Variant.Text" OnClick="@Cancel" Size="Size.Small" Color="Color.Primary">
Chiudi
</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!;
[Parameter] public string ErrorMessage { get; set; } = string.Empty;
private void Cancel() => MudDialog.Cancel();
}

View File

@@ -0,0 +1,30 @@
.exception-header {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.exception-header > i {
font-size: 3rem;
line-height: normal;
color: var(--bs-danger);
}
.exception-header > span {
font-size: x-large;
font-weight: 700;
}
.text {
font-size: medium;
font-weight: 500;
display: flex;
text-align: center;
}
code {
width: 100%;
height: auto;
color: var(--bs-gray-dark);
}

View File

@@ -1,5 +1,6 @@
@using SteUp.Shared.Components.Layout
@using SteUp.Shared.Components.Layout.Overlay
@using SteUp.Shared.Components.Layout.Spinner
@using SteUp.Shared.Components.SingleElements.Card.ModalForm
@using SteUp.Shared.Components.SingleElements.MessageBox
@using SteUp.Shared.Core.Dto
@@ -14,6 +15,7 @@
@inject IIntegryApiService IntegryApiService
@inject IAttachedService AttachedService
@inject IIspezioniService IspezioniService
@inject IIntegrySteupService IntegrySteupService
@inject OnScannerService OnScannerService
<MudDialog Class="customDialog-form">
@@ -27,7 +29,7 @@
<CardFormModal Title="Reparto" Loading="SteupDataService.Reparti.IsNullOrEmpty()">
<MudSelectExtended ReadOnly="IsView" T="JtbFasiDto?" Variant="Variant.Text"
@bind-Value="Scheda.Reparto" ToStringFunc="@(x => x?.Descrizione)"
@bind-Value:after="OnAfterChangeValue" Required="true"
@bind-Value:after="OnAfterChangeReparto" Required="true"
RequiredError="Reparto obbligatorio">
@foreach (var fasi in SteupDataService.Reparti)
{
@@ -103,10 +105,29 @@
<div class="input-manual-barcode">
<MudTextField FullWidth="true" ReadOnly="IsView" T="string?" Variant="Variant.Text"
@bind-Value="ManualBarcode" Placeholder="Digita manualmente il codice"/>
<MudIconButton Color="Color.Primary" Size="Size.Small" Icon="@Icons.Material.Rounded.Send" />
<MudIconButton Color="Color.Primary" OnClick="@OnManualBarcodeSet"
Size="Size.Small" Icon="@Icons.Material.Rounded.Send"/>
</div>
@if (!Scheda.Articoli.IsNullOrEmpty())
{
<div class="art-list">
@foreach (var articolo in Scheda.Articoli)
{
<MudChip T="string" OnClose="@(() => RemoveArt(articolo.Barcode))" style="height: auto;">
<MudStack Direction="Column" Spacing="0" class="py-1">
<MudText Typo="Typo.subtitle2" Style="line-height: 1.1; font-weight: 700;">
@articolo.Descrizione
</MudText>
<MudText Typo="Typo.body2" Style="line-height: 1.5;">
Barcode: @articolo.Barcode
</MudText>
</MudStack>
</MudChip>
}
</div>
}
</CardFormModal>
@if (!IsView)
{
<div class="container-button ripple-container">
@@ -114,7 +135,7 @@
FullWidth="true"
StartIcon="@Icons.Material.Rounded.Balance"
Size="Size.Medium"
OnClick="@OpenAddAttached"
OnClick="@OpenSelectArt"
Variant="Variant.Outlined">
Consulta articoli
</MudButton>
@@ -161,6 +182,7 @@
</MudDialog>
<SpinnerOverlay VisibleOverlay="VisibleOverlay" SuccessAnimation="SuccessAnimation"/>
<LoaderLayout Visible="OnLoading" Text="@TextLoading"/>
@code {
[CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!;
@@ -171,11 +193,13 @@
private bool IsView => !NetworkService.ConnectionAvailable;
private string ManualBarcode { get; set; }
private string? ManualBarcode { get; set; }
//Overlay
private bool VisibleOverlay { get; set; }
private bool SuccessAnimation { get; set; }
private bool OnLoading { get; set; }
private string? TextLoading { get; set; }
private bool FileLoading { get; set; }
@@ -189,7 +213,9 @@
protected override void OnInitialized()
{
OnScannerService.OnNewScanSuccessful += OnNewScanSuccessful;
Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter;
OnScannerService.OnNewScanSuccessful += HandleNewScanSuccessful;
OnScannerService.OnErrorScan += OnErrorScan;
_originalScheda = Scheda.Clone();
@@ -238,6 +264,7 @@
StateHasChanged();
await Task.Delay(1250);
DisposeMessage();
MudDialog.Close(Scheda);
}
@@ -305,12 +332,19 @@
if (await CheckSavePreAction())
{
await AttachedService.CleanTempStorageAsync();
DisposeMessage();
MudDialog.Cancel();
}
}
#region Form
private void OnAfterChangeReparto()
{
Scheda.ActivityTypeId = null;
OnAfterChangeValue();
}
private void OnAfterChangeValue()
{
RecalcDirty();
@@ -374,7 +408,7 @@
var result = await ModalHelper.OpenAddAttached(Dialog);
if (result is not { Canceled: false, Data: List<AttachedDto> attachedList }) return;
VisibleOverlay = true;
OnLoading = true;
await InvokeAsync(StateHasChanged);
await Task.Yield();
@@ -408,12 +442,77 @@
await InvokeAsync(() =>
{
VisibleOverlay = false;
OnLoading = false;
StateHasChanged();
});
});
}
private void OpenSelectArt()
{
if (Scheda.CodJfas.IsNullOrEmpty())
{
Snackbar.Clear();
Snackbar.Add("Selezionare prima il reparto", Severity.Error);
return;
}
OnLoading = true;
TextLoading = "Download articoli in griglia";
StateHasChanged();
_ = Task.Run(async () =>
{
List<ArticoliInGrigliaDto>? articoli;
try
{
articoli = await IntegrySteupService.RetrieveGrigliaPlu(
new RetrieveGrigliaPluRequestDto
{
CodMdep = CodMdep,
ActivityTypeId = Scheda.ActivityTypeId,
CodJfas = Scheda.CodJfas
}
);
}
catch (Exception e)
{
await InvokeAsync(() =>
{
OnLoading = false;
TextLoading = null;
StateHasChanged();
OnError(e.Message);
});
return;
}
var modal = await ModalHelper.OpenSelectArt(Dialog, articoli);
await InvokeAsync(() =>
{
OnLoading = false;
TextLoading = null;
StateHasChanged();
});
if (modal is { Canceled: false, Data: List<ArticoliInGrigliaDto> articoliSelezionati })
{
Scheda.Articoli.AddRange(articoliSelezionati.ConvertAll(x => new SchedaArticolo
{
Barcode = x.Barcode,
Descrizione = x.Descrizione
})
);
await InvokeAsync(StateHasChanged);
}
});
}
private void OnRemoveAttached(int index)
{
if (AttachedList is null || index < 0 || index >= AttachedList.Count) return;
@@ -455,16 +554,89 @@
#region Scanner
public static void OnNewScanSuccessful(string? value)
private void OnManualBarcodeSet()
{
//To be implemented
if (ManualBarcode == null) return;
HandleNewScanSuccessful(ManualBarcode);
ManualBarcode = null;
}
private static void OnErrorScan(string? value)
private async void HandleNewScanSuccessful(string? value)
{
//To be implemented
try
{
if (value is null) return;
if (Scheda.Articoli.Any(x => x.Barcode.Equals(value))) return;
await InvokeAsync(() =>
{
OnLoading = true;
StateHasChanged();
});
var art = await IntegrySteupService.RetrieveArtFromBarcode(value);
await InvokeAsync(() =>
{
OnLoading = false;
StateHasChanged();
if (art != null)
{
RecalcDirty(true);
Scheda.Articoli.Add(new SchedaArticolo
{
Barcode = art.Barcode,
Descrizione = art.Descrizione
});
StateHasChanged();
}
else
{
OnError("Nessun articolo trovato");
}
});
}
catch (Exception e)
{
await InvokeAsync(() =>
{
OnLoading = false;
StateHasChanged();
});
OnError(e.Message);
}
}
private void OnErrorScan(string? value) => OnError(value);
#endregion
private void OnError(string? errorMessage)
{
if (errorMessage == null) return;
_ = ModalHelper.ShowError(Dialog, errorMessage);
}
private void RemoveArt(string barcode)
{
var index = Scheda.Articoli.FindIndex(x => x.Barcode.Equals(barcode));
if (index < 0) return;
RecalcDirty(true);
Scheda.Articoli.RemoveAt(index);
StateHasChanged();
}
private void DisposeMessage()
{
OnScannerService.OnNewScanSuccessful -= HandleNewScanSuccessful;
OnScannerService.OnErrorScan -= OnErrorScan;
}
}

View File

@@ -11,6 +11,8 @@
display: flex;
align-items: center;
gap: 1.5rem;
margin-top: .5rem;
margin-bottom: .5rem;
}
.scroll-attached {
@@ -35,4 +37,13 @@
.input-card {
margin-bottom: 1.5rem;
}
.art-list {
max-height: 150px;
overflow: auto;
}
.art-list ::deep p {
white-space: normal;
}

View File

@@ -0,0 +1,59 @@
@using SteUp.Shared.Components.Layout
@using SteUp.Shared.Core.Dto
<MudDialog OnBackdropClick="Cancel">
<DialogContent>
<HeaderLayout SmallHeader="true" Cancel="true" OnCancel="@(() => MudDialog.Cancel())"
Title="Seleziona articoli"/>
@if (!Articoli.IsNullOrEmpty())
{
<MudSelectExtended MultiSelection="true" ItemCollection="Articoli" SearchBox="true"
SearchBoxAutoFocus="true" @bind-SelectedValues="ArticoliSelected"
SearchFunc="SearchFunc" T="ArticoliInGrigliaDto" Virtualize="true"
Label="Articoli" AnchorOrigin="Origin.BottomCenter" Variant="Variant.Outlined"
SearchBoxPlaceholder="Descrizione articolo" SearchBoxClearable="true"
MultiSelectionTextFunc="MultiSelectionTextFunc" ToStringFunc="ToStringFunc"/>
}
else
{
<div class="spinner-container" style="height: unset !important; margin-bottom: 1rem;">
<MudIcon Size="Size.Large" Color="Color.Error" Icon="@Icons.Material.Rounded.Close"/>
<MudText>Nessun articolo trovato</MudText>
</div>
}
</DialogContent>
<DialogActions>
@if (!Articoli.IsNullOrEmpty())
{
<MudButton Variant="Variant.Filled" OnClick="@Submit" Size="Size.Small" Color="Color.Primary">
Seleziona
</MudButton>
}
</DialogActions>
</MudDialog>
@code {
[CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!;
[Parameter] public List<ArticoliInGrigliaDto>? Articoli { get; set; }
private IEnumerable<ArticoliInGrigliaDto>? ArticoliSelected { get; set; }
private void Cancel() => MudDialog.Cancel();
private void Submit() =>
MudDialog.Close(DialogResult.Ok(ArticoliSelected?.ToList()));
private static bool SearchFunc(ArticoliInGrigliaDto? obj, string? searchString)
{
if (searchString.IsNullOrEmpty() || obj == null) return true;
return obj.Descrizione.ContainsIgnoreCase(searchString!);
}
private static string? ToStringFunc(ArticoliInGrigliaDto? arg) => arg?.Descrizione;
private static string MultiSelectionTextFunc(List<ArticoliInGrigliaDto?> arg) =>
$"{arg.Count} selezionat{(arg.Count == 1 ? "o" : "i")}";
}