Files
TaskHybrid/salesbook.Shared/Components/SingleElements/Modal/ActivityForm.razor
2025-09-09 11:43:07 +02:00

612 lines
22 KiB
Plaintext

@using System.Globalization
@using System.Text.RegularExpressions
@using CommunityToolkit.Mvvm.Messaging
@using salesbook.Shared.Components.Layout
@using salesbook.Shared.Components.Layout.Overlay
@using salesbook.Shared.Components.SingleElements.BottomSheet
@using salesbook.Shared.Core.Dto
@using salesbook.Shared.Core.Dto.Activity
@using salesbook.Shared.Core.Dto.Contact
@using salesbook.Shared.Core.Entity
@using salesbook.Shared.Core.Interface
@using salesbook.Shared.Core.Interface.IntegryApi
@using salesbook.Shared.Core.Interface.System.Network
@using salesbook.Shared.Core.Messages.Activity.Copy
@inject IManageDataService ManageData
@inject INetworkService NetworkService
@inject IIntegryApiService IntegryApiService
@inject IMessenger Messenger
@inject IDialogService Dialog
@inject IAttachedService AttachedService
<MudDialog Class="customDialog-form">
<DialogContent>
<HeaderLayout ShowProfile="false" Cancel="true" OnCancel="() => MudDialog.Cancel()" LabelSave="@LabelSave" OnSave="Save" Title="@(IsNew ? "Nuova" : $"{ActivityModel.ActivityId}")"/>
<div class="content">
<div class="input-card">
<MudTextField ReadOnly="IsView" T="string?" Placeholder="Descrizione" Variant="Variant.Text" Lines="3" @bind-Value="ActivityModel.ActivityDescription" @bind-Value:after="OnAfterChangeValue" DebounceInterval="500" OnDebounceIntervalElapsed="OnAfterChangeValue"/>
</div>
<div class="input-card">
<div class="form-container">
<MudAutocomplete ReadOnly="IsView" T="string?" Placeholder="Cliente"
SearchFunc="@SearchCliente" @bind-Value="ActivityModel.Cliente" @bind-Value:after="OnClienteChanged"
CoerceValue="true"/>
</div>
<div class="divider"></div>
<div class="form-container">
<span class="disable-full-width">Commessa</span>
@if (Commesse.IsNullOrEmpty())
{
<span class="warning-text">Nessuna commessa presente</span>
}
else
{
<MudSelectExtended FullWidth="true" ReadOnly="@(IsView || Commesse.IsNullOrEmpty())" T="string?" Variant="Variant.Text" @bind-Value="ActivityModel.CodJcom" @bind-Value:after="OnCommessaChanged" Class="customIcon-select" AdornmentIcon="@Icons.Material.Filled.Code">
@foreach (var com in Commesse)
{
<MudSelectItemExtended Class="custom-item-select" Value="@com.CodJcom">@($"{com.CodJcom} - {com.Descrizione}")</MudSelectItemExtended>
}
</MudSelectExtended>
}
</div>
</div>
<div class="input-card">
<div class="form-container">
<span>Inizio</span>
<MudTextField ReadOnly="IsView" T="DateTime?" Format="s" Culture="CultureInfo.CurrentUICulture" InputType="InputType.DateTimeLocal" @bind-Value="ActivityModel.EstimatedTime" @bind-Value:after="OnAfterChangeValue" DebounceInterval="500" OnDebounceIntervalElapsed="OnAfterChangeValue"/>
</div>
<div class="divider"></div>
<div class="form-container">
<span>Fine</span>
<MudTextField ReadOnly="IsView" T="DateTime?" Format="s" Culture="CultureInfo.CurrentUICulture" InputType="InputType.DateTimeLocal" @bind-Value="ActivityModel.EstimatedEndtime" @bind-Value:after="OnAfterChangeValue" DebounceInterval="500" OnDebounceIntervalElapsed="OnAfterChangeValue"/>
</div>
<div class="divider"></div>
<div class="form-container">
<span>Avviso</span>
<MudSwitch ReadOnly="IsView" T="bool" Disabled="true" Color="Color.Primary"/>
</div>
</div>
<div class="input-card">
<div class="form-container">
<span class="disable-full-width">Assegnata a</span>
<MudSelectExtended FullWidth="true" ReadOnly="IsView" T="string?" Variant="Variant.Text" @bind-Value="ActivityModel.UserName" @bind-Value:after="OnAfterChangeValue" Class="customIcon-select" AdornmentIcon="@Icons.Material.Filled.Code">
@foreach (var user in Users)
{
<MudSelectItemExtended Class="custom-item-select" Value="@user.UserName">@user.FullName</MudSelectItemExtended>
}
</MudSelectExtended>
</div>
<div class="divider"></div>
<div class="form-container">
<span class="disable-full-width">Tipo</span>
<MudSelectExtended ReadOnly="IsView" FullWidth="true" T="string?" Variant="Variant.Text" @bind-Value="ActivityModel.ActivityTypeId" @bind-Value:after="OnAfterChangeValue" Class="customIcon-select" AdornmentIcon="@Icons.Material.Filled.Code">
@foreach (var type in ActivityType)
{
<MudSelectItemExtended Class="custom-item-select" Value="@type.ActivityTypeId">@type.ActivityTypeId</MudSelectItemExtended>
}
</MudSelectExtended>
</div>
<div class="divider"></div>
<div class="form-container" @onclick="OpenSelectEsito">
<span class="disable-full-width">Esito</span>
<MudSelectExtended ReadOnly="true" FullWidth="true" T="string?" Variant="Variant.Text" @bind-Value="ActivityModel.ActivityResultId" @bind-Value:after="OnAfterChangeValue" Class="customIcon-select" AdornmentIcon="@Icons.Material.Filled.Code">
@foreach (var result in ActivityResult)
{
<MudSelectItemExtended Class="custom-item-select" Value="@result.ActivityResultId">@result.ActivityResultId</MudSelectItemExtended>
}
</MudSelectExtended>
</div>
</div>
<div class="input-card">
<MudTextField ReadOnly="IsView" T="string?" Placeholder="Note" Variant="Variant.Text" Lines="4" @bind-Value="ActivityModel.Note" @bind-Value:after="OnAfterChangeValue" DebounceInterval="500" OnDebounceIntervalElapsed="OnAfterChangeValue"/>
</div>
<div class="container-chip-attached">
@if (!AttachedList.IsNullOrEmpty())
{
foreach (var item in AttachedList!.Select((p, index) => new { p, index }))
{
@if (item.p.Type == AttachedDTO.TypeAttached.Position)
{
<MudChip T="string" Icon="@Icons.Material.Rounded.LocationOn" Color="Color.Success" OnClick="() => OpenPosition(item.p)" OnClose="() => OnRemoveAttached(item.index)">
@item.p.Description
</MudChip>
}
else
{
<MudChip T="string" Color="Color.Default" OnClick="() => OpenAttached(item.p)" OnClose="() => OnRemoveAttached(item.index)">
@item.p.Name
</MudChip>
}
}
}
@if (ActivityFileList != null)
{
foreach (var file in ActivityFileList)
{
<MudChip T="string" OnClick="() => OpenAttached(file.Id, file.FileName)" Color="Color.Default">
@file.FileName
</MudChip>
}
}
</div>
<div class="container-button">
<MudButton Class="button-settings green-icon"
FullWidth="true"
StartIcon="@Icons.Material.Rounded.AttachFile"
Size="Size.Medium"
OnClick="OpenAddAttached"
Variant="Variant.Outlined">
Aggiungi allegati
</MudButton>
@if (!IsNew)
{
<div class="divider"></div>
<MudButton Class="button-settings gray-icon"
FullWidth="true"
StartIcon="@Icons.Material.Filled.ContentCopy"
Size="Size.Medium"
OnClick="Duplica"
Variant="Variant.Outlined">
Duplica
</MudButton>
<div class="divider"></div>
<MudButton Class="button-settings red-icon"
FullWidth="true"
StartIcon="@Icons.Material.Outlined.Delete"
Size="Size.Medium"
OnClick="DeleteActivity"
Variant="Variant.Outlined">
Elimina
</MudButton>
}
</div>
</div>
<MudMessageBox @ref="ConfirmDelete" Class="c-messageBox" Title="Attenzione!" CancelText="Annulla">
<MessageContent>
Confermi la cancellazione dell'attività corrente?
</MessageContent>
<YesButton>
<MudButton Size="Size.Small" Variant="Variant.Filled" Color="Color.Error"
StartIcon="@Icons.Material.Rounded.DeleteForever">
Cancella
</MudButton>
</YesButton>
</MudMessageBox>
<MudMessageBox @ref="AddNamePosition" Class="c-messageBox" Title="Nome della posizione" CancelText="Annulla">
<MessageContent>
<MudTextField @bind-Value="NamePosition" Variant="Variant.Outlined" />
</MessageContent>
<YesButton>
<MudButton Size="Size.Small" Variant="Variant.Filled" Color="Color.Primary"
StartIcon="@Icons.Material.Rounded.Check">
Salva
</MudButton>
</YesButton>
</MudMessageBox>
<MudMessageBox @ref="ConfirmMemo" Class="c-messageBox" Title="Attenzione!" CancelText="Annulla">
<MessageContent>
Vuoi creare un promemoria?
</MessageContent>
<YesButton>
<MudButton Size="Size.Small" Variant="Variant.Filled" Color="Color.Primary"
StartIcon="@Icons.Material.Rounded.Check">
Crea
</MudButton>
</YesButton>
</MudMessageBox>
</DialogContent>
</MudDialog>
<SaveOverlay VisibleOverlay="VisibleOverlay" SuccessAnimation="SuccessAnimation"/>
<SelectEsito @bind-IsSheetVisible="OpenEsito" @bind-ActivityModel="ActivityModel" @bind-ActivityModel:after="OnAfterChangeEsito"/>
<AddMemo @bind-IsSheetVisible="OpenAddMemo"/>
@code {
[CascadingParameter] private IMudDialogInstance MudDialog { get; set; }
[Parameter] public string? Id { get; set; }
[Parameter] public ActivityDTO? ActivityCopied { get; set; }
private ActivityDTO OriginalModel { get; set; } = new();
private ActivityDTO ActivityModel { get; set; } = new();
private List<StbActivityResult> ActivityResult { get; set; } = [];
private List<SrlActivityTypeUser> ActivityType { get; set; } = [];
private List<StbUser> Users { get; set; } = [];
private List<JtbComt> Commesse { get; set; } = [];
private List<AnagClie> Clienti { get; set; } = [];
private List<PtbPros> Pros { get; set; } = [];
private List<ActivityFileDto>? ActivityFileList { get; set; }
private bool IsNew { get; set; }
private bool IsView => !NetworkService.ConnectionAvailable;
private string? LabelSave { get; set; }
//Overlay for save
private bool VisibleOverlay { get; set; }
private bool SuccessAnimation { get; set; }
private bool OpenEsito { get; set; }
private bool OpenAddMemo { get; set; }
//MessageBox
private MudMessageBox ConfirmDelete { get; set; }
private MudMessageBox ConfirmMemo { get; set; }
private MudMessageBox AddNamePosition { get; set; }
//Attached
private List<AttachedDTO>? AttachedList { get; set; }
private string? NamePosition { get; set; }
private bool CanAddPosition { get; set; } = true;
protected override async Task OnInitializedAsync()
{
Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter;
if (ActivityCopied != null)
{
ActivityModel = ActivityCopied.Clone();
}
else if (!Id.IsNullOrEmpty())
ActivityModel = (await ManageData.GetActivity(new WhereCondActivity { ActivityId = Id }, true)).Last();
if (Id.IsNullOrEmpty()) Id = ActivityModel.ActivityId;
IsNew = Id.IsNullOrEmpty();
LabelSave = IsNew ? "Aggiungi" : null;
_ = LoadData();
await LoadCommesse();
if (IsNew)
{
ActivityModel.EstimatedTime = DateTime.Today.Add(TimeSpan.FromHours(DateTime.Now.Hour));
ActivityModel.EstimatedEndtime = DateTime.Today.Add(TimeSpan.FromHours(DateTime.Now.Hour) + TimeSpan.FromHours(1));
ActivityModel.UserName = UserSession.User.Username;
}
await LoadActivityType();
OriginalModel = ActivityModel.Clone();
}
private async Task Save()
{
if (!CheckPreSave()) return;
VisibleOverlay = true;
StateHasChanged();
await SavePosition();
var response = await IntegryApiService.SaveActivity(ActivityModel);
if (response == null)
return;
var newActivity = response.Last();
await ManageData.InsertOrUpdate(newActivity);
await SaveAttached(newActivity.ActivityId!);
SuccessAnimation = true;
StateHasChanged();
await Task.Delay(1250);
MudDialog.Close(newActivity);
}
private async Task SavePosition()
{
if (AttachedList != null)
{
foreach (var attached in AttachedList)
{
if (attached.Type != AttachedDTO.TypeAttached.Position) continue;
var position = new PositionDTO
{
Description = attached.Description,
Lat = attached.Lat,
Lng = attached.Lng
};
ActivityModel.Position = await IntegryApiService.SavePosition(position);
}
}
}
private async Task SaveAttached(string activityId)
{
if (AttachedList != null)
{
foreach (var attached in AttachedList)
{
if (attached.FileContent is not null && attached.Type != AttachedDTO.TypeAttached.Position)
{
await IntegryApiService.UploadFile(activityId, attached.FileBytes, attached.Name);
}
}
}
}
private bool CheckPreSave()
{
Snackbar.Clear();
if (!ActivityModel.ActivityTypeId.IsNullOrEmpty()) return true;
Snackbar.Add("Tipo attività obbligatorio!", Severity.Error);
return false;
}
private async Task LoadData()
{
if (!IsNew && Id != null)
{
ActivityFileList = await IntegryApiService.GetActivityFile(Id);
}
Users = await ManageData.GetTable<StbUser>();
ActivityResult = await ManageData.GetTable<StbActivityResult>();
Clienti = await ManageData.GetClienti(new WhereCondContact {FlagStato = "A"});
Pros = await ManageData.GetProspect();
}
private async Task LoadActivityType()
{
if (ActivityModel.UserName is null) ActivityType = [];
ActivityType = await ManageData.GetTable<SrlActivityTypeUser>(x =>
x.UserName != null && x.UserName.Equals(ActivityModel.UserName)
);
}
private async Task LoadCommesse() =>
Commesse = await ManageData.GetTable<JtbComt>(x => x.CodAnag != null && x.CodAnag.Equals(ActivityModel.CodAnag));
private async Task<IEnumerable<string>?> SearchCliente(string value, CancellationToken token)
{
if (string.IsNullOrEmpty(value))
return null;
var listToReturn = new List<string>();
listToReturn.AddRange(
Clienti.Where(x => x.RagSoc.Contains(value, StringComparison.OrdinalIgnoreCase)).Select(x => $"{x.CodAnag} - {x.RagSoc}")
);
listToReturn.AddRange(
Pros.Where(x => x.RagSoc.Contains(value, StringComparison.OrdinalIgnoreCase)).Select(x => $"{x.CodPpro} - {x.RagSoc}")
);
return listToReturn;
}
private async Task OnClienteChanged()
{
ActivityModel.CodJcom = null;
if (ActivityModel.Cliente.IsNullOrEmpty()) return;
var match = Regex.Match(ActivityModel.Cliente!, @"^\s*(\S+)\s*-\s*(.*)$");
if (!match.Success)
return;
ActivityModel.CodAnag = match.Groups[1].Value;
ActivityModel.Cliente = match.Groups[2].Value;
await LoadCommesse();
OnAfterChangeValue();
}
private async Task OnCommessaChanged()
{
ActivityModel.Commessa = (await ManageData.GetTable<JtbComt>(x => x.CodJcom.Equals(ActivityModel.CodJcom))).Last().Descrizione;
OnAfterChangeValue();
}
private async Task OnUserChanged()
{
await LoadActivityType();
OnAfterChangeValue();
}
private void OnAfterChangeValue()
{
if (!IsNew)
{
LabelSave = !OriginalModel.Equals(ActivityModel) ? "Aggiorna" : null;
}
if (ActivityModel.EstimatedEndtime <= ActivityModel.EstimatedTime)
{
ActivityModel.EstimatedEndtime = ActivityModel.EstimatedTime.Value.AddHours(1);
}
}
private async Task OnAfterChangeEsito()
{
OnAfterChangeValue();
// var result = await ConfirmMemo.ShowAsync();
// if (result is true)
// OpenAddMemo = !OpenAddMemo;
}
private void OpenSelectEsito()
{
if (!IsNew && (ActivityModel.UserName is null || !ActivityModel.UserName.Equals(UserSession.User.Username)))
{
Snackbar.Add("Non puoi inserire un esito per un'attività che non ti è stata assegnata.", Severity.Info);
return;
}
OpenEsito = !OpenEsito;
StateHasChanged();
}
private async Task DeleteActivity()
{
var result = await ConfirmDelete.ShowAsync();
if (result is true)
{
VisibleOverlay = true;
StateHasChanged();
try
{
await IntegryApiService.DeleteActivity(ActivityModel.ActivityId);
ActivityModel.Deleted = true;
SuccessAnimation = true;
StateHasChanged();
await Task.Delay(1250);
MudDialog.Close(ActivityModel);
}
catch (Exception e)
{
VisibleOverlay = false;
StateHasChanged();
Snackbar.Add("Impossibile cancellare l'attività", Severity.Error);
}
}
}
private void Duplica()
{
var activityCopy = ActivityModel.Clone();
activityCopy.ActivityId = null;
MudDialog.Cancel();
Messenger.Send(new CopyActivityMessage(activityCopy));
}
private async Task OpenAddAttached()
{
var result = await ModalHelpers.OpenAddAttached(Dialog, CanAddPosition);
if (result is { Canceled: false, Data: not null } && result.Data.GetType() == typeof(AttachedDTO))
{
var attached = (AttachedDTO)result.Data;
if (attached.Type == AttachedDTO.TypeAttached.Position)
{
CanAddPosition = false;
var resultNamePosition = await AddNamePosition.ShowAsync();
if (resultNamePosition is true)
attached.Description = NamePosition;
attached.Name = NamePosition!;
}
AttachedList ??= [];
AttachedList.Add(attached);
LabelSave = "Aggiorna";
}
}
private void OnRemoveAttached(int index)
{
if (AttachedList is null || index < 0 || index >= AttachedList.Count)
return;
var attached = AttachedList[index];
if (attached.Type == AttachedDTO.TypeAttached.Position)
CanAddPosition = true;
AttachedList.RemoveAt(index);
StateHasChanged();
}
private async Task OpenAttached(string idAttached, string fileName)
{
try
{
var bytes = await IntegryApiService.DownloadFile(ActivityModel.ActivityId!, fileName);
await AttachedService.OpenFile(bytes, fileName);
}
catch (Exception ex)
{
Snackbar.Clear();
Snackbar.Add("Impossibile aprire il file", Severity.Error);
Console.WriteLine($"Errore durante l'apertura del file: {ex.Message}");
}
}
private async Task OpenAttached(AttachedDTO attached)
{
if (attached is { FileContent: not null, MimeType: not null })
{
await AttachedService.OpenFile(attached.FileContent!, attached.Name);
}
else
{
Snackbar.Clear();
Snackbar.Add("Impossibile aprire il file", Severity.Error);
}
}
private void OpenPosition(AttachedDTO attached)
{
if (attached is { Lat: not null, Lng: not null })
{
const string baseUrl = "https://www.google.it/maps/place/";
NavigationManager.NavigateTo(
$"{baseUrl}{AdjustCoordinate(attached.Lat.Value)},{AdjustCoordinate(attached.Lng.Value)}"
);
}
else
{
Snackbar.Clear();
Snackbar.Add("Impossibile aprire la posizione", Severity.Error);
}
}
private static string AdjustCoordinate(double coordinate) =>
coordinate.ToString(CultureInfo.InvariantCulture).Replace(",", ".");
}