335 lines
19 KiB
Plaintext
335 lines
19 KiB
Plaintext
@attribute [Authorize]
|
||
@rendermode InteractiveServer
|
||
|
||
@page "/management/Clienti/Modifica"
|
||
@page "/management/Clienti/Modifica/{ClienteId:guid}"
|
||
|
||
@using Microsoft.AspNetCore.Identity
|
||
@using Microsoft.EntityFrameworkCore
|
||
@using StandManager.Model
|
||
|
||
@inject AuthenticationStateProvider auth
|
||
|
||
<PageTitle>@pageTitle</PageTitle>
|
||
|
||
<div class="page-wrapper">
|
||
<!-- BEGIN PAGE HEADER -->
|
||
<div class="page-header d-print-none" aria-label="Page header">
|
||
<div class="container-xl">
|
||
<div class="row g-2 align-items-center">
|
||
<div class="col">
|
||
<h2 class="page-title">@pageTitle</h2>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<!-- END PAGE HEADER -->
|
||
<!-- BEGIN PAGE BODY -->
|
||
<div class="page-body">
|
||
<div class="container-xl">
|
||
<div class="row row-cards">
|
||
<div class="col-lg-12">
|
||
<div class="card">
|
||
<div class="card-header">
|
||
Anagrafica
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="row g-5">
|
||
<EditForm Model="cliente" OnValidSubmit="onClienteSave" FormName="editClienteForm">
|
||
<DataAnnotationsValidator />
|
||
|
||
<div class="col-12">
|
||
<div class="row">
|
||
<div class="col-3 mb-3">
|
||
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Ragione Sociale</RadzenText>
|
||
<RadzenTextBox Style="width: 100%" aria-label="Nome" @bind-Value="@cliente.RagioneSociale" />
|
||
<ValidationMessage For="@(() => cliente.RagioneSociale)" />
|
||
</div>
|
||
|
||
<div class="col-3 mb-3">
|
||
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Partita Iva</RadzenText>
|
||
<RadzenTextBox Style="width: 100%" aria-label="Cognome" @bind-Value="@cliente.PartitaIva" />
|
||
<ValidationMessage For="@(() => cliente.PartitaIva)" />
|
||
</div>
|
||
|
||
<div class="col-3 mb-3">
|
||
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Email</RadzenText>
|
||
<RadzenTextBox Style="width: 100%" aria-label="Email" @bind-Value="@cliente.Email" />
|
||
</div>
|
||
|
||
<div class="col-3 mb-3">
|
||
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Numero di Telefono</RadzenText>
|
||
<RadzenTextBox Style="width: 100%" aria-label="Email" @bind-Value="@cliente.NumeroTelefono" />
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<div class="col-3 mb-3">
|
||
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Email di invito</RadzenText>
|
||
<RadzenTextBox Style="width: 100%" aria-label="Email" @bind-Value="@cliente.EmailInvito" />
|
||
</div>
|
||
<div class="col-3 mb-3">
|
||
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Tipologia Gestionale</RadzenText>
|
||
<RadzenTextBox Style="width: 100%" aria-label="Email" @bind-Value="@cliente.TipologiaGestionale" />
|
||
</div>
|
||
<div class="col-3 mb-3">
|
||
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Tipologia</RadzenText>
|
||
<RadzenDropDown Style="width: 100%" TValue="Guid ?" @bind-Value=@cliente.TipologiaClienteId Data=@tipologiaCliente TextProperty="Nome" ValueProperty="Id" Name="tipologiaClienteDrop" />
|
||
</div>
|
||
<div class="col-3 mb-3">
|
||
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Stato</RadzenText>
|
||
<RadzenDropDown Style="width: 100%" TValue="int" @bind-Value=@cliente.StatoClienteInt Data=@statoCliente TextProperty="Description" ValueProperty="Key" Name="statoClienteDrop" />
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<div class="col-3 mb-3">
|
||
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Agente</RadzenText>
|
||
<RadzenDropDown Style="width: 100%" TValue="Guid ?" @bind-Value=@cliente.AgenteId Data=@agenti TextProperty="Info" ValueProperty="Id" Name="agenteDrop" Disabled="@(!(cliente.CapoareaId == idClaim) && !isAdmin)" />
|
||
</div>
|
||
<div class="col-3 mb-3">
|
||
<RadzenText TextStyle="TextStyle.Subtitle2" TagName="TagName.H3">Capoarea</RadzenText>
|
||
<RadzenDropDown Style="width: 100%" TValue="Guid ?" @bind-Value=@cliente.CapoareaId Data=@capiarea TextProperty="Info" ValueProperty="Id" Name="capoareaDrop" Disabled="!isAdmin" Change="(x => OnCapoareaChange(x))" />
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<div class="col-3 mb-3">
|
||
<button type="button" class="btn btn-default w-100" @onclick="backToHome">
|
||
Annulla
|
||
</button>
|
||
</div>
|
||
<div class="col-3 mb-3">
|
||
<button type="submit" class="btn btn-primary w-100">
|
||
Salva
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</EditForm>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
@if (cliente.Id != Guid.Empty)
|
||
{
|
||
<div class="col-lg-12">
|
||
<div class="card">
|
||
<div class="card-header">
|
||
Destinazioni
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="row g-5">
|
||
<div class="col-auto ms-auto">
|
||
<div class="btn-list">
|
||
<RadzenButton ButtonStyle="ButtonStyle.Light" Variant="Variant.Flat" Size="ButtonSize.Medium" Click="@(args => onNuovaDestinazionePressed())" @onclick:stopPropagation="true" Text="Nuova destinazione"></RadzenButton>
|
||
</div>
|
||
</div>
|
||
<div class="table-responsive">
|
||
<RadzenDataGrid @ref="destinazioniGrid" AllowFiltering="true" AllowColumnResize="true" AllowAlternatingRows="false" FilterMode="FilterMode.CheckBoxList" AllowSorting="true" PageSize="5"
|
||
AllowPaging="true" PagerHorizontalAlign="HorizontalAlign.Left" ShowPagingSummary="true"
|
||
Data="@cliente.Destinazioni" LogicalFilterOperator="LogicalFilterOperator.Or" SelectionMode="DataGridSelectionMode.Single">
|
||
<Columns>
|
||
<RadzenDataGridColumn Property="@nameof(DestinazioneViewModel.Id)" Filterable="false" Title="ID" Width="80px" TextAlign="TextAlign.Center" />
|
||
<RadzenDataGridColumn Property="@nameof(DestinazioneViewModel.RagioneSociale)" Title="Ragione sociale" Width="200px" />
|
||
<RadzenDataGridColumn Property="@nameof(DestinazioneViewModel.PartitaIva)" Title="Partita IVA" Width="200px" />
|
||
<RadzenDataGridColumn Property="@nameof(DestinazioneViewModel.Cap)" Title="Cap" Width="100px" />
|
||
<RadzenDataGridColumn Property="@nameof(DestinazioneViewModel.Citta)" Title="Citta" Width="150px" />
|
||
<RadzenDataGridColumn Property="@nameof(DestinazioneViewModel.Indirizzo)" Title="Indirizzo" Width="200px" />
|
||
<RadzenDataGridColumn Property="@nameof(DestinazioneViewModel.NomeAgente)" Title="Agente" Width="200px" />
|
||
|
||
<RadzenDataGridColumn Context="destinazione" Filterable="false" Sortable="false" TextAlign="TextAlign.Right" Width="200px">
|
||
<Template Context="destinazione">
|
||
<RadzenButton Icon="edit" ButtonStyle="ButtonStyle.Light" Variant="Variant.Flat" Size="ButtonSize.Medium" class="rz-my-1 rz-ms-1" Click="@(args => EditDestinazione(destinazione))" @onclick:stopPropagation="true" />
|
||
<RadzenButton Icon="delete" ButtonStyle="ButtonStyle.Danger" Variant="Variant.Flat" Size="ButtonSize.Medium" Shade="Shade.Lighter" class="rz-my-1 rz-ms-1" Click="@(args => DeleteDestinazione(destinazione))" @onclick:stopPropagation="true" />
|
||
</Template>
|
||
</RadzenDataGridColumn>
|
||
</Columns>
|
||
</RadzenDataGrid>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
@code {
|
||
[Parameter]
|
||
public Guid? ClienteId { get; set; }
|
||
public Guid idClaim { get; set; }
|
||
|
||
[SupplyParameterFromForm]
|
||
private ClienteViewModel cliente { get; set; } = new();
|
||
|
||
private IEnumerable<UtenteViewModel> agenti { get; set; } = new List<UtenteViewModel>();
|
||
private IEnumerable<UtenteViewModel> capiarea { get; set; } = new List<UtenteViewModel>();
|
||
private List<TipologiaClienteViewModel> tipologiaCliente { get; set; } = new();
|
||
private List<LookupViewModel<int>> statoCliente { get; set; } = new();
|
||
RadzenDataGrid<DestinazioneViewModel> destinazioniGrid;
|
||
|
||
private bool isAdmin { get; set; } = false;
|
||
private Guid capoareaId { get; set; } = Guid.Empty;
|
||
|
||
private string pageTitle => cliente?.Id == Guid.Empty ? "Nuovo cliente" : "Modifica cliente";
|
||
|
||
private DialogOptions editNewDialogOption { get; set; } = new()
|
||
{
|
||
Resizable = false,
|
||
Draggable = false,
|
||
Width = "900px"
|
||
};
|
||
|
||
/// <summary>
|
||
/// Inizializza la pagina: carica gli agenti, le tipologie cliente
|
||
/// e, se presente l’ID, recupera il cliente con agente e destinazioni,
|
||
/// registrando anche l’evento di chiusura del dialog.
|
||
/// </summary>
|
||
protected override async Task OnInitializedAsync()
|
||
{
|
||
idClaim = await MembershipUtils.GetUserId(auth);
|
||
var idRuolo = await MembershipUtils.GetRoleId(auth);
|
||
var ruolo = await _managerService.RuoloService.RicercaPer(
|
||
x => x.Id == idRuolo && x.Permessi.Any(y => y.Feature.Type == FeatureType.Capoarea || y.Feature.Type == FeatureType.AdminGlobal),
|
||
includi:x => x.Include(y => y.Permessi).ThenInclude(z => z.Feature));
|
||
|
||
isAdmin = ruolo?.Permessi.Where(x => x.Eliminato == false)?.Any(x => x.Feature.Type == FeatureType.AdminGlobal) ?? false;
|
||
|
||
if (ClienteId.GetValueOrDefault() != Guid.Empty)
|
||
cliente = await _managerService.ClienteService.RicercaPer(x => x.Id == ClienteId, includi: x => x.Include(y => y.Agente).Include(y => y.Capoarea).Include(y => y.TipologiaCliente).Include(y => y.Destinazioni).ThenInclude(z => z.Agente));
|
||
else
|
||
cliente = new ClienteViewModel();
|
||
|
||
capoareaId = cliente.CapoareaId.GetValueOrDefault();
|
||
|
||
capiarea = isAdmin
|
||
? (await _managerService.UtenteService.RicercaQueryable(
|
||
x => x.Eliminato == false && x.IsCapoarea,
|
||
ordinamento: x => x.OrderBy(y => y.Cognome).ThenBy(z => z.Nome)))
|
||
.Select(x => (UtenteViewModel)x).ToList()
|
||
: [await _managerService.UtenteService.RicercaPer(x => x.Id == cliente.CapoareaId)];
|
||
|
||
agenti = cliente.CapoareaId == idClaim || isAdmin
|
||
? (await _managerService.UtenteService.RicercaQueryable(
|
||
x => x.Eliminato == false && (isAdmin || x.CapoareaId == idClaim) && !x.IsCapoarea && x.CapoareaId == cliente.CapoareaId,
|
||
includi: x => x.Include(y => y.Capoarea),
|
||
ordinamento: x => x.OrderBy(y => y.Cognome).ThenBy(z => z.Nome)))
|
||
.Select(x => (UtenteViewModel)x).ToList()
|
||
: [await _managerService.UtenteService.RicercaPer(x => x.Id == cliente.AgenteId)];
|
||
|
||
var eUtils = new EnumUtils();
|
||
tipologiaCliente = (await _managerService.TipologiaClienteService.RicercaQueryable(ordinamento: x => x.OrderBy(y => y.Nome))).Select(x => (TipologiaClienteViewModel)x).ToList();
|
||
statoCliente = eUtils.GetEnumList<ClienteStato>();
|
||
|
||
_dialogService.OnClose += onDialogClose;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Alla chiusura del dialog ricarica da database le destinazioni
|
||
/// del cliente aggiornando la lista in schermata.
|
||
/// </summary>
|
||
private async void onDialogClose(dynamic result)
|
||
{
|
||
cliente.Destinazioni = (await _managerService.DestinazioneService.RicercaQueryable(x => x.Eliminato == false && x.ClienteId == cliente.Id, includi: x => x.Include(y => y.Agente)))
|
||
.Select(x => (DestinazioneViewModel)x).ToList();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Salva il cliente: recupera o crea il modello, applica i dati dalla UI,
|
||
/// collega l’agente, crea una destinazione di default se è un nuovo cliente
|
||
/// e poi salva usando l’ID dell’utente loggato, tornando alla lista clienti.
|
||
/// </summary>
|
||
private async Task onClienteSave()
|
||
{
|
||
var model = await _managerService.ClienteService.RicercaPer(x => x.Id == cliente.Id, solaLettura: false)
|
||
?? new Cliente() { Destinazioni = new List<Destinazione>() };
|
||
|
||
model = cliente.Map(model);
|
||
if (cliente.AgenteId.GetValueOrDefault() != Guid.Empty)
|
||
model.Agente = await _managerService.UtenteService.RicercaPer(x => x.Id == cliente.AgenteId);
|
||
|
||
if (cliente.TipologiaClienteId.GetValueOrDefault() != Guid.Empty)
|
||
{
|
||
model.TipologiaClienteId = cliente.TipologiaClienteId;
|
||
model.TipologiaCliente = await _managerService.TipologiaClienteService.RicercaPer(x => x.Id == cliente.TipologiaClienteId);
|
||
}
|
||
|
||
if (cliente.Id == Guid.Empty)
|
||
{
|
||
var destinazione = new Destinazione() { Cliente = model };
|
||
destinazione = cliente.GetDestinazione(destinazione);
|
||
model.Destinazioni.Add(destinazione);
|
||
}
|
||
|
||
await _managerService.ClienteService.Salva(model, idClaim);
|
||
_navManager.NavigateTo("/management/Clienti");
|
||
}
|
||
|
||
/// <summary>
|
||
/// Torna alla pagina di elenco clienti senza salvare altre modifiche.
|
||
/// </summary>
|
||
private void backToHome()
|
||
{
|
||
_navManager.NavigateTo("/management/Clienti");
|
||
}
|
||
|
||
/// <summary>
|
||
/// Apre il dialog di modifica per la destinazione selezionata.
|
||
/// </summary>
|
||
private async Task EditDestinazione(DestinazioneViewModel destinazione)
|
||
{
|
||
await _dialogService.OpenAsync<Cliente_Destinazione>($"Destinazione {destinazione.RagioneSociale}", new Dictionary<string, object>() { { "destinazioneId", destinazione.Id }, { "clienteId", cliente.Id }, { "canSetAgente", (cliente.CapoareaId == idClaim) || isAdmin }, { "capoareaId", capoareaId } }, editNewDialogOption);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Apre il dialog per creare una nuova destinazione collegata al cliente.
|
||
/// </summary>
|
||
private async Task onNuovaDestinazionePressed()
|
||
{
|
||
await _dialogService.OpenAsync<Cliente_Destinazione>($"Nuova destinazione", new Dictionary<string, object>() { { "clienteId", cliente.Id }, { "canSetAgente", (cliente.CapoareaId == idClaim) || isAdmin }, { "capoareaId", capoareaId } }, editNewDialogOption);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Chiede conferma, elimina la destinazione scelta se confermato
|
||
/// e ricarica l’elenco delle destinazioni del cliente.
|
||
/// </summary>
|
||
private async Task DeleteDestinazione(DestinazioneViewModel destinazione)
|
||
{
|
||
var ok = await _dialogService.Confirm($"Vuoi davvero eliminare la destinazione {cliente.RagioneSociale}?", "Conferma eliminazione", new ConfirmOptions { OkButtonText = "Sì", CancelButtonText = "No", Width = "400px" });
|
||
|
||
if (ok == true)
|
||
{
|
||
await _managerService.DestinazioneService.Elimina(destinazione.Id, await MembershipUtils.GetUserId(auth));
|
||
cliente.Destinazioni = (await _managerService.DestinazioneService.RicercaQueryable(x => x.Eliminato == false && x.ClienteId == cliente.Id, includi: x => x.Include(y => y.Agente)))
|
||
.Select(x => (DestinazioneViewModel)x).ToList();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Aggiorna la drop di agenti in base al valore di capoarea
|
||
/// </summary>
|
||
private async Task OnCapoareaChange(object value)
|
||
{
|
||
var capoId = Guid.Empty;
|
||
|
||
if (Guid.TryParse(value.ToString(), out capoId))
|
||
{
|
||
capoareaId = capoId;
|
||
cliente.AgenteId = null;
|
||
|
||
agenti = cliente.CapoareaId == idClaim || isAdmin
|
||
? (await _managerService.UtenteService.RicercaQueryable(
|
||
x => x.Eliminato == false && (isAdmin || x.CapoareaId == idClaim) && !x.IsCapoarea && x.CapoareaId == capoareaId,
|
||
includi: x => x.Include(y => y.Capoarea),
|
||
ordinamento: x => x.OrderBy(y => y.Cognome).ThenBy(z => z.Nome)))
|
||
.Select(x => (UtenteViewModel)x).ToList()
|
||
: [await _managerService.UtenteService.RicercaPer(x => x.Id == cliente.AgenteId)];
|
||
}
|
||
}
|
||
}
|