Files
lux/Lux.UI/Components/Compo/Templates/TemplateRowList.razor.cs
Samuele Locatelli 780c2f42eb Buy Items: fix clone con evento tornato a parent
Template: fix display righe cancellabili/non cancellabili
2026-05-11 11:45:36 +02:00

1419 lines
52 KiB
C#

using EgwCoreLib.Razor;
using Newtonsoft.Json;
using WebWindowComplex;
using WebWindowComplex.DTO;
using static EgwCoreLib.Lux.Core.Enums;
using static WebWindowComplex.LayoutConst;
namespace Lux.UI.Components.Compo.Templates
{
public partial class TemplateRowList : IDisposable
{
#region Public Properties
/// <summary>
/// Modello dell'offerta corrente (bindato dal genitore).
/// </summary>
[Parameter]
public TemplateModel CurrParent { get; set; } = null!;
/// <summary>
/// Ricerca libera da parent
/// </summary>
[Parameter]
public string CurrSearchVal { get; set; } = "";
[Parameter]
public DisplayMode DisplayMode { get; set; } = DisplayMode.Standard;
[Parameter]
public EventCallback<bool> EC_ReqFullPage { get; set; }
[Parameter]
public EventCallback<bool> EC_Updated { get; set; }
#endregion Public Properties
#region Public Methods
/// <summary>
/// Dispose sottoscrizione canale
/// </summary>
public void Dispose()
{
DLService.PipeUpdate.EA_NewMessage -= PipeUpdate_EA_NewMessage;
DLService.PipePng.EA_NewMessage -= PipePng_EA_NewMessage;
DLService.PipeSvg.EA_NewMessage -= PipeSvg_EA_NewMessage;
DLService.PipeHwOpt.EA_NewMessage -= PipeHwOpt_EA_NewMessage;
DLService.PipeProfElement.EA_NewMessage -= PipeProfElement_EA_NewMessage;
DLService.PipeProfList.EA_NewMessage -= PipeProfList_EA_NewMessage;
DLService.PipeShape.EA_NewMessage -= PipeShape_EA_NewMessage;
}
#endregion Public Methods
#region Protected Methods
/// <summary>
/// Verifica after render x stato interattivo pagina
/// </summary>
/// <param name="firstRender"></param>
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
// JS interop or data fetches go here
isInteractive = true;
}
}
/// <summary>
/// init obj
/// </summary>
protected override async Task OnInitializedAsync()
{
ConfInit();
prevJwd = "";
await ReloadBaseList();
DLService.PipeUpdate.EA_NewMessage += PipeUpdate_EA_NewMessage;
DLService.PipePng.EA_NewMessage += PipePng_EA_NewMessage;
DLService.PipeSvg.EA_NewMessage += PipeSvg_EA_NewMessage;
DLService.PipeHwOpt.EA_NewMessage += PipeHwOpt_EA_NewMessage;
DLService.PipeProfElement.EA_NewMessage += PipeProfElement_EA_NewMessage;
DLService.PipeProfList.EA_NewMessage += PipeProfList_EA_NewMessage;
DLService.PipeShape.EA_NewMessage += PipeShape_EA_NewMessage;
}
protected override async Task OnParametersSetAsync()
{
await ReloadDataAsync();
currPage = 1;
UpdateTable();
}
#endregion Protected Methods
#region Private Fields
private static Logger Log = LogManager.GetCurrentClassLogger();
private List<GenValueModel> AllColors = new();
private List<EnvirParamModel> AllConfEnvir = new();
private List<GlassModel> AllConfGlass = new();
private List<HardwareModel> AllConfHardware = new();
private List<WoodModel> AllConfWood = new();
private List<TemplateRowModel> AllRecords = new List<TemplateRowModel>();
private string apiUrl = "";
private List<string> AvailColorMaterialList = new List<string>();
private List<string> AvailFamilyHardwareList = new List<string>();
private List<string> AvailGlassList = new List<string>();
private List<Egw.Window.Data.Hardware> AvailHardwareList = new();
/// <summary>
/// Lista profili da DB
/// </summary>
private List<ProfileModel> AvailProfileList = new();
/// <summary>
/// Lista profili da Redis (old way)
/// </summary>
private List<string> AvailProfileListOld = new List<string>();
private List<string> AvailWoodList = new List<string>();
/// <summary>
/// Base path x network share files
/// </summary>
private string basePath = "unsafe_uploads";
private string calcTag = "calc";
private EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS cEnvir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW;
/// <summary>
/// Channel update HwOptions
/// </summary>
private string chHwOpt = "";
/// <summary>
/// Channel update PNG
/// </summary>
private string chPng = "";
/// <summary>
/// Channel update Profile List
/// </summary>
private string chProfElem = "";
/// <summary>
/// Channel update Profile List
/// </summary>
private string chProfList = "";
/// <summary>
/// Channel update Shape
/// </summary>
private string chShape = "";
/// <summary>
/// Channel update SVG
/// </summary>
private string chSvg = "";
private List<AreaProfiles> currAreaProfiles = new();
/// <summary>
/// Predisposizione valori live SVG/JWD
/// </summary>
private LivePayload CurrData = new LivePayload();
/// <summary>
/// Modalit editint attiva
/// </summary>
private EditMode CurrEditMode = EditMode.None;
private Dictionary<int, string> currGroupShape = new();
private Dictionary<int, string> currHwOption = new();
private int currPage = 1;
private string currPng = "";
private int CurrSellingItemId = 0;
private string currSvg = "";
private TemplateRowModel? EditRecord = null;
private string genericBasePath = "";
private string imgBasePath = "";
/// <summary>
/// Semaforo x definire se sia gia in modalita ionterattiva o di prerendering
/// </summary>
private bool isInteractive = false;
private bool isLoading = false;
private List<TemplateRowModel> ListFilt = new List<TemplateRowModel>();
private List<TemplateRowModel> ListRecords = new List<TemplateRowModel>();
private List<SellingItemModel> ListSellItems = new List<SellingItemModel>();
private int numRecord = 5;
/// <summary>
/// Versione precedente JWD x test e confronto
/// </summary>
private string prevJwd = "";
/// <summary>
/// Dizionario richieste
/// </summary>
private Dictionary<string, string> reqDict = new Dictionary<string, string>();
private TemplateRowModel? SelRecord = null;
/// <summary>
/// Configurazione elenchi anagrafiche
/// </summary>
private BaseListPayload SetupList = new BaseListPayload();
private int totalCount = 0;
private BootstrapModal Modal = new();
private string mTitle = "";
private string mMessage = "";
private BootstrapModal.ModalMode mMode = BootstrapModal.ModalMode.ND;
private Dictionary<bool, string> modalOpt = new Dictionary<bool, string>();
#endregion Private Fields
#region Private Properties
[Inject]
private IConfigDataService CDService { get; set; } = null!;
[Inject]
private IConfGlassService CGService { get; set; } = null!;
[Inject]
private IConfiguration Config { get; set; } = null!;
[Inject]
private IConfProfileService CPService { get; set; } = null!;
[Inject]
private ICalcRuidService CRService { get; set; } = null!;
[Inject]
private ICalcRequestService CService { get; set; } = null!;
[Inject]
private IConfWoodService CWService { get; set; } = null!;
/// <summary>
/// Servizi di accesso ai dati iniettati dal framework.
/// </summary>
[Inject]
private IDataLayerServices DLService { get; set; } = null!;
[Inject]
private IEnvirParamService EPService { get; set; } = null!;
[Inject]
private IGenValService GVService { get; set; } = default!;
[Inject]
private IImageCacheService ICService { get; set; } = null!;
[Inject]
private IJSRuntime JSRuntime { get; set; } = null!;
[Inject]
private ISellingItemService SIService { get; set; } = null!;
/// <summary>
/// ID Template corrente
/// </summary>
private int TemplateID
{
get => CurrParent.TemplateID;
}
[Inject]
private ITemplateService TplService { get; set; } = default!;
[Inject]
private ITemplateRowService TRService { get; set; } = default!;
#endregion Private Properties
#region Private Methods
/// <summary>
/// Effettua vera richiesta della BOM
/// </summary>
/// <returns></returns>
private async Task callRefreshProfList()
{
Dictionary<string, string> DictExec = new Dictionary<string, string>();
var cMode = Egw.Window.Data.Enums.QuestionModes.CONFIG;
var cSubMode = Egw.Window.Data.Enums.QuestionConfSubModes.PROFILELIST;
// compongo righiesta
string reqUid = "Default";
DictExec.Add("Mode", $"{(int)cMode}");
DictExec.Add("UID", reqUid);
// creo registrazione richiesta...
var ruid = await CRService.AddRequestAsync($"{cEnvir}", $"{cMode}-{cSubMode}", reqUid);
// aggiungo RUID effettivo
DictExec.Add("RUID", ruid);
DictExec.Add("SubMode", $"{(int)cSubMode}");
CalcRequestDTO req = new CalcRequestDTO()
{
EnvType = cEnvir,
DictExec = DictExec
};
// chiamo la chiamata POST alla API, che manda la richiesta via REDIS
await CService.CallRestPost($"{apiUrl}/{genericBasePath}", $"{calcTag}/{reqUid}", req);
}
private string CheckSelect(TemplateRowModel curRec)
{
string answ = "";
if (SelRecord != null)
{
answ = curRec.TemplateRowID == SelRecord.TemplateRowID ? "table-info" : "";
}
return answ;
}
/// <summary>
/// Chiude edit andando eventualmente a salvare
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
private async Task CloseEdit(bool doSave)
{
// Proseguo solo se sono in interattivo (NO prerender pagina)
if (isInteractive)
{
// ...se ho editing
if (EditRecord != null)
{
// SE richiesto salvataggio...
if (doSave)
{
// salvo su DB!
await TRService.UpdateSerStructAsync(EditRecord.TemplateRowID, prevJwd);
// salvo nel record corrente!
EditRecord.SerStruct = prevJwd;
}
else
// altrimenti ricalcolo valore salvato
{
prevJwd = EditRecord.SerStruct;
CurrData.CurrJwd = EditRecord.SerStruct;
}
// aggiorno nel dizionari
if (reqDict.ContainsKey("SerializedData"))
{
reqDict["SerializedData"] = prevJwd;
}
// chiamo comunque update IMG
await reqImgUpdate(EditRecord);
// reimposto il select record...
SelRecord = EditRecord;
// ora resetto
EditRecord = null;
CurrEditMode = EditMode.None;
}
}
}
/// <summary>
/// Chiude edit con preprocess x caso JWD
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
private async Task CloseEditJwd(DataSave infoSave)
{
prevJwd = infoSave.currJwd;
// elimino richiesta full
// reset preview
await CloseEdit(infoSave.ForceSave);
await Task.Delay(200);
CurrData.SvgPreview = "";
await EC_ReqFullPage.InvokeAsync(false);
}
private void ConfInit()
{
basePath = Config.GetValue<string>("ServerConf:FileSharePath") ?? "unsafe_uploads";
apiUrl = Config.GetValue<string>("ServerConf:Prog.ApiUrl") ?? "";
imgBasePath = Config.GetValue<string>("ServerConf:ImageBaseUrl") ?? "";
genericBasePath = Config.GetValue<string>("ServerConf:GenericBaseUrl") ?? "";
calcTag = Config.GetValue<string>("ServerConf:CalcTag") ?? "calc";
chHwOpt = Config.GetValue<string>("ServerConf:ChannelHwOpt") ?? "";
chPng = Config.GetValue<string>("ServerConf:ChannelPng") ?? "";
chProfElem = Config.GetValue<string>("ServerConf:ChannelProfElem") ?? "";
chProfList = Config.GetValue<string>("ServerConf:ChannelProfList") ?? "";
chShape = Config.GetValue<string>("ServerConf:ChannelShape") ?? "";
chSvg = Config.GetValue<string>("ServerConf:ChannelSvg") ?? "";
}
/// <summary>
/// Eliminazione file (old)
/// </summary>
/// <param name="secureName">Nome secure da impiegare</param>
/// <param name="content">Contenuto file</param>
private bool DeleteOldFile(string folderPath, string secureName)
{
bool answ = false;
if (!string.IsNullOrEmpty(folderPath))
{
// calcolo path file...
string filePath = Path.Combine(basePath, folderPath, secureName);
// se esiste...
if (File.Exists(filePath))
{
File.Delete(filePath);
}
}
return answ;
}
/// <summary>
/// Esecuzione azione richiesta
/// </summary>
/// <param name="actReq">Azione richiesta</param>
private void DoAction(LayoutConst.DataAction actReq)
{
switch (actReq)
{
case LayoutConst.DataAction.None:
break;
case LayoutConst.DataAction.ResetDictShape:
CurrData.DictShape = new Dictionary<int, string>();
break;
case LayoutConst.DataAction.ResetHwOpt:
CurrData.DictOptionsXml = new Dictionary<int, string>();
break;
case DataAction.ResetDimElem:
CurrData.ProfElementList = new List<AreaProfiles>();
break;
default:
break;
}
}
private void DoAdd()
{
// valori helper
DateTime adesso = DateTime.Now;
int newRow = 1 + (AllRecords.Count > 0 ? AllRecords.Max(x => x.RowNum) : 0);
EditRecord = new TemplateRowModel()
{
TemplateID = CurrParent.TemplateID,
Envir = CurrParent.Envir,
Name = $"New Template ITEM {adesso.Year}",
RowNum = newRow,
SerStruct = "{}",
SellingItemID = CurrSellingItemId
};
}
private async Task DoClone(TemplateRowModel rec2clone)
{
mTitle = "Attenzione";
mMessage = "Confermi di voler duplicare la riga corrente?";
mMode = BootstrapModal.ModalMode.Confirm;
modalOpt = new();
modalOpt.Add(true, "Si");
modalOpt.Add(false, "No");
if (!await Modal!.ShowAsync<bool>())
return;
// calcolo indice riga...
int numRow = totalCount + 1;
TemplateRowModel newRec = new TemplateRowModel()
{
AwaitBom = true,
AwaitPrice = true,
Envir = rec2clone.Envir,
FileName = rec2clone.FileName,
FileResource = rec2clone.FileResource,
FileSize = rec2clone.FileSize,
Inserted = DateTime.Now,
ItemBOM = rec2clone.ItemBOM,
ItemSteps = rec2clone.ItemSteps,
Note = rec2clone.Note,
TemplateID = TemplateID,
TemplateRowUID = "",
Qty = rec2clone.Qty,
RowNum = numRow,
SellingItemID = rec2clone.SellingItemID,
SerStruct = rec2clone.SerStruct,
StepCost = rec2clone.StepCost,
StepPrice = rec2clone.StepPrice,
};
// salvo sul DB
await TRService.UpsertAsync(newRec);
// chiamo update record che non hanno UID x questo ordine
var list2fix = await TRService.FixRowUidAsync(TemplateID);
if (list2fix != null && list2fix.Count > 0)
{
// rileggo i miei record...
await ReloadDataAsync();
var listCalc = SorListCalc();
foreach (var item in listCalc)
{
// se UID tra quelli da ricalcolare...
if (list2fix.Contains(item.TemplateRowUID))
{
// chiedo BOM e immagine
await reqBomUpdate(item);
}
}
}
await ReloadDataAsync();
UpdateTable();
}
private void DoClose()
{
SelRecord = null;
EditRecord = null;
}
/// <summary>
/// Esegue lettura file + invio richiesta specifica
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
private async Task DoDelete(TemplateRowModel curRec)
{
mTitle = "Attenzione";
mMessage = $"Confermi di voler eliminare il template selezionato?\n" +
$"Env: {curRec.Envir} | {curRec.Name}";
mMode = BootstrapModal.ModalMode.Confirm;
modalOpt = new();
modalOpt.Add(true, "Si");
modalOpt.Add(false, "No");
if (!await Modal!.ShowAsync<bool>())
return;
await TRService.DeleteAsync(curRec);
// elimino cache img
await ICService.DeleteSvgAsync(curRec.TemplateRowUID, curRec.Envir);
// rileggo
await ReloadDataAsync();
UpdateTable();
}
private Task DoEdit(TemplateRowModel curRec)
{
EditRecord = curRec;
// chiamo comunque update IMG
return reqImgUpdate(curRec);
}
/// <summary>
/// Edit del file:
/// - abilitazione fileUpload
/// - anteprima grande (live)
/// </summary>
/// <param name="curRec"></param>
private Task DoEditFile(TemplateRowModel curRec)
{
EditRecord = curRec;
/// modalitedit: gestione JWD
CurrEditMode = EditMode.File;
return EC_ReqFullPage.InvokeAsync(true);
}
/// <summary>
/// Apre editor finestre del record richiesto
/// </summary>
/// <param name="curRec"></param>
private async Task DoEditJwd(TemplateRowModel curRec)
{
EditRecord = curRec;
/// modalitedit: gestione JWD
CurrEditMode = EditMode.SerStruc;
// preparazione dati da record corrente
PrepareWindowData(EditRecord.SerStruct);
// chiamo comunque update IMG
await reqImgUpdate(EditRecord);
// reset prev
prevJwd = "";
await EC_ReqFullPage.InvokeAsync(true);
}
private async Task DoRecalcTemplate(bool forceResetCalc)
{
isLoading = true;
if (forceResetCalc)
{
await setAwaitPrice(true, false);
UpdateTable();
await InvokeAsync(StateHasChanged);
await Task.Delay(300);
}
await TplService.UpdateCostAsync(TemplateID);
if (forceResetCalc)
{
await Task.Delay(300);
await setAwaitPrice(false, true);
}
// rileggo dati
await ReloadDataAsync();
UpdateTable();
isLoading = false;
await EC_Updated.InvokeAsync(true);
}
private async Task DoReset()
{
EditRecord = null;
SelRecord = null;
await ReloadDataAsync();
UpdateTable();
}
/// <summary>
/// Salva record ed avanza compilazione
/// </summary>
/// <param name="updRec"></param>
/// <returns></returns>
private async Task DoSave(TemplateRowModel updRec)
{
// salvo record
await TRService.UpsertAsync(updRec);
// chiamo comunque update IMG
await reqImgUpdate(updRec);
EditRecord = null;
SelRecord = null;
// faccio fix UID del gruppo...
await TRService.FixRowUidAsync(TemplateID);
await ReloadDataAsync();
UpdateTable();
}
/// <summary>
/// Salvataggio del JWD aggiornato nella mia riga di offerta
/// </summary>
/// <param name="CurrArgs"></param>
private async Task ExecRequest(Dictionary<string, string> CurrArgs)
{
// Proseguo solo se sono in interattivo (NO prerender pagina)
if (isInteractive)
{
// ...se ho editing
if (EditRecord != null)
{
// SE contiene il mio Jwd...
if (CurrArgs.ContainsKey("SerializedData"))
{
string serStruct = CurrArgs["SerializedData"];
// controllo SE variato...
if (!prevJwd.Equals(serStruct) || !EgwCoreLib.Utils.DictUtils.DictAreEqual(reqDict, CurrArgs))
{
// aggiorno val prev
reqDict = CurrArgs;
prevJwd = serStruct;
// aggiorno live data
CurrData.CurrJwd = serStruct;
// chiamo richiesta update
CalcRequestDTO calcRequestDTO = new CalcRequestDTO();
calcRequestDTO.EnvType = EditRecord.Envir;
calcRequestDTO.DictExec = reqDict;
// chiamo la chiamata POST alla API, che manda la richiesta via REDIS
await ICService.CallRestPost($"{apiUrl}/{genericBasePath}", $"{calcTag}/{EditRecord.TemplateRowUID}", calcRequestDTO);
#if false
// salvo su DB!
await DLService.OffertRowUpdateSerStruct(EditRecord.OfferRowID, serStruct);
#endif
}
}
}
}
}
/// <summary>
/// Path da parent record
/// </summary>
/// <param name="objID"></param>
/// <returns></returns>
private string FolderPath(int objID)
{
return $"TP-{objID:X8}";
}
/// <summary>
/// Display fileSize scalato
/// </summary>
/// <param name="size"></param>
/// <returns></returns>
private string fSize(long size)
{
return EgwCoreLib.Utils.FileHelpers.SizeSuffix(size, 1);
}
/// <summary>
/// Calcola src immagine direttamente da modello dati + base path
/// </summary>
/// <param name="imgUrl"></param>
/// <returns></returns>
private string imgSrc(string imgUrl)
{
return $"{apiUrl}/{imgBasePath}/{imgUrl}";
}
/// <summary>
/// Restituisce il contenuto del file salvato
/// </summary>
/// <param name="folderPath"></param>
/// <param name="secureName"></param>
/// <returns></returns>
private string LoadFileContent(string folderPath, string secureName)
{
string answ = "";
if (!string.IsNullOrEmpty(folderPath))
{
try
{
// calcolo path file...
string filePath = Path.Combine(basePath, folderPath, secureName);
if (File.Exists(filePath))
{
answ = File.ReadAllText(filePath);
}
}
catch (Exception exc)
{
Log.Error($"Exception on LoadFileContent{Environment.NewLine}{exc}");
}
}
return answ;
}
/// <summary>
/// Aggiornamento costing completo:
/// - verifica UID
/// - ricalcolo BOM
/// - update prezzi
/// </summary>
/// <returns></returns>
private async Task OfferUpdateAllCosting()
{
mTitle = "Attenzione";
mMessage = "Confermi di voler ricalcolare/validare in toto i Template del gruppo?";
mMode = BootstrapModal.ModalMode.Confirm;
modalOpt = new();
modalOpt.Add(true, "Si");
modalOpt.Add(false, "No");
if (!await Modal!.ShowAsync<bool>())
return;
// metto a waiting tutte le righe con bom...
var listCalc = SorListCalc();
foreach (var item in listCalc)
{
await TRService.UpdateAwaitStateAsync(item.TemplateRowID, true, true);
}
await InvokeAsync(StateHasChanged);
// verifica preliminare UID
await TRService.FixRowUidAsync(TemplateID);
// ricalcolo di tutte le BOM e relativi prezzi...
foreach (var item in listCalc)
{
// chiedo BOM e immagine
await reqBomUpdate(item);
}
}
/// <summary>
/// Ricevuto HwOpt, processo
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void PipeHwOpt_EA_NewMessage(object? sender, EventArgs e)
{
// vale SOLO SE sono in editing...
if (EditRecord != null)
{
// aggiorno visualizzazione
PubSubEventArgs currArgs = (PubSubEventArgs)e;
// conversione on-the-fly dati HwOpt
if (!string.IsNullOrEmpty(currArgs.newMessage))
{
if (currArgs.msgUid.Equals($"{chHwOpt}:{EditRecord.TemplateRowUID}"))
{
// se non è vuoto deserializzo
if (currArgs.newMessage.Count() > 2)
{
var rawDict = JsonConvert.DeserializeObject<Dictionary<int, string>>(currArgs.newMessage) ?? new Dictionary<int, string>();
currHwOption = rawDict;
}
else
{
currHwOption = new Dictionary<int, string>();
}
// salvo in live data...
CurrData.DictOptionsXml = currHwOption;
}
await InvokeAsync(StateHasChanged);
}
}
}
private async void PipePng_EA_NewMessage(object? sender, EventArgs e)
{
// vale SOLO SE sono in editing...
if (EditRecord != null)
{
// aggiorno visualizzazione
PubSubEventArgs currArgs = (PubSubEventArgs)e;
// conversione on-the-fly PNG da mostrare
if (!string.IsNullOrEmpty(currArgs.newMessage))
{
if (currArgs.msgUid.Equals($"{chPng}:{EditRecord.TemplateRowUID}"))
{
currPng = currArgs.newMessage;
}
await InvokeAsync(StateHasChanged);
}
}
}
/// <summary>
/// Ricevuto ProfElem, processo
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void PipeProfElement_EA_NewMessage(object? sender, EventArgs e)
{
// vale SOLO SE sono in editing...
if (EditRecord != null)
{
// aggiorno visualizzazione
PubSubEventArgs currArgs = (PubSubEventArgs)e;
// conversione on-the-fly Profile
if (!string.IsNullOrEmpty(currArgs.newMessage))
{
if (currArgs.msgUid.StartsWith($"{chProfElem}:{EditRecord.TemplateRowUID}"))
{
// deserializzo il dizionario delle risposte...
var rawDict = JsonConvert.DeserializeObject<List<AreaProfiles>>(currArgs.newMessage);
currAreaProfiles = rawDict ?? new List<AreaProfiles>();
CurrData.ProfElementList = currAreaProfiles;
}
await InvokeAsync(StateHasChanged);
}
}
}
/// <summary>
/// Ricevuta profile list, processo
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void PipeProfList_EA_NewMessage(object? sender, EventArgs e)
{
// vale SOLO SE sono in editing...
if (EditRecord != null)
{
// aggiorno visualizzazione
PubSubEventArgs currArgs = (PubSubEventArgs)e;
// conversione on-the-fly ProfileListAsync
if (!string.IsNullOrEmpty(currArgs.newMessage))
{
if (currArgs.msgUid.Equals($"{chProfList}:{EditRecord.TemplateRowUID}"))
{
try
{
// nuova gestione da DB: attendo 500ms che il DB sia aggiornato
await Task.Delay(500);
// rileggo DB x info profili
AvailProfileList = await CPService.GetAllAsync();
// converto i profili nel nuovo formato x payload...
var profList = AvailProfileList.Select(x => new ProfilePayload()
{
ProfileName = x.Code,
ThresholdList = x.ThresholdList,
ParameterDict = x.ProfileDataDict
}).ToList();
SetupList.ProfileList = profList;
}
catch
{ }
}
await InvokeAsync(StateHasChanged);
}
}
}
/// <summary>
/// Ricevuta shape, procersso
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void PipeShape_EA_NewMessage(object? sender, EventArgs e)
{
// vale SOLO SE sono in editing...
if (EditRecord != null)
{
// aggiorno visualizzazione
PubSubEventArgs currArgs = (PubSubEventArgs)e;
// conversione on-the-fly SVG da mostrare
if (!string.IsNullOrEmpty(currArgs.newMessage))
{
if (currArgs.msgUid.StartsWith($"{chShape}:{EditRecord.TemplateRowUID}"))
{
// deserializzo il dizionario delle risposte...
var rawDict = JsonConvert.DeserializeObject<Dictionary<int, string>>(currArgs.newMessage);
currGroupShape = rawDict ?? new Dictionary<int, string>();
CurrData.DictShape = currGroupShape;
}
await InvokeAsync(StateHasChanged);
}
}
}
/// <summary>
/// Ricevuto SVG, se il mio lo aggiorno...
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void PipeSvg_EA_NewMessage(object? sender, EventArgs e)
{
// vale SOLO SE sono in editing...
if (EditRecord != null)
{
// aggiorno visualizzazione
PubSubEventArgs currArgs = (PubSubEventArgs)e;
// conversione on-the-fly SVG da mostrare
if (!string.IsNullOrEmpty(currArgs.newMessage))
{
if (currArgs.msgUid.Equals($"{chSvg}:{EditRecord.TemplateRowUID}"))
{
currSvg = currArgs.newMessage;
// salvo in live data...
CurrData.SvgPreview = currSvg;
}
await InvokeAsync(StateHasChanged);
}
}
}
/// <summary>
/// Task verifica update ricevuti
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void PipeUpdate_EA_NewMessage(object? sender, EventArgs e)
{
// aggiorno visualizzazione
PubSubEventArgs currArgs = (PubSubEventArgs)e;
// conversione on-the-fly SVG da mostrare
if (!string.IsNullOrEmpty(currArgs.newMessage))
{
// cerco se faccia parte dei record correnti...
var recFound = AllRecords.Any(x => x.TemplateRowUID == currArgs.newMessage);
if (recFound)
{
isLoading = true;
await Task.Delay(1);
// se si tratta dell'UID corrente --> fa update
await DoRecalcTemplate(false);
await InvokeAsync(StateHasChanged);
}
}
await Task.Delay(1);
}
/// <summary>
/// Preparazione dati x componente edit JWD
/// </summary>
/// <param name="currJwd"></param>
private void PrepareWindowData(string currJwd)
{
// converto i profili nel nuovo formato x payload...
var profList = AvailProfileList.Select(x => new ProfilePayload()
{
ProfileName = x.Code,
ThresholdList = x.ThresholdList,
ParameterDict = x.ProfileDataDict
}).ToList();
// preparo conf oggetti x controllo
SetupList = new BaseListPayload()
{
ColorMaterial = AvailColorMaterialList,
FamilyHardware = AvailFamilyHardwareList,
Glass = AvailGlassList,
Hardware = AvailHardwareList,
Wood = AvailWoodList,
ProfileList = profList
};
CurrData = new LivePayload()
{
CurrJwd = currJwd,
SvgPreview = "" //currSvg
};
}
/// <summary>
/// init classi configurazione
/// </summary>
/// <returns></returns>
private async Task ReloadBaseList()
{
// lettura config setup varie da DB/Cache Redis
AllConfEnvir = await EPService.GetAllAsync();
AllConfGlass = await CGService.GetAllAsync();
AvailProfileList = await CPService.GetAllAsync();
// FixMe Todo: eliminare da REDIS e usare elenco DB solamente...
var rawProfiles = await CDService.ProfileListAsync(cEnvir, "Default");
// se fosse vuoto chiamo update...
if (rawProfiles == null || rawProfiles.Count == 0)
{
await callRefreshProfList();
// aspetto 200ms... e richiedo!
await Task.Delay(200);
rawProfiles = await CDService.ProfileListAsync(cEnvir, "Default");
}
AvailProfileListOld = rawProfiles;
var rawHw = await CDService.HwModelListAsync(cEnvir, "HW.AGB");
// hw filtro solo validi...
AllConfHardware = rawHw
.Where(x => !x.FamilyName.Equals(x.Description, StringComparison.OrdinalIgnoreCase))
.ToList();
AllConfWood = await CWService.GetAllAsync();
AllColors = await GVService.GetFiltAsync("WoodCol");
// conversione tipi
AvailGlassList = AllConfGlass
.Select(x => x.Description)
.ToList();
AvailFamilyHardwareList = AllConfHardware
.DistinctBy(x => x.FamilyName)
.OrderBy(x => x.FamilyName)
.Select(x => x.FamilyName)
.ToList();
AvailHardwareList = AllConfHardware
.Select(x => new Egw.Window.Data.Hardware(x.Id, x.FamilyName, x.Description, x.OpeningType, x.Shape, x.SashQty, x.SashPosition))
.ToList();
AvailWoodList = AllConfWood
.Select(x => x.Description)
.ToList();
// FixMe Todo: aggiunta profili (manca anche nel costruttore...)
AvailColorMaterialList = AllColors
.OrderBy(x => x.Index)
.Select(x => x.ValString)
.ToList();
}
private async Task ReloadDataAsync()
{
// leggo i selling items
ListSellItems = await SIService.GetByEnvirAsync(CurrParent.Envir);
// leggo i dati principali
if (CurrParent != null)
{
AllRecords = await TRService.GetByParentAsync(CurrParent.TemplateID);
}
else
{
AllRecords.Clear();
}
}
/// <summary>
/// Effettua vera richiesta della BOM
/// </summary>
/// <param name="currRec"></param>
/// <returns></returns>
private async Task reqBomUpdate(TemplateRowModel currRec)
{
// salvo richiesta BOM su record
currRec.AwaitBom = true;
currRec.AwaitPrice = true;
await TRService.UpdateAwaitStateAsync(currRec.TemplateRowID, true, true);
// preparo la domanda serializzata
Dictionary<string, string> DictExec = new Dictionary<string, string>();
// verifico parametri da conf envir...
var envRec = AllConfEnvir.FirstOrDefault(x => x.EnvirID == currRec.Envir);
string serKey = envRec != null ? envRec.SerStrucKey : "SerializedData";
// cablata la BOM
DictExec.Add("Mode", $"{(int)Egw.Window.Data.Enums.QuestionModes.BOM}");
// UID cablato x ora...
DictExec.Add("UID", currRec.TemplateRowUID);
// aggiungo file secondo ambiente...
switch (currRec.Envir)
{
case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW:
DictExec.Add(serKey, currRec.SerStruct);
break;
case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.BEAM:
case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WALL:
case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.CABINET:
// rileggo da file...
string folderPath = FolderPath(currRec.TemplateID);
string rawData = LoadFileContent(folderPath, currRec.FileResource);
DictExec.Add(serKey, rawData);
DictExec.Add("FileName", currRec.FileName);
break;
case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.NULL:
default:
break;
}
CalcRequestDTO req = new CalcRequestDTO()
{
EnvType = currRec.Envir,
DictExec = DictExec
};
await InvokeAsync(StateHasChanged);
// chiamo la chiamata POST alla API, che manda la richiesta via REDIS
await CService.CallRestPost($"{apiUrl}/{genericBasePath}", $"{calcTag}/{currRec.TemplateRowUID}", req);
}
/// <summary>
/// Effettua richiesta update IMG
/// </summary>
/// <param name="currRec"></param>
/// <returns></returns>
private async Task reqImgUpdate(TemplateRowModel currRec)
{
// preparo la domanda serializzata
Dictionary<string, string> DictExec = new Dictionary<string, string>();
string rawData = "";
// verifico parametri da conf envir...
var envRec = AllConfEnvir.FirstOrDefault(x => x.EnvirID == currRec.Envir);
string serKey = envRec != null ? envRec.SerStrucKey : "SerializedData";
// cablata la generazione IMG
DictExec.Add("Mode", $"{(int)Egw.Window.Data.Enums.QuestionModes.PREVIEW}");
// UID cablato x ora...
DictExec.Add("UID", currRec.TemplateRowUID);
// aggiungo file secondo ambiente...
switch (currRec.Envir)
{
case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW:
DictExec.Add(serKey, currRec.SerStruct);
if (DictExec.ContainsKey(serKey))
{
rawData = DictExec[serKey];
}
break;
case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.BEAM:
case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WALL:
case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.CABINET:
// rileggo da file...
string folderPath = FolderPath(currRec.TemplateID);
rawData = LoadFileContent(folderPath, currRec.FileResource);
DictExec.Add(serKey, rawData);
DictExec.Add("FileName", currRec.FileName);
break;
case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.NULL:
default:
break;
}
// Procedo SOLO SE i dati serializzati sono + di "{}"
if (!string.IsNullOrEmpty(rawData) && rawData.Length > 2)
{
CalcRequestDTO req = new CalcRequestDTO()
{
EnvType = currRec.Envir,
DictExec = DictExec
};
await InvokeAsync(StateHasChanged);
// chiamo la chiamata POST alla API, che manda la richiesta via REDIS
await CService.CallRestPost($"{apiUrl}/{genericBasePath}", $"{calcTag}/{currRec.TemplateRowUID}", req);
}
}
/// <summary>
/// Lancia la richiesta di ricaolo della BOM dal JWD (o equivalente)
/// </summary>
/// <returns></returns>
private async Task RequestBom(TemplateRowModel currRec)
{
mTitle = "Attenzione";
mMessage = "Confermi di voler ricalcolare la BOM?";
mMode = BootstrapModal.ModalMode.Confirm;
modalOpt = new();
modalOpt.Add(true, "Si");
modalOpt.Add(false, "No");
if (!await Modal!.ShowAsync<bool>())
return;
await reqBomUpdate(currRec);
}
/// <summary>
/// Salvataggio dei dati del file caricato
/// </summary>
/// <param name="fileDict">Dizionario info file</param>
private void SaveFile(Dictionary<string, string> fileDict)
{
// verifico di essere in edit...
if (EditRecord != null)
{
string folderPath = FolderPath(EditRecord.TemplateID);
// verifico di avere parametri...
if (fileDict != null && fileDict.Count > 0)
{
string secureName = "";
string content = "";
if (fileDict.ContainsKey("secureName"))
{
secureName = fileDict["secureName"];
}
if (fileDict.ContainsKey("content"))
{
content = fileDict["content"];
}
if (!string.IsNullOrEmpty(folderPath) && !string.IsNullOrEmpty(secureName) && !string.IsNullOrEmpty(content))
{
// salvo!
SaveFileContent(folderPath, secureName, content);
}
}
// altrimenti signifca cleanup eventuale vecchio file...
else
{
DeleteOldFile(folderPath, EditRecord.FileResource);
}
}
}
/// <summary>
/// Esegue salvataggio del file ricevuto
/// </summary>
/// <param name="folderPath">Path relativo x file (tipicamente UID parent order)</param>
/// <param name="secureName">Nome secure da impiegare</param>
/// <param name="content">Contenuto file</param>
private bool SaveFileContent(string folderPath, string secureName, string content)
{
bool answ = false;
if (!string.IsNullOrEmpty(folderPath))
{
// calcolo path file...
string filePath = Path.Combine(basePath, folderPath, secureName);
string? directoryPath = Path.GetDirectoryName(filePath);
try
{
if (!string.IsNullOrEmpty(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
File.WriteAllText(filePath, content);
}
catch (Exception exc)
{
Log.Error($"Exception on save{Environment.NewLine}{exc}");
}
}
return answ;
}
private void SaveNumRec(int newNum)
{
numRecord = newNum;
UpdateTable();
}
private void SavePage(int newNum)
{
currPage = newNum;
UpdateTable();
}
private async Task setAwaitPrice(bool awaitPrice, bool flushCache)
{
foreach (var item in AllRecords)
{
item.AwaitPrice = awaitPrice;
await TRService.UpdateAwaitStateAsync(item.TemplateRowID, null, awaitPrice, flushCache);
}
}
/// <summary>
/// Verifia ammissibilit display btn ricalcolo BOM da item
/// </summary>
/// <param name="reqItem"></param>
/// <returns></returns>
private bool ShowBom(TemplateRowModel reqItem)
{
bool answ = false;
switch (reqItem.Envir)
{
case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW:
answ = !string.IsNullOrEmpty(reqItem.SerStruct) && reqItem.SerStruct.Length > 2;
break;
case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.BEAM:
case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WALL:
case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.CABINET:
// da cambiare con ricerca file su disco?!?
answ = !string.IsNullOrEmpty(reqItem.FileResource) && !string.IsNullOrEmpty(reqItem.FileName);
break;
case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.NULL:
default:
break;
}
return answ;
}
/// <summary>
/// Elenco Rows calcolabili:
/// - contengono serializzazione come JWD
/// - contengono file come BTL
/// </summary>
private List<TemplateRowModel> SorListCalc()
{
var rawList = AllRecords
.Where(x => (!string.IsNullOrEmpty(x.SerStruct) && x.SerStruct.Length > 2) || (!string.IsNullOrEmpty(x.FileName) && !string.IsNullOrEmpty(x.FileResource)))
.ToList();
return rawList ?? new List<TemplateRowModel>();
}
private void UpdateTable()
{
//ListFilt.Clear();
ListFilt = AllRecords;
if (!string.IsNullOrEmpty(CurrSearchVal))
{
ListFilt = AllRecords
.Where(x => x.Name.Contains(CurrSearchVal, StringComparison.InvariantCultureIgnoreCase) || x.TemplateRowUID.Contains(CurrSearchVal, StringComparison.InvariantCultureIgnoreCase))
.ToList();
}
totalCount = ListFilt.Count;
// gestione paginazione
ListRecords = ListFilt
.Skip(numRecord * (currPage - 1))
.Take(numRecord)
.ToList();
}
private async Task UploadFile(InputFileChangeEventArgs e)
{
// Proseguo solo se sono in interattivo (NO prerender pagina)
if (isInteractive)
{
if (EditRecord != null)
{
// init dizionari arg richiesta update
Dictionary<string, string> fileArgs = new Dictionary<string, string>();
// leggo il contenuto del PRIMO (singolo) file
IBrowserFile file = e.File;
// limite file size (al momento 10 MB)
var maxAllowedSize = 10 * 1024 * 1024;
using var stream = file.OpenReadStream(maxAllowedSize);
using var reader = new StreamReader(stream);
string rawContent = await reader.ReadToEndAsync();
// calcolo il nome del file trusted...
string trustedFileName = Path.GetRandomFileName();
EditRecord.FileResource = trustedFileName;
EditRecord.FileName = file.Name;
EditRecord.FileSize = rawContent.LongCount();
// salvo sul DB i dati (nome, nome sicuro, size...)
await TRService.UpdateFileDataAsync(EditRecord);
// parametri richiesta
fileArgs.Add("Mode", $"{(int)Egw.Window.Data.Enums.QuestionModes.PREVIEW}");
fileArgs.Add("SubMode", "2");
fileArgs.Add("FileName", $"{file.Name}");
fileArgs.Add("Height", "1200");
fileArgs.Add("Width", "1800");
//fileArgs.Add("Btl", rawContent);
fileArgs.Add("SerializedData", rawContent);
// invio!
CalcRequestDTO calcRequestDTO = new CalcRequestDTO();
calcRequestDTO.EnvType = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.BEAM;
calcRequestDTO.DictExec = fileArgs;
await ICService.CallRestPost($"{apiUrl}/{genericBasePath}", $"{calcTag}/{EditRecord.TemplateRowUID}", calcRequestDTO);
}
}
}
#endregion Private Methods
}
}