Files
SteUP_Dotnet/SteUp.Shared/Components/SingleElements/Modal/ModalFormScheda.razor

731 lines
25 KiB
Plaintext

@using System.Data.Common
@using Microsoft.Extensions.Logging
@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
@using SteUp.Shared.Core.Entities
@using SteUp.Shared.Core.Enum
@using SteUp.Shared.Core.Interface.IntegryApi
@using SteUp.Shared.Core.Interface.LocalDb
@using SteUp.Shared.Core.Interface.System
@using SteUp.Shared.Core.Messages.Scanner
@inject INetworkService NetworkService
@inject IDialogService Dialog
@inject IIntegryApiService IntegryApiService
@inject IFileManager FileManager
@inject IIspezioniService IspezioniService
@inject IIntegrySteupService IntegrySteupService
@inject OnScannerService OnScannerService
@inject ILogger<ModalFormScheda> Logger
<MudDialog Class="customDialog-form">
<DialogContent>
<MudForm ReadOnly="ReadOnly" @ref="_form">
<HeaderLayout Cancel="true" OnCancel="@Cancel" LabelSave="@LabelSave"
OnSave="Save" Title="Scheda"/>
<div class="content">
<CardFormModal Title="Reparto" Loading="SteupDataService.Reparti.IsNullOrEmpty()">
<MudSelectExtended T="JtbFasiDto?" Variant="Variant.Text"
@bind-Value="Scheda.Reparto" ToStringFunc="@(x => x?.Descrizione)"
@bind-Value:after="OnAfterChangeReparto" Required="true"
RequiredError="Reparto obbligatorio">
@foreach (var fasi in SteupDataService.Reparti)
{
<MudSelectItemExtended Class="custom-item-select" Value="@fasi">
@fasi.Descrizione
</MudSelectItemExtended>
}
</MudSelectExtended>
</CardFormModal>
<CardFormModal Title="Motivo" Loading="SteupDataService.TipiAttività.IsNullOrEmpty()">
<MudSelectExtended ReadOnly="@Scheda.CodJfas.IsNullOrEmpty()" T="string?"
Variant="Variant.Text"
@bind-Value="Scheda.ActivityTypeId" @bind-Value:after="OnAfterChangeValue"
Required="true" RequiredError="Motivo obbligatorio">
@foreach (var type in SteupDataService.TipiAttività.Where(x => x.CodJfas.EqualsIgnoreCase(Scheda.CodJfas!)))
{
<MudSelectItemExtended Class="custom-item-select"
Value="@type.ActivityTypeId">@type.ActivityTypeId</MudSelectItemExtended>
}
</MudSelectExtended>
</CardFormModal>
@if (FileLoading)
{
<CardFormModal Title="Immagini">
<div class="container-attached">
<MudProgressLinear Color="Color.Primary" Indeterminate="true" Class="my-3"/>
</div>
</CardFormModal>
}
else if (!AttachedList.IsNullOrEmpty())
{
<CardFormModal Title="Immagini">
<div class="container-attached">
<div class="scroll-attached">
@foreach (var item in AttachedList!.Select((p, index) => new { p, index }))
{
<MudCard>
@if (!item.p.ThumbPath.IsNullOrEmpty())
{
<MudCardMedia Image="@item.p.ThumbPath" Height="100"/>
}
<MudCardContent Class="image_card">
<MudText Typo="Typo.subtitle1"><b>@item.p.Name</b></MudText>
@if (item.p.ToUpload)
{
<MudIconButton Variant="Variant.Outlined"
Icon="@Icons.Material.Rounded.Close"
Size="Size.Small" Color="Color.Error"
OnClick="@(() => OnRemoveAttached(item.index))"/>
}
</MudCardContent>
</MudCard>
}
</div>
</div>
</CardFormModal>
}
@if (!ReadOnly)
{
<div class="container-button ripple-container">
<MudButton Class="button-settings green-icon"
FullWidth="true"
StartIcon="@Icons.Material.Rounded.AttachFile"
Size="Size.Medium"
OnClick="@OpenAddAttached"
Variant="Variant.Outlined">
Aggiungi foto
</MudButton>
</div>
}
<CardFormModal Title="Articoli">
<div class="input-manual-barcode">
<MudTextField FullWidth="true" T="string?" Variant="Variant.Text"
@bind-Value="ManualBarcode" Placeholder="Digita manualmente il codice"/>
<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 (NetworkService.ConnectionAvailable && !ReadOnly)
{
<div class="container-button ripple-container">
<MudButton Class="button-settings red-icon"
FullWidth="true"
StartIcon="@Icons.Material.Rounded.Balance"
Size="Size.Medium"
OnClick="@OpenSelectArt"
Variant="Variant.Outlined">
Consulta articoli
</MudButton>
</div>
}
<CardFormModal Title="Scadenza">
<MudSelectExtended FullWidth="true" T="int" Variant="Variant.Text"
@bind-Value="@Scheda.Scadenza" @bind-Value:after="OnAfterChangeValue">
<MudSelectItemExtended Class="custom-item-select" Text="24H" Value="3"/>
<MudSelectItemExtended Class="custom-item-select" Text="1 Settimana" Value="2"/>
<MudSelectItemExtended Class="custom-item-select" Text="1 Mese" Value="1"/>
<MudSelectItemExtended Class="custom-item-select" Text="2 Mesi" Value="0"/>
</MudSelectExtended>
</CardFormModal>
<CardFormModal Title="Responsabile">
<MudTextField FullWidth="true" T="string?" Variant="Variant.Text"
@bind-Value="Scheda.Responsabile" @bind-Value:after="OnAfterChangeValue"
DebounceInterval="500" OnDebounceIntervalElapsed="OnAfterChangeValue"/>
</CardFormModal>
<CardFormModal Title="Note">
<MudTextField T="string?" Variant="Variant.Text" Lines="3"
@bind-Value="Scheda.Note" @bind-Value:after="OnAfterChangeValue"
DebounceInterval="500" OnDebounceIntervalElapsed="OnAfterChangeValue"/>
</CardFormModal>
<div class="container-button">
<MudButton Class="button-settings primary-icon"
FullWidth="true"
StartIcon="@Icons.Material.Rounded.Description"
Size="Size.Medium"
OnClick="@SuggestActivityDescription"
Variant="Variant.Outlined">
Suggerisci note descrittive
</MudButton>
</div>
</div>
</MudForm>
<ConfirmUpdateActivity @ref="_confirmUpdateMessage"/>
</DialogContent>
</MudDialog>
<SpinnerOverlay VisibleOverlay="VisibleOverlay" SuccessAnimation="SuccessAnimation"/>
<LoaderLayout Visible="OnLoading" Text="@TextLoading"/>
@code {
[CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!;
[Parameter] public required string CodMdep { get; set; }
[Parameter] public required DateTime Data { get; set; }
[Parameter] public bool IsNew { get; set; }
[Parameter] public bool ReadOnly { get; set; }
[Parameter] public Scheda Scheda { get; set; } = new();
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; }
private ConfirmUpdateActivity _confirmUpdateMessage = null!;
private MudForm _form = null!;
private string? LabelSave { get; set; }
private bool IsDirty { get; set; }
private Scheda _originalScheda = null!;
private List<AttachedDto>? AttachedList { get; set; }
protected override void OnInitialized()
{
Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter;
OnScannerService.OnNewScanSuccessful += HandleNewScanSuccessful;
OnScannerService.OnErrorScan += OnErrorScan;
_originalScheda = Scheda.Clone();
Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter;
LoadAttached();
}
private void LoadAttached()
{
if (Scheda.ImageNames == null) return;
FileLoading = true;
StateHasChanged();
Task.Run(async () =>
{
var fileList = await FileManager.GetInspectionFiles(
new Ispezione
{
CodMdep = CodMdep,
Data = Data,
Rilevatore = UserSession.User.Username
},
Scheda.ImageNames
);
await InvokeAsync(() =>
{
AttachedList = fileList;
FileLoading = false;
StateHasChanged();
});
});
}
private async Task Save()
{
VisibleOverlay = true;
StateHasChanged();
SaveSchedaResponseDto? apiResponse = null;
try
{
if (NetworkService.ConnectionAvailable)
{
apiResponse = await IntegrySteupService.SaveScheda(
new SaveRequestDto
{
LocalIdScheda = Scheda.Id,
ActivityTypeId = Scheda.ActivityTypeId,
CodJfas = Scheda.CodJfas,
CodMdep = CodMdep,
DataCreazione = Data,
Note = Scheda.Note,
PersonaRif = Scheda.Responsabile,
Barcodes = Scheda.Articoli.ConvertAll(x => x.Barcode),
Scandeza = (ScadenzaEnum)Scheda.Scadenza,
ParentActivityId = IsNew ? null : Scheda.Ispezione?.ActivityId
}
);
}
}
catch (Exception e)
{
var message = e.Message;
Logger.LogError(e, message);
Console.WriteLine(message);
await Dialog.ShowError(message);
}
if (IsNew) await NewSave(apiResponse);
else await Update(apiResponse);
if (Scheda.ActivityId.IsValorized()) await UploadFile(Scheda);
await FileManager.CleanTempStorageAsync();
SuccessAnimation = true;
StateHasChanged();
await Task.Delay(1250);
DisposeMessage();
MudDialog.Close(Scheda);
}
private async Task NewSave(SaveSchedaResponseDto? apiResponse)
{
if (!AttachedList.IsNullOrEmpty())
{
foreach (var attached in AttachedList!)
{
var fileNameAdded = await FileManager.SaveInspectionFile(
new Ispezione
{
CodMdep = CodMdep,
Data = Data,
Rilevatore = UserSession.User.Username
},
attached.FileBytes!,
attached.Name!
);
Scheda.ImageNames ??= [];
if (fileNameAdded != null)
Scheda.ImageNames.Add(fileNameAdded);
}
}
if (apiResponse != null)
{
await IspezioniService.UpdateActivityIdIspezioneAsync(CodMdep, Data,
UserSession.User.Username, apiResponse.ActivityIdIspezione
);
Scheda.ActivityId = apiResponse.ActivityIdScheda;
}
await IspezioniService.AddSchedaAsync(CodMdep, Data, UserSession.User.Username, Scheda);
}
private async Task Update(SaveSchedaResponseDto? apiResponse)
{
if (!AttachedList.IsNullOrEmpty())
{
foreach (var attached in AttachedList!.Where(x => !x.SavedOnAppData))
{
var ispezione = new Ispezione
{
CodMdep = CodMdep,
Data = Data,
Rilevatore = UserSession.User.Username
};
if (!attached.ToRemove)
{
var fileNameAdded = await FileManager.SaveInspectionFile(
ispezione,
attached.FileBytes!,
attached.Name!
);
Scheda.ImageNames ??= [];
if (fileNameAdded != null)
Scheda.ImageNames.Add(fileNameAdded);
}
else
_ = FileManager.RemoveInspectionFile(ispezione, attached.Name!);
}
}
Scheda.ActivityId = apiResponse?.ActivityIdScheda;
await IspezioniService.UpdateSchedaAsync(Scheda);
if (apiResponse != null)
await IspezioniService.UpdateActivityIdIspezioneAsync(CodMdep, Data,
UserSession.User.Username, apiResponse.ActivityIdIspezione
);
}
private async Task UploadFile(Scheda scheda)
{
if (AttachedList.IsNullOrEmpty()) return;
var ispezione = new Ispezione
{
CodMdep = CodMdep,
Data = Data,
Rilevatore = UserSession.User.Username
};
foreach (var file in AttachedList!.Where(x => x.ToUpload))
{
if (file.FileBytes == null || file.Name == null) continue;
await IntegrySteupService.UploadFile(scheda.ActivityId!, file.FileBytes, file.Name);
var newPath = await FileManager.MoveInspectionFileFromToUploadToFinal(ispezione, file.Name);
if (newPath == null) continue;
var filePathToRemove = FileManager.GetFileToUploadDir(ispezione, file.Name);
scheda.ImageNames!.Remove(filePathToRemove);
scheda.ImageNames.Add(newPath);
}
await IspezioniService.UpdateFileListSchedaAsync(scheda.Id, scheda.ImageNames);
}
private async Task Cancel()
{
if (await CheckSavePreAction())
{
await FileManager.CleanTempStorageAsync();
DisposeMessage();
MudDialog.Cancel();
}
}
#region Form
private void OnAfterChangeReparto()
{
Scheda.ActivityTypeId = null;
OnAfterChangeValue();
}
private void OnAfterChangeValue()
{
RecalcDirty();
StateHasChanged();
}
private void RecalcDirty(bool forceTrue = false)
{
IsDirty = forceTrue || !ValueComparer.AreEqual(Scheda, _originalScheda);
if (IsDirty) LabelSave = !IsNew ? "Aggiorna" : "Salva";
else LabelSave = null;
}
private static class ValueComparer
{
public static bool AreEqual(Scheda? a, Scheda? b)
{
if (a is null || b is null) return a == b;
return
a.CodJfas == b.CodJfas &&
a.DescrizioneReparto == b.DescrizioneReparto &&
a.ActivityTypeId == b.ActivityTypeId &&
a.Note == b.Note &&
a.Responsabile == b.Responsabile &&
a.Scadenza == b.Scadenza;
}
}
private async Task<bool> CheckSavePreAction()
{
if (!IsDirty) return true;
var resul = await _confirmUpdateMessage.ShowAsync();
if (resul is not true) return true;
VisibleOverlay = true;
StateHasChanged();
await Submit();
VisibleOverlay = false;
StateHasChanged();
return false;
}
private async Task Submit()
{
await _form.Validate();
if (_form.IsValid) await Save();
}
#endregion
#region File
private async Task OpenAddAttached()
{
var result = await Dialog.OpenAddAttached();
if (result is not { Canceled: false, Data: List<AttachedDto> attachedList }) return;
OnLoading = true;
await InvokeAsync(StateHasChanged);
await Task.Yield();
// prepara placeholder in UI subito (così vedi le card con spinner)
AttachedList ??= [];
foreach (var a in attachedList)
AttachedList.Add(new AttachedDto { Name = a.Name, MimeType = a.MimeType, FileBytes = a.FileBytes });
await InvokeAsync(StateHasChanged);
RecalcDirty(true);
// Processa in background e aggiorna UI man mano (o a blocchi)
_ = Task.Run(async () =>
{
for (var i = 0; i < attachedList.Count; i++)
{
var a = attachedList[i];
if (a.FileBytes is null || a.Name is null) continue;
var (origUrl, thumbUrl) = await FileManager.SaveAndCreateThumbAsync(a.FileBytes, a.Name);
await InvokeAsync(() =>
{
var target = AttachedList![AttachedList.Count - attachedList.Count + i];
target.TempPath = origUrl;
target.ThumbPath = thumbUrl;
target.ToUpload = true;
StateHasChanged();
});
}
await InvokeAsync(() =>
{
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)
{
Console.WriteLine(e.Message);
await InvokeAsync(() =>
{
OnLoading = false;
TextLoading = null;
StateHasChanged();
OnError(e, e.Message);
});
return;
}
var modal = await Dialog.OpenSelectArt(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;
if (AttachedList[index].SavedOnAppData)
AttachedList[index].ToRemove = true;
AttachedList.RemoveAt(index);
RecalcDirty(true);
StateHasChanged();
}
#endregion
private void SuggestActivityDescription()
{
if (Scheda.ActivityTypeId == null)
{
Snackbar.Add("Indicare prima il motivo", Severity.Error);
return;
}
VisibleOverlay = true;
StateHasChanged();
_ = Task.Run(async () =>
{
var activityDescriptions = await IntegryApiService.SuggestActivityDescription(Scheda.ActivityTypeId);
var modal = await Dialog.OpenSuggestActivityDescription(activityDescriptions);
if (modal is { Canceled: false, Data: not null })
Scheda.Note = modal.Data!.ToString();
VisibleOverlay = false;
await InvokeAsync(StateHasChanged);
});
}
#region Scanner
private void OnManualBarcodeSet()
{
if (ManualBarcode == null) return;
HandleNewScanSuccessful(ManualBarcode);
ManualBarcode = null;
}
private async void HandleNewScanSuccessful(string? value)
{
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(null, "Nessun articolo trovato");
}
});
}
catch (Exception e)
{
Console.WriteLine(e.Message);
await InvokeAsync(() =>
{
OnLoading = false;
StateHasChanged();
});
OnError(e, e.Message);
}
}
private void OnErrorScan(string? value) => OnError(new Exception(value), value);
#endregion
private void OnError(Exception? e, string? errorMessage)
{
if (e != null) Logger.LogError(e, errorMessage);
if (errorMessage == null) return;
_ = Dialog.ShowError(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;
}
}