diff --git a/TIAM.Services/SignalRTags.cs b/TIAM.Services/SignalRTags.cs
index 398b13a0..684bfbd6 100644
--- a/TIAM.Services/SignalRTags.cs
+++ b/TIAM.Services/SignalRTags.cs
@@ -4,6 +4,8 @@ namespace TIAM.Services;
public class SignalRTags : AcSignalRTags
{
+ public const int None = 0;
+
public const int GetTransfer = 3;
public const int GetTransfers = 4;
public const int GetTransfersByContextId = 5;
diff --git a/TIAMSharedUI/Pages/User/MyServiceProviders.razor b/TIAMSharedUI/Pages/User/MyServiceProviders.razor
index fd04f340..d3ecd46e 100644
--- a/TIAMSharedUI/Pages/User/MyServiceProviders.razor
+++ b/TIAMSharedUI/Pages/User/MyServiceProviders.razor
@@ -68,7 +68,8 @@
PageSize="12"
KeyFieldName="Id"
- ValidationEnabled="false"
+ ValidationEnabled="false"
+ DetailRowDisplayMode="GridDetailRowDisplayMode.Always"
CustomizeEditModel="Grid_CustomizeEditModel"
EditMode="GridEditMode.EditRow"
KeyboardNavigationEnabled="true">
@@ -81,8 +82,14 @@
-
+
+
+
+ @{
+ @(((Company)context.DataItem).Profile.Address.AddressText)
+ }
+
@@ -141,19 +148,13 @@
}
- protected override async Task OnAfterRenderAsync(bool firstRender)
- {
- if (firstRender)
- await _gridCompany.StartEditRowAsync(0);
- }
-
void Grid_CustomizeEditModel(GridCustomizeEditModelEventArgs e)
{
if (e.IsNew)
{
var newEmployee = (Company)e.EditModel;
newEmployee.Name = "John";
- newEmployee.OwnerId = Guid.NewGuid();
+ newEmployee.AffiliateId = Guid.NewGuid();
}
}
diff --git a/TIAMSharedUI/Pages/User/SysAdmins/AddressDetailGridComponent.razor b/TIAMSharedUI/Pages/User/SysAdmins/AddressDetailGridComponent.razor
index 2d99bc54..3a092984 100644
--- a/TIAMSharedUI/Pages/User/SysAdmins/AddressDetailGridComponent.razor
+++ b/TIAMSharedUI/Pages/User/SysAdmins/AddressDetailGridComponent.razor
@@ -13,6 +13,7 @@
@using TIAMSharedUI.Pages.Components.EditComponents
@using TIAMWebApp.Shared.Application.Services
@using AyCode.Interfaces.Addresses
+@using AyCode.Core
@inject IServiceProviderDataService serviceProviderDataService
@inject IUserDataService userDataService
@inject ITransferDataService transferDataService
@@ -25,9 +26,9 @@
DataSource="DataSource"
Logger="_logger"
SignalRClient="AdminSignalRClient"
- OnEditModelSaving="DataItemSaving"
- OnDataItemDeleting="DataItemDeleting"
- OnDataItemChanged="DataItemChanged"
+ OnGridEditModelSaving="DataItemSaving"
+ OnGridItemDeleting="DataItemDeleting"
+ OnGridItemChanged="DataItemChanged"
PageSize="5"
AutoExpandAllGroupRows="true"
KeyboardNavigationEnabled="KeyboardNavigationEnabled"
@@ -37,15 +38,14 @@
ColumnResizeMode="GridColumnResizeMode.NextColumn"
ShowFilterRow="false">
-
-
+
+
-
-
+
+
-
-
+
@{
diff --git a/TIAMSharedUI/Pages/User/SysAdmins/AddressGridComponent.razor b/TIAMSharedUI/Pages/User/SysAdmins/AddressGridComponent.razor
index 0168bab4..532c8c49 100644
--- a/TIAMSharedUI/Pages/User/SysAdmins/AddressGridComponent.razor
+++ b/TIAMSharedUI/Pages/User/SysAdmins/AddressGridComponent.razor
@@ -11,6 +11,7 @@
@using TIAM.Entities.Addresses
@using TIAMSharedUI.Shared.Components.Grids
@using TIAMSharedUI.Pages.Components.EditComponents
+@using AyCode.Core
@inject IServiceProviderDataService serviceProviderDataService
@inject IUserDataService userDataService
@inject ITransferDataService transferDataService
@@ -30,11 +31,11 @@
ColumnResizeMode="GridColumnResizeMode.NextColumn"
ShowFilterRow="true">
-
-
+
+
-
-
+
+
diff --git a/TIAMSharedUI/Pages/User/SysAdmins/ManageServiceProviders.razor b/TIAMSharedUI/Pages/User/SysAdmins/ManageServiceProviders.razor
index ab7f7ea7..18247ea6 100644
--- a/TIAMSharedUI/Pages/User/SysAdmins/ManageServiceProviders.razor
+++ b/TIAMSharedUI/Pages/User/SysAdmins/ManageServiceProviders.razor
@@ -12,6 +12,7 @@
@using AyCode.Core.Extensions;
@using TIAM.Entities.Addresses
@using TIAMSharedUI.Shared.Components.Grids
+@using AyCode.Core
@layout AdminLayout
@inject IEnumerable LogWriters
@inject IStringLocalizer localizer
@@ -92,29 +93,29 @@
KeyFieldName="Id">
-
-
+
+
-
+
@{
var keyField = context.Value as Guid?;
- var keyItem = (Company)context.DataItem;
if (keyField.IsNullOrEmpty())
{
-
+
}
else
{
- @keyField
+ @keyField.Value.ToString("N")
}
}
-
+
-
+
+
@*
*@
diff --git a/TIAMSharedUI/Pages/User/SysAdmins/ManageTransfers.razor b/TIAMSharedUI/Pages/User/SysAdmins/ManageTransfers.razor
index 642b1107..c19e7045 100644
--- a/TIAMSharedUI/Pages/User/SysAdmins/ManageTransfers.razor
+++ b/TIAMSharedUI/Pages/User/SysAdmins/ManageTransfers.razor
@@ -18,6 +18,8 @@
@using AyCode.Core.Extensions
@using AyCode.Core.Consts
@using AyCode.Core
+@using AyCode.Core.Helpers
+@using TIAM.Entities.Emails
@layout AdminLayout
@inject IEnumerable LogWriters
@inject IStringLocalizer localizer
@@ -90,10 +92,10 @@
Logger="_logger"
SignalRClient="AdminSignalRClient"
OnDataSourceChanged="DataSourceChanged"
- OnDataItemChanging="DataSourceItemChanging"
- OnDataItemChanged="DataSourceItemChanged"
- OnDataItemDeleting="DataItemDeleting"
- OnEditModelSaving="DataItemSaving"
+ OnGridItemChanging="DataSourceItemChanging"
+ OnGridItemChanged="DataSourceItemChanged"
+ OnGridItemDeleting="DataItemDeleting"
+ OnGridEditModelSaving="DataItemSaving"
CustomizeElement="Grid_CustomizeElement"
CustomizeEditModel="Grid_CustomizeEditModel"
@@ -105,22 +107,22 @@
KeyFieldName="Id">
-
-
+
+
@{
- var idKeyField = ((Transfer)context.DataItem).Id as Guid?;
- var editUri = $"mytransfers/{idKeyField}";
+ var idKeyField = ((Transfer)context.DataItem).Id;
+ var editUri = $"mytransfers/{idKeyField:N}";
- @context.Value
+ @context.Value
}
-
+
-
+
@@ -140,7 +142,7 @@
TransferStatusModel keyField = Statuses.FirstOrDefault(x => x.StatusValue == Convert.ToInt16(context.Value));
string transferStatusText = keyField.StatusName;
- @transferStatusText
+ @transferStatusText
}
@@ -152,6 +154,31 @@
+
+
+
+
+
+
+
+
+
+ @System.Text.RegularExpressions.Regex.Replace((displayTextContext.Value as string)!, "<(.|\n)*?>", string.Empty)
+
+
+
+ @{
+ var value = ((EmailMessage)editTextContext.EditModel).Text;
+
+ }
+
+
+
+
+
@@ -383,7 +410,7 @@
{
_logger.Info("DataSourceItemChanged called");
- AppointmentModels.UpdateCollection(CreateAppointmentModel(args.DataItem), args.DataChangeMode == DataChangeMode.Remove);
+ AppointmentModels.UpdateCollection(CreateAppointmentModel(args.DataItem), args.TrackingState == TrackingState.Remove);
}
private void DataItemSaving(GridEditModelSavingEventArgs e)
diff --git a/TIAMSharedUI/Pages/User/SysAdmins/ProfileGridComponent.razor b/TIAMSharedUI/Pages/User/SysAdmins/ProfileGridComponent.razor
index 7fe56619..9250f1d7 100644
--- a/TIAMSharedUI/Pages/User/SysAdmins/ProfileGridComponent.razor
+++ b/TIAMSharedUI/Pages/User/SysAdmins/ProfileGridComponent.razor
@@ -5,6 +5,7 @@
@using TIAMWebApp.Shared.Application.Utility
@using AyCode.Services.Loggers
@using TIAM.Core.Loggers
+@using AyCode.Core
@inject IServiceProviderDataService serviceProviderDataService
@inject IUserDataService userDataService
@inject IEnumerable LogWriters
@@ -26,7 +27,7 @@
ShowFilterRow="true">
-
+
diff --git a/TIAMSharedUI/Pages/User/SysAdmins/ServiceProviderGridComponent.razor b/TIAMSharedUI/Pages/User/SysAdmins/ServiceProviderGridComponent.razor
index f3c1a5e1..54fd580e 100644
--- a/TIAMSharedUI/Pages/User/SysAdmins/ServiceProviderGridComponent.razor
+++ b/TIAMSharedUI/Pages/User/SysAdmins/ServiceProviderGridComponent.razor
@@ -8,6 +8,7 @@
@using TIAMWebApp.Shared.Application.Utility
@using AyCode.Services.Loggers
@using TIAM.Core.Loggers
+@using AyCode.Core
@inject IServiceProviderDataService ServiceProviderDataService
@inject IEnumerable LogWriters
@@ -27,8 +28,8 @@
ColumnResizeMode="GridColumnResizeMode.NextColumn"
ShowFilterRow="true">
-
-
+
+
diff --git a/TIAMSharedUI/Pages/User/SysAdmins/TransferToDriverGridComponent.razor b/TIAMSharedUI/Pages/User/SysAdmins/TransferToDriverGridComponent.razor
index 030b7790..9744e98a 100644
--- a/TIAMSharedUI/Pages/User/SysAdmins/TransferToDriverGridComponent.razor
+++ b/TIAMSharedUI/Pages/User/SysAdmins/TransferToDriverGridComponent.razor
@@ -10,6 +10,7 @@
@using TIAMWebApp.Shared.Application.Services
@using AyCode.Core.Enums
@using AyCode.Core.Extensions
+@using AyCode.Core
@inject IUserDataService UserDataService
@inject IEnumerable LogWriters
@inject AdminSignalRClient AdminSignalRClient;
@@ -20,9 +21,9 @@
DataSource="ParentData.TransferToDrivers"
Logger="_logger"
SignalRClient="AdminSignalRClient"
- OnEditModelSaving="DataItemSaving"
- OnDataItemDeleting="DataItemDeleting"
- OnDataItemChanged="DataItemChanged"
+ OnGridEditModelSaving="DataItemSaving"
+ OnGridItemDeleting="DataItemDeleting"
+ OnGridItemChanged="DataItemChanged"
PageSize="5"
AutoExpandAllGroupRows="true"
KeyboardNavigationEnabled="KeyboardNavigationEnabled"
@@ -34,7 +35,7 @@
ShowFilterRow="false">
-
+
@@ -58,7 +59,7 @@
private void DataItemChanged(GridDataItemChangedEventArgs args)
{
- ParentData.TransferToDrivers.UpdateCollection(args.DataItem, args.DataChangeMode == DataChangeMode.Remove);
+ ParentData.TransferToDrivers.UpdateCollection(args.DataItem, args.TrackingState == TrackingState.Remove);
OnTransferToDriverChanged.InvokeAsync(args.DataItem);
}
diff --git a/TIAMSharedUI/Pages/User/SysAdmins/UserProductMappingGridComponent.razor b/TIAMSharedUI/Pages/User/SysAdmins/UserProductMappingGridComponent.razor
index 66afd837..82e3b586 100644
--- a/TIAMSharedUI/Pages/User/SysAdmins/UserProductMappingGridComponent.razor
+++ b/TIAMSharedUI/Pages/User/SysAdmins/UserProductMappingGridComponent.razor
@@ -8,6 +8,7 @@
@using TIAM.Core.Loggers
@using AyCode.Core.Loggers
@using AyCode.Services.Loggers
+@using AyCode.Core
@inject IServiceProviderDataService ServiceProviderDataService
@inject IEnumerable LogWriters
@@ -26,7 +27,7 @@
ShowFilterRow="true">
-
+
diff --git a/TIAMSharedUI/Shared/Components/Grids/TiamGrid.cs b/TIAMSharedUI/Shared/Components/Grids/TiamGrid.cs
index d159ee18..f36fb4b7 100644
--- a/TIAMSharedUI/Shared/Components/Grids/TiamGrid.cs
+++ b/TIAMSharedUI/Shared/Components/Grids/TiamGrid.cs
@@ -8,41 +8,22 @@ using AyCode.Services.SignalRs;
using AyCode.Utils.Extensions;
using DevExpress.Blazor;
using Microsoft.AspNetCore.Components;
+using TIAM.Services;
using TIAMWebApp.Shared.Application.Services;
using TIAMWebApp.Shared.Application.Utility;
namespace TIAMSharedUI.Shared.Components.Grids
{
- public class GridDataItemChangingEventArgs : GridDataItemChangedEventArgs where TDataItem : class, IId
- {
- internal GridDataItemChangingEventArgs(TiamGrid grid, TDataItem dataItem, DataChangeMode dataChangeMode) : base(grid, dataItem, dataChangeMode)
- { }
-
- public bool IsCanceled { get; set; }
- }
-
- public class GridDataItemChangedEventArgs where TDataItem : class, IId
- {
- internal GridDataItemChangedEventArgs(TiamGrid grid, TDataItem dataItem, DataChangeMode dataChangeMode)
- {
- Grid = grid;
- DataItem = dataItem;
- DataChangeMode = dataChangeMode;
- }
-
- public TiamGrid Grid { get; }
- public TDataItem DataItem { get; }
- public DataChangeMode DataChangeMode { get; }
- }
-
public class TiamGrid : DxGrid where TDataItem : class, IId
{
protected bool IsFirstInitializeParameters;
- private IList _dataSource = null!;
+ private SignalRDataSource _dataSource = null!;
+ private IList _dataSourceParam = [];
private string _gridLogName;
public TiamGrid() : base()
- { }
+ {
+ }
[Parameter] public LoggerClient Logger { get; set; }
[Parameter] public string GridName { get; set; }
@@ -56,14 +37,19 @@ namespace TIAMSharedUI.Shared.Components.Grids
[Parameter] public int RemoveMessageTag { get; set; }
protected new EventCallback DataItemDeleting { get; set; }
- [Parameter] public EventCallback OnDataItemDeleting{ get; set; }
+ [Parameter] public EventCallback OnGridItemDeleting { get; set; }
protected new EventCallback EditModelSaving { get; set; }
- [Parameter] public EventCallback OnEditModelSaving { get; set; }
+ [Parameter] public EventCallback OnGridEditModelSaving { get; set; }
[Parameter] public EventCallback> OnDataSourceChanged { get; set; }
- [Parameter] public EventCallback> OnDataItemChanging { get; set; }
- [Parameter] public EventCallback> OnDataItemChanged { get; set; }
+ [Parameter] public EventCallback> OnGridItemChanging { get; set; }
+
+ ///
+ /// After the server has responded!
+ ///
+ [Parameter]
+ public EventCallback> OnGridItemChanged { get; set; }
[Parameter]
[DefaultValue(null)]
@@ -80,28 +66,49 @@ namespace TIAMSharedUI.Shared.Components.Grids
return _dataSource!;
}
- set
- {
- if (value == null) throw new ArgumentNullException(nameof(value));
-
- var equals = Equals(_dataSource, value);
-
- _dataSource = value;
- Data = _dataSource;
-
- if (!equals) OnDataSourceChanged.InvokeAsync(_dataSource);
- }
+ set => _dataSourceParam = value;
}
- protected override void OnInitialized()
+ protected override async Task OnInitializedAsync()
{
if (Logger == null)
throw new NullReferenceException($"[{GetType().Name}] Logger == null");
if (SignalRClient == null)
+ {
Logger.Error($"[{GetType().Name}] SignalRClient == null");
+ throw new NullReferenceException($"[{GetType().Name}] SignalRClient == null");
+ }
- base.OnInitialized();
+ var crudTags = new SignalRCrudTags(GetAllMessageTag, SignalRTags.None, AddMessageTag, UpdateMessageTag, RemoveMessageTag);
+ _dataSource = new SignalRDataSource(SignalRClient, crudTags, ContextId);
+
+ Data = _dataSource;
+
+ _dataSource.OnDataSourceLoaded += OnDataSourceLoaded;
+ _dataSource.OnDataSourceItemChanged += OnDataSourceItemChanged;
+
+ await base.OnInitializedAsync();
+ }
+
+ private async Task OnDataSourceItemChanged(ItemChangedEventArgs args)
+ {
+ if (args.TrackingState is TrackingState.GetAll or TrackingState.None) return;
+
+ Logger.Debug($"{_gridLogName} OnDataSourceItemChanged; trackingState: {args.TrackingState}");
+
+ var changedEventArgs = new GridDataItemChangedEventArgs(this, args.Item, args.TrackingState);
+ await OnGridItemChanged.InvokeAsync(changedEventArgs);
+
+ await InvokeAsync(StateHasChanged);
+ }
+
+ private Task OnDataSourceLoaded()
+ {
+ Logger.Debug($"{_gridLogName} OnDataSourceLoaded");
+
+ Reload();
+ return OnDataSourceChanged.InvokeAsync(_dataSource);
}
protected override async Task OnAfterRenderAsync(bool firstRender)
@@ -110,35 +117,35 @@ namespace TIAMSharedUI.Shared.Components.Grids
if (firstRender)
{
- if (_dataSource == null || _dataSource.Count == 0) RefreshDataSourceAsync().Forget();
+ if (_dataSourceParam.Count > 0) await _dataSource.LoadDataSource(_dataSourceParam);
+ else _dataSource.LoadDataSourceAsync(true).Forget();
}
}
- public Task AddDataItem(TDataItem dataItem) => AddDataItem(dataItem, AddMessageTag);
- public Task AddDataItem(TDataItem dataItem, int messageTag) => PostDataToServerAsync(dataItem, messageTag, DataChangeMode.Add);
-
- public Task UpdateDataItem(TDataItem dataItem) => UpdateDataItem(dataItem, UpdateMessageTag);
- public Task UpdateDataItem(TDataItem dataItem, int messageTag) => PostDataToServerAsync(dataItem, messageTag, DataChangeMode.Update);
-
- public Task RemoveDataItem(TDataItem dataItem) => RemoveDataItem(dataItem, RemoveMessageTag);
- public Task RemoveDataItem(TDataItem dataItem, int messageTag) => PostDataToServerAsync(dataItem, messageTag, DataChangeMode.Remove);
-
- public Task RemoveDataItem(Guid id) => RemoveDataItem(id, RemoveMessageTag);
- public Task RemoveDataItem(Guid id, int messageTag)
+ public Task AddDataItem(TDataItem dataItem)
{
- var dataItem = _dataSource.FirstOrDefault(x => x.Id == id);
+ if (dataItem.Id.IsNullOrEmpty()) dataItem.Id = Guid.NewGuid();
+ return _dataSource.Add(dataItem, true);
+ }
- return dataItem == null ? Task.CompletedTask : RemoveDataItem(dataItem);
+ public Task AddDataItemAsync(TDataItem dataItem)
+ {
+ if (dataItem.Id.IsNullOrEmpty()) dataItem.Id = Guid.NewGuid();
+ _dataSource.Add(dataItem);
+
+ return SaveChangesToServerAsync();
}
private async Task OnItemSaving(GridEditModelSavingEventArgs e)
{
var dataItem = (e.EditModel as TDataItem)!;
- var logText = e.IsNew ? "add" : "update";
- Logger.Info($"{_gridLogName} OnItemSaving {logText}; Id: {dataItem.Id}");
+ if (e.IsNew && dataItem.Id.IsNullOrEmpty()) dataItem.Id = Guid.NewGuid();
- await OnEditModelSaving.InvokeAsync(e);
+ var logText = e.IsNew ? "add" : "update";
+ Logger.Debug($"{_gridLogName} OnItemSaving {logText}; Id: {dataItem.Id}");
+
+ await OnGridEditModelSaving.InvokeAsync(e);
if (e.Cancel)
{
@@ -146,79 +153,70 @@ namespace TIAMSharedUI.Shared.Components.Grids
return;
}
- if (e.IsNew) await AddDataItem(dataItem);
- else await UpdateDataItem(dataItem);
+ if (e.IsNew) await AddDataItemAsync(dataItem);
+ else await UpdateDataItemAsync(dataItem);
+ }
+
+ private Task SaveChangesToServerAsync()
+ {
+ try
+ {
+ return _dataSource.SaveChangesAsync();
+ }
+ catch (Exception ex)
+ {
+ Logger.Error($"{_gridLogName} SaveChangesToServerAsync->SaveChangesAsync error!", ex);
+ }
+
+ return Task.CompletedTask;
+ }
+
+ private async Task SaveChangesToServer()
+ {
+ var result = false;
+
+ try
+ {
+ var unsavedItems = await _dataSource.SaveChanges();
+
+ if ((result = unsavedItems.Count == 0) == false)
+ Logger.Error($"{_gridLogName} SaveChangesToServer->SaveChanges error! unsavedCount: {unsavedItems.Count}");
+ }
+ catch (Exception ex)
+ {
+ Logger.Error($"{_gridLogName} OnItemSaving", ex);
+ }
+
+ return result;
}
private async Task OnItemDeleting(GridDataItemDeletingEventArgs e)
{
- Logger.Info($"{_gridLogName} OnItemDeleting");
+ Logger.Debug($"{_gridLogName} OnItemDeleting");
- await OnDataItemDeleting.InvokeAsync(e);
+ await OnGridItemDeleting.InvokeAsync(e);
if (e.Cancel)
{
Logger.Debug($"{_gridLogName} OnItemDeleting canceled");
return;
}
-
+
var dataItem = (e.DataItem as TDataItem)!;
await RemoveDataItem(dataItem);
}
- public virtual Task RefreshDataSourceAsync()
+ private void OnCustomizeElement(GridCustomizeElementEventArgs e)
{
- if (GetAllMessageTag == 0) return Task.CompletedTask;
-
- Logger.Info($"{_gridLogName} RefreshDataSourceAsync called");
-
- return SignalRClient.GetAllAsync>(GetAllMessageTag, ContextId, response =>
+ if (e.ElementType == GridElementType.DetailCell)
{
- if (response.Status == SignalResponseStatus.Error)
- return;
-
- BeginUpdate();
- DataSource = response.ResponseData ?? [];
- EndUpdate();
-
- InvokeAsync(StateHasChanged).Forget();
- });
- }
-
- protected virtual async Task PostDataToServerAsync(TDataItem dataItem, int messageTag, DataChangeMode dataChangeMode)
- {
- var eventArgs = new GridDataItemChangingEventArgs(this, dataItem, dataChangeMode);
- await OnDataItemChanging.InvokeAsync(eventArgs);
-
- if (eventArgs.IsCanceled)
- {
- Logger.Debug($"{_gridLogName} OnDataItemChanging canceled");
- return;
+ e.Style = "padding: 0.5rem; opacity: 0.75";
}
-
- if (messageTag == 0) return;
-
- Logger.Info($"{_gridLogName} PostDataToServerAsync called; transferId " + dataItem.Id);
-
- if (dataItem.Id.IsNullOrEmpty()) dataItem.Id = Guid.NewGuid();
-
- _dataSource.UpdateCollection(dataItem, dataChangeMode == DataChangeMode.Remove); //egyből látszódik a változás a grid-ben, nem csak a callback lefutásakor! felhasználóbarátabb... - J.
-
- SignalRClient.PostDataAsync(messageTag, dataItem, async repsonse =>
+ else if (false && e.ElementType == GridElementType.DataCell && e.Column.Name == nameof(IId.Id))
{
- if (repsonse.Status != SignalResponseStatus.Success || repsonse.ResponseData == null)
- {
- RefreshDataSourceAsync().Forget();
- return;
- }
-
- _dataSource.UpdateCollection(repsonse.ResponseData, dataChangeMode == DataChangeMode.Remove);
-
- await OnDataItemChanged.InvokeAsync(eventArgs);
- InvokeAsync(StateHasChanged).Forget();
- }).Forget();
-
- //transfer = await devAdminSignalClient.PostDataAsync(SignalRTags.UpdateTransferAsync, transfer);
+ e.Column.Visible = AcDomain.IsDeveloperVersion;
+ e.Column.ShowInColumnChooser = AcDomain.IsDeveloperVersion;
+ }
}
protected override Task SetParametersAsyncCore(ParameterView parameters)
@@ -227,7 +225,9 @@ namespace TIAMSharedUI.Shared.Components.Grids
{
base.DataItemDeleting = EventCallback.Factory.Create(this, OnItemDeleting);
base.EditModelSaving = EventCallback.Factory.Create(this, OnItemSaving);
-
+
+ CustomizeElement += OnCustomizeElement;
+
//ShowFilterRow = true;
//PageSize = 4;
//ShowGroupPanel = true;
@@ -235,7 +235,6 @@ namespace TIAMSharedUI.Shared.Components.Grids
TextWrapEnabled = false;
- //var columns = GetColumns();
//var dataColumns = GetDataColumns();
//var idColumn = dataColumns.FirstOrDefault(x => x.FieldName == nameof(IId.Id));
@@ -251,6 +250,7 @@ namespace TIAMSharedUI.Shared.Components.Grids
return base.SetParametersAsyncCore(parameters);
}
+
protected override void OnParametersSet()
{
base.OnParametersSet();
@@ -260,5 +260,54 @@ namespace TIAMSharedUI.Shared.Components.Grids
_gridLogName = $"[{GridName}]";
}
+
+ //public Task AddDataItem(TDataItem dataItem, int messageTag) => PostDataToServerAsync(dataItem, messageTag, TrackingState.Add);
+
+ public Task UpdateDataItem(TDataItem dataItem) => _dataSource.Update(dataItem, true);
+
+ public Task UpdateDataItemAsync(TDataItem dataItem)
+ {
+ _dataSource.Update(dataItem, false);
+ return SaveChangesToServerAsync();
+ }
+ //public Task UpdateDataItem(TDataItem dataItem, int messageTag) => PostDataToServerAsync(dataItem, messageTag, TrackingState.Update);
+
+ public Task AddOrUpdateDataItem(TDataItem dataItem) => _dataSource.AddOrUpdate(dataItem, true);
+
+ public Task RemoveDataItem(TDataItem dataItem) => _dataSource.Remove(dataItem, true);
+ //public Task RemoveDataItem(TDataItem dataItem, int messageTag) => PostDataToServerAsync(dataItem, messageTag, TrackingState.Remove);
+
+ public Task RemoveDataItem(Guid id) => RemoveDataItem(id, RemoveMessageTag);
+
+ public Task RemoveDataItem(Guid id, int messageTag)
+ {
+ var dataItem = _dataSource.FirstOrDefault(x => x.Id == id);
+ if (dataItem == null) return Task.CompletedTask;
+
+ return _dataSource.Remove(dataItem, true);
+ }
+ }
+
+ public class GridDataItemChangingEventArgs : GridDataItemChangedEventArgs where TDataItem : class, IId
+ {
+ internal GridDataItemChangingEventArgs(TiamGrid grid, TDataItem dataItem, TrackingState trackingState) : base(grid, dataItem, trackingState)
+ {
+ }
+
+ public bool IsCanceled { get; set; }
+ }
+
+ public class GridDataItemChangedEventArgs where TDataItem : class, IId
+ {
+ internal GridDataItemChangedEventArgs(TiamGrid grid, TDataItem dataItem, TrackingState trackingState)
+ {
+ Grid = grid;
+ DataItem = dataItem;
+ TrackingState = trackingState;
+ }
+
+ public TiamGrid Grid { get; }
+ public TDataItem DataItem { get; }
+ public TrackingState TrackingState { get; }
}
}
diff --git a/TIAMWebApp/Server/Controllers/ServiceProviderAPIController.cs b/TIAMWebApp/Server/Controllers/ServiceProviderAPIController.cs
index bc7ee8b3..f1f8419d 100644
--- a/TIAMWebApp/Server/Controllers/ServiceProviderAPIController.cs
+++ b/TIAMWebApp/Server/Controllers/ServiceProviderAPIController.cs
@@ -30,9 +30,9 @@ namespace TIAMWebApp.Server.Controllers
[NonAction]
[ApiExplorerSettings(IgnoreApi = true)]
- private async Task CompanyDataChanging(Company company, DataChangeMode dataChangeMode)
+ private async Task CompanyDataChanging(Company company, TrackingState trackingState)
{
- var logText = $"[{dataChangeMode.ToString().ToUpper()}] CompanyDataChanging called; Id: {company.Id}; OwnerId: {company.OwnerId}; Name: {company.Name}";
+ var logText = $"[{trackingState.ToString().ToUpper()}] CompanyDataChanging called; Id: {company.Id}; OwnerId: {company.OwnerId}; Name: {company.Name}";
if (company.Name.IsNullOrEmpty())
{
@@ -42,9 +42,9 @@ namespace TIAMWebApp.Server.Controllers
_logger.Info(logText);
- switch (dataChangeMode)
+ switch (trackingState)
{
- case DataChangeMode.Add:
+ case TrackingState.Add:
if (company.Id.IsNullOrEmpty()) company.Id = Guid.NewGuid();
//if (company.OwnerId.IsNullOrEmpty()) company.OwnerId = Guid.Parse("540271F6-C604-4C16-8160-D5A7CAFEDF00"); //TESZT - J.
@@ -54,13 +54,13 @@ namespace TIAMWebApp.Server.Controllers
return await adminDal.CreateServiceProviderAsync(company);
- case DataChangeMode.Update:
+ case TrackingState.Update:
return await adminDal.UpdateCompanyAsync(company);
- case DataChangeMode.Remove:
+ case TrackingState.Remove:
return await adminDal.RemoveCompanyAsync(company);
default:
- throw new ArgumentOutOfRangeException(nameof(dataChangeMode), dataChangeMode, null);
+ throw new ArgumentOutOfRangeException(nameof(trackingState), trackingState, null);
}
}
@@ -68,21 +68,21 @@ namespace TIAMWebApp.Server.Controllers
[ApiExplorerSettings(IgnoreApi = true)]
[SignalR(SignalRTags.AddCompany)]
public async Task AddCompanyAsync(Company company)
- => await CompanyDataChanging(company, DataChangeMode.Add) ? company.ToJson() : string.Empty;
+ => await CompanyDataChanging(company, TrackingState.Add) ? company.ToJson() : string.Empty;
[AllowAnonymous]
[HttpPost]
[Route(APIUrls.UpdateServiceProviderRouteName)]
[SignalR(SignalRTags.UpdateCompany)]
public async Task UpdateServiceProvider(Company company)
- => await CompanyDataChanging(company, DataChangeMode.Update) ? company.ToJson() : string.Empty;
+ => await CompanyDataChanging(company, TrackingState.Update) ? company.ToJson() : string.Empty;
[AllowAnonymous]
[HttpPost]
[Route(APIUrls.RemoveServiceProviderRouteName)]
[SignalR(SignalRTags.RemoveCompany)]
public async Task RemoveServiceProvider(Company company)
- => await CompanyDataChanging(company, DataChangeMode.Remove) ? company.ToJson() : string.Empty;
+ => await CompanyDataChanging(company, TrackingState.Remove) ? company.ToJson() : string.Empty;
//15.
[AllowAnonymous]
@@ -220,9 +220,9 @@ namespace TIAMWebApp.Server.Controllers
[NonAction]
[ApiExplorerSettings(IgnoreApi = true)]
- private async Task CarDataChanging(Car car, DataChangeMode dataChangeMode)
+ private async Task CarDataChanging(Car car, TrackingState trackingState)
{
- var logText = $"[{dataChangeMode.ToString().ToUpper()}] CarDataChanging called; Id: {car.Id}; OwnerId: {car.UserProductMappingId}; LicensePlate: {car.LicencePlate}";
+ var logText = $"[{trackingState.ToString().ToUpper()}] CarDataChanging called; Id: {car.Id}; OwnerId: {car.UserProductMappingId}; LicensePlate: {car.LicencePlate}";
if (car.UserProductMappingId.IsNullOrEmpty() || car.LicencePlate.IsNullOrWhiteSpace())
{
@@ -232,19 +232,19 @@ namespace TIAMWebApp.Server.Controllers
_logger.Info(logText);
- switch (dataChangeMode)
+ switch (trackingState)
{
- case DataChangeMode.Add:
+ case TrackingState.Add:
if (car.Id.IsNullOrEmpty()) car.Id = Guid.NewGuid();
return await adminDal.AddCarAsync(car);
- case DataChangeMode.Update:
+ case TrackingState.Update:
return await adminDal.UpdateCarAsync(car);
- case DataChangeMode.Remove:
+ case TrackingState.Remove:
return await adminDal.RemoveCarAsync(car);
default:
- throw new ArgumentOutOfRangeException(nameof(dataChangeMode), dataChangeMode, null);
+ throw new ArgumentOutOfRangeException(nameof(trackingState), trackingState, null);
}
}
@@ -255,7 +255,7 @@ namespace TIAMWebApp.Server.Controllers
[EndpointSummary("Create car")]
[SignalR(SignalRTags.CreateCar)]
public async Task CreateCar(Car car)
- => await CarDataChanging(car, DataChangeMode.Add) ? Ok(car) : BadRequest("Invalid request");
+ => await CarDataChanging(car, TrackingState.Add) ? Ok(car) : BadRequest("Invalid request");
[AllowAnonymous]
[HttpPost]
@@ -264,7 +264,7 @@ namespace TIAMWebApp.Server.Controllers
[EndpointSummary("Update car")]
[SignalR(SignalRTags.UpdateCar)]
public async Task UpdateCar(Car car)
- => await CarDataChanging(car, DataChangeMode.Update) ? Ok(car) : BadRequest("Invalid request");
+ => await CarDataChanging(car, TrackingState.Update) ? Ok(car) : BadRequest("Invalid request");
[AllowAnonymous]
[HttpPost]
@@ -273,7 +273,7 @@ namespace TIAMWebApp.Server.Controllers
[EndpointSummary("Delete car")]
[SignalR(SignalRTags.DeleteCar)]
public async Task DeleteCar(Car car)
- => await CarDataChanging(car, DataChangeMode.Remove) ? Ok(car) : BadRequest("Invalid request");
+ => await CarDataChanging(car, TrackingState.Remove) ? Ok(car) : BadRequest("Invalid request");
[HttpPost]
[Route(APIUrls.AddProductRouteName)]
diff --git a/TIAMWebApp/Server/Services/DevAdminSignalRhub.cs b/TIAMWebApp/Server/Services/DevAdminSignalRhub.cs
index 07d738f7..be9b565f 100644
--- a/TIAMWebApp/Server/Services/DevAdminSignalRhub.cs
+++ b/TIAMWebApp/Server/Services/DevAdminSignalRhub.cs
@@ -14,6 +14,10 @@ using System.Runtime.CompilerServices;
using MessagePack;
using TIAM.Entities.Addresses;
using TIAM.Entities.Profiles;
+using Microsoft.AspNetCore.Hosting;
+using System.Collections.Generic;
+using TIAM.Entities.Emails;
+using TIAM.Services.Server;
namespace TIAMWebApp.Server.Services;
@@ -57,7 +61,11 @@ public class DynamicMethodCallModel where TAttribute : TagAttribute
public object InstanceObject { get; init; }
public ConcurrentDictionary> MethodsByMessageTag { get; init; } = new();
- public DynamicMethodCallModel(Type instanceObjectType) : this(Activator.CreateInstance(instanceObjectType)!)
+
+ public DynamicMethodCallModel(Type instanceObjectType) : this(instanceObjectType, null!)
+ { }
+
+ public DynamicMethodCallModel(Type instanceObjectType, params object[] constructorParams) : this(Activator.CreateInstance(instanceObjectType, constructorParams)!)
{ }
public DynamicMethodCallModel(object instanceObject)
@@ -75,7 +83,7 @@ public class DynamicMethodCallModel where TAttribute : TagAttribute
public class DevAdminSignalRHub : Hub, IAcSignalRHubServer
{
- private readonly List> _dynamicMethodCallModels = new();
+ private readonly List> _dynamicMethodCallModels = [];
private readonly TIAM.Core.Loggers.Logger _logger;
private readonly AdminDal _adminDal;
@@ -93,6 +101,7 @@ public class DevAdminSignalRHub : Hub, IAcSignalRHubServe
_dynamicMethodCallModels.Add(new DynamicMethodCallModel(serviceProviderApiController));
_dynamicMethodCallModels.Add(new DynamicMethodCallModel(transferDataApiController));
_dynamicMethodCallModels.Add(new DynamicMethodCallModel(messageApiController));
+ //_dynamicMethodCallModels.Add(new DynamicMethodCallModel(typeof(AdminDal)));
}
diff --git a/TIAMWebApp/Shared/Services/AcSignalRClientBase.cs b/TIAMWebApp/Shared/Services/AcSignalRClientBase.cs
index cefac36e..d531c604 100644
--- a/TIAMWebApp/Shared/Services/AcSignalRClientBase.cs
+++ b/TIAMWebApp/Shared/Services/AcSignalRClientBase.cs
@@ -2,6 +2,7 @@
using AyCode.Core;
using AyCode.Core.Extensions;
using AyCode.Core.Helpers;
+using AyCode.Core.Interfaces;
using AyCode.Services.Loggers;
using AyCode.Services.SignalRs;
using MessagePack.Resolvers;
@@ -75,9 +76,6 @@ namespace TIAMWebApp.Shared.Application.Services
public virtual Task SendMessageToServerAsync(int messageTag)
=> SendMessageToServerAsync(messageTag, null, AcDomain.NextUniqueInt32);
- public virtual Task SendMessageToServerAsync(int messageTag, int requestId)
- => SendMessageToServerAsync(messageTag, null, requestId);
-
public virtual async Task SendMessageToServerAsync(int messageTag, ISignalRMessage? message, int? requestId)
{
Logger.DebugConditional($"Client SendMessageToServerAsync; {nameof(messageTag)}: {messageTag}; {nameof(requestId)}: {requestId};");
@@ -89,18 +87,21 @@ namespace TIAMWebApp.Shared.Application.Services
}
#region CRUD
- public virtual Task GetByIdAsync(int messageTag, Guid id) where TResponse : class
- => SendMessageToServerAsync(messageTag, new SignalPostJsonDataMessage(new IdMessage(id)), AcDomain.NextUniqueInt32);
+ public virtual Task GetByIdAsync(int messageTag, Guid id) where TResponseData : class
+ => SendMessageToServerAsync(messageTag, new SignalPostJsonDataMessage(new IdMessage(id)), AcDomain.NextUniqueInt32);
public virtual Task GetByIdAsync(int messageTag, Guid id, Action> responseCallback)
=> SendMessageToServerAsync(messageTag, new SignalPostJsonDataMessage(new IdMessage(id)), responseCallback);
- public virtual Task GetAllAsync(int messageTag) where TResponse : class
- => SendMessageToServerAsync(messageTag);
+ public virtual Task GetAllAsync(int messageTag) where TResponseData : class
+ => SendMessageToServerAsync(messageTag);
public virtual Task GetAllAsync(int messageTag, Action> responseCallback)
=> SendMessageToServerAsync(messageTag, null, responseCallback);
public virtual Task GetAllAsync(int messageTag, Guid? contextId, Action> responseCallback)
=> SendMessageToServerAsync(messageTag, (contextId.IsNullOrEmpty() ? null : new SignalPostJsonDataMessage(new IdMessage(contextId.Value))), responseCallback);
+ public virtual Task GetAllAsync(int messageTag, Guid? contextId) where TResponseData : class
+ => SendMessageToServerAsync(messageTag, contextId.IsNullOrEmpty() ? null : new SignalPostJsonDataMessage(new IdMessage(contextId.Value)), AcDomain.NextUniqueInt32);
+
public virtual Task PostDataAsync(int messageTag, TPostData postData) where TPostData : class
=> SendMessageToServerAsync(messageTag, new SignalPostJsonDataMessage(postData), AcDomain.NextUniqueInt32);
public virtual Task PostDataAsync(int messageTag, TPostData postData, Action> responseCallback) where TPostData : class
@@ -114,7 +115,7 @@ namespace TIAMWebApp.Shared.Application.Services
public virtual Task SendMessageToServerAsync(int messageTag, ISignalRMessage? message) where TResponse : class
=> SendMessageToServerAsync(messageTag, message, AcDomain.NextUniqueInt32);
- public virtual async Task SendMessageToServerAsync(int messageTag, ISignalRMessage? message, int requestId) where TResponse : class
+ protected virtual async Task SendMessageToServerAsync(int messageTag, ISignalRMessage? message, int requestId) where TResponse : class
{
Logger.DebugConditional($"Client SendMessageToServerAsync; {nameof(messageTag)}: {messageTag}; {nameof(requestId)}: {requestId};");
diff --git a/TIAMWebApp/Shared/Utility/SignalRDataSource.cs b/TIAMWebApp/Shared/Utility/SignalRDataSource.cs
new file mode 100644
index 00000000..fc9a14f3
--- /dev/null
+++ b/TIAMWebApp/Shared/Utility/SignalRDataSource.cs
@@ -0,0 +1,1034 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.Serialization;
+using System.Runtime.Serialization.Formatters.Binary;
+using AyCode.Core.Enums;
+using AyCode.Core.Extensions;
+using AyCode.Core.Helpers;
+using AyCode.Core.Interfaces;
+using AyCode.Services.SignalRs;
+using TIAM.Services;
+using TIAMWebApp.Shared.Application.Services;
+
+namespace TIAMWebApp.Shared.Application.Utility
+{
+ public class TrackingItem(TrackingState trackingState, T currentValue, T? originalValue = null) where T : class, IId
+ {
+ public TrackingState TrackingState { get; internal set; } = trackingState;
+ public T CurrentValue { get; internal set; } = currentValue;
+ public T? OriginalValue { get; init; } = originalValue;
+
+ internal TrackingItem UpdateItem(TrackingState trackingState, T newValue)
+ {
+ CurrentValue = newValue;
+
+ if (TrackingState != TrackingState.Add)
+ TrackingState = trackingState;
+
+ return this;
+ }
+ }
+
+
+ public class ChangeTracking /*: IEnumerable>*/ where T : class, IId
+ {
+ private readonly List> _trackingItems = []; //TODO: Dictionary... - J.
+
+ internal TrackingItem? AddTrackingItem(TrackingState trackingState, T newValue, T? originalValue = null)
+ {
+ if (newValue.Id.IsNullOrEmpty()) throw new ArgumentNullException(nameof(newValue), $@"currentValue.Id.IsNullOrEmpty()");
+
+ var itemIndex = _trackingItems.FindIndex(x => x.CurrentValue.Id == newValue.Id);
+ TrackingItem? trackingItem = null;
+
+ if (itemIndex > -1)
+ {
+ trackingItem = _trackingItems[itemIndex];
+
+ if (trackingState == TrackingState.Remove && trackingItem.TrackingState == TrackingState.Add)
+ {
+ _trackingItems.RemoveAt(itemIndex);
+ return null;
+ }
+
+ return trackingItem.UpdateItem(trackingState, newValue);
+
+ }
+
+ if (originalValue != null && Equals(newValue, originalValue))
+ originalValue = TrackingItemHelpers.JsonClone(originalValue);
+
+ trackingItem = new TrackingItem(trackingState, newValue, originalValue);
+ _trackingItems.Add(trackingItem);
+
+ return trackingItem;
+ }
+
+ public int Count => _trackingItems.Count;
+ internal void Clear() => _trackingItems.Clear();
+ public List> ToList() => _trackingItems.ToList();
+
+ public bool TryGetTrackingItem(Guid id, [NotNullWhen(true)] out TrackingItem? trackingItem)
+ {
+ trackingItem = _trackingItems.FirstOrDefault(x => x.CurrentValue.Id == id);
+ return trackingItem != null;
+ }
+
+ internal void Remove(TrackingItem trackingItem) => _trackingItems.Remove(trackingItem);
+
+ //public IEnumerator> GetEnumerator()
+ //{
+ // return _trackingItems.GetEnumerator();
+ //}
+
+ //IEnumerator IEnumerable.GetEnumerator()
+ //{
+ // return GetEnumerator();
+ //}
+ }
+
+
+
+ [Serializable]
+ [DebuggerDisplay("Count = {Count}")]
+ public class SignalRDataSource : IList, IList, IReadOnlyList where T : class, IId
+ {
+ private readonly object _syncRoot = new();
+
+ protected readonly List InnerList = []; //TODO: Dictionary??? - J.
+ protected readonly ChangeTracking TrackingItems = new();
+
+ protected readonly Guid? ContextId;
+ public AcSignalRClientBase SignalRClient;
+ protected readonly SignalRCrudTags SignalRCrudTags;
+
+ public Func, Task>? OnDataSourceItemChanged;
+ public Func? OnDataSourceLoaded;
+
+ public SignalRDataSource(AcSignalRClientBase signalRClient, SignalRCrudTags signalRCrudTags, Guid? contextId = null)
+ {
+ ContextId = contextId;
+
+ SignalRCrudTags = signalRCrudTags;
+ SignalRClient = signalRClient;
+ }
+
+ public bool IsSynchronized => true;
+ public object SyncRoot => _syncRoot;
+ public bool IsFixedSize => false;
+
+ ///
+ /// GetAllMessageTag
+ ///
+ ///
+ ///
+ public async Task LoadDataSource(bool clearChangeTracking = true)
+ {
+ if (SignalRCrudTags.GetAllMessageTag == SignalRTags.None) throw new ArgumentException($"SignalRCrudTags.GetAllMessageTag == SignalRTags.None");
+
+ var resultList = (await SignalRClient.GetAllAsync>(SignalRCrudTags.GetAllMessageTag, ContextId)) ?? throw new NullReferenceException();
+
+ await LoadDataSource(resultList);
+ }
+
+ public Task LoadDataSourceAsync(bool clearChangeTracking = true)
+ {
+ if (SignalRCrudTags.GetAllMessageTag == SignalRTags.None) throw new ArgumentException($"SignalRCrudTags.GetAllMessageTag == SignalRTags.None");
+
+ return SignalRClient.GetAllAsync>(SignalRCrudTags.GetAllMessageTag, ContextId, result=>
+ {
+ if (result.Status != SignalResponseStatus.Success || result.ResponseData == null)
+ throw new NullReferenceException($"LoadDataSourceAsync; result.Status != SignalResponseStatus.Success || result.ResponseData == null; Status: {SignalResponseStatus.Success}");
+
+ LoadDataSource(result.ResponseData).Forget();
+ });
+ }
+
+ public async Task LoadDataSource(IList fromSource, bool clearChangeTracking = true)
+ {
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ Clear(clearChangeTracking);
+
+ foreach (var item in fromSource)
+ {
+ InnerList.Add(item);
+
+ var eventArgs = new ItemChangedEventArgs(item, TrackingState.GetAll);
+ if (OnDataSourceItemChanged != null) await OnDataSourceItemChanged.Invoke(eventArgs);
+ }
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+
+ if (OnDataSourceLoaded != null) await OnDataSourceLoaded.Invoke();
+ }
+
+ public async Task LoadItem(Guid id)
+ {
+ if (SignalRCrudTags.GetItemMessageTag == SignalRTags.None) throw new ArgumentException($"SignalRCrudTags.GetItemMessageTag == SignalRTags.None");
+
+ T? resultitem = null;
+
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ resultitem = await SignalRClient.GetByIdAsync(SignalRCrudTags.GetItemMessageTag, id);
+ if (resultitem == null) return null;
+
+ if (TryGetIndex(id, out var index)) InnerList[index] = resultitem;
+ else InnerList.Add(resultitem);
+
+ var eventArgs = new ItemChangedEventArgs(resultitem, TrackingState.Get);
+ if (OnDataSourceItemChanged != null) await OnDataSourceItemChanged.Invoke(eventArgs);
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+
+ return resultitem;
+ }
+
+ ///
+ /// set: UpdateMessageTag
+ ///
+ ///
+ ///
+ ///
+ public T this[int index]
+ {
+ get
+ {
+ if ((uint)index >= (uint)Count) throw new ArgumentOutOfRangeException(nameof(index));
+
+ Monitor.Enter(_syncRoot);
+ try
+ {
+ return InnerList[index];
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+ set
+ {
+ Monitor.Enter(_syncRoot);
+ try
+ {
+ UpdateUnsafe(index, value);
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+
+ }
+ }
+
+ public void Add(T newValue)
+ {
+ if (newValue.Id.IsNullOrEmpty()) throw new ArgumentNullException(nameof(newValue), @"Add->newValue.Id.IsNullOrEmpty()");
+
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ if (Contains(newValue))
+ throw new ArgumentException($@"It already contains this Id! Id: {newValue.Id}", nameof(newValue));
+
+ UnsafeAdd(newValue);
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+
+ ///
+ /// AddMessageTag
+ ///
+ ///
+ ///
+ ///
+ public async Task Add(T newValue, bool autoSave)
+ {
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ Add(newValue);
+
+ return autoSave ? await SaveItem(newValue, TrackingState.Add) : newValue;
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+
+ ///
+ /// AddMessageTag or UpdateMessageTag
+ ///
+ ///
+ ///
+ ///
+ public async Task AddOrUpdate(T newValue, bool autoSave)
+ {
+ if (newValue.Id.IsNullOrEmpty()) throw new ArgumentNullException(nameof(newValue), @"AddOrUpdate->newValue.Id.IsNullOrEmpty()");
+
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ var index = IndexOf(newValue);
+
+ return index > -1 ? await Update(index, newValue, autoSave) : await Add(newValue, autoSave);
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+
+ }
+
+ //public void AddRange(IEnumerable collection)
+ //{
+ // lock (_syncRoot)
+ // {
+
+ // }
+ //}
+
+ protected void UnsafeAdd(T newValue)
+ {
+ TrackingItems.AddTrackingItem(TrackingState.Add, newValue);
+ InnerList.Add(newValue);
+ }
+
+ ///
+ /// AddMessageTag
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void Insert(int index, T newValue)
+ {
+ if (newValue.Id.IsNullOrEmpty()) throw new ArgumentNullException(nameof(newValue), @"Insert->newValue.Id.IsNullOrEmpty()");
+
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ if (Contains(newValue))
+ throw new ArgumentException($@"Insert; It already contains this Id! Id: {newValue.Id}", nameof(newValue));
+
+ TrackingItems.AddTrackingItem(TrackingState.Add, newValue);
+ InnerList.Insert(index, newValue);
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+
+ public async Task Insert(int index, T newValue, bool autoSave)
+ {
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ Insert(index, newValue);
+
+ return autoSave ? await SaveItem(newValue, TrackingState.Add) : newValue;
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+ ///
+ /// UpdateMessageTag
+ ///
+ ///
+ ///
+ public Task Update(T newItem, bool autoSave) => Update(IndexOf(newItem), newItem, autoSave);
+
+ ///
+ /// UpdateMessageTag
+ ///
+ ///
+ ///
+ ///
+ /// ///
+ /// ///
+ ///
+ ///
+ public async Task Update(int index, T newValue, bool autoSave)
+ {
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ UpdateUnsafe(index, newValue);
+
+ return autoSave ? await SaveItem(newValue, TrackingState.Update) : newValue;
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+
+ private void UpdateUnsafe(int index, T newValue)
+ {
+ if (default(T) != null && newValue == null) throw new NullReferenceException(nameof(newValue));
+ if (newValue.Id.IsNullOrEmpty()) throw new ArgumentNullException(nameof(newValue), @"UpdateUnsafe->newValue.Id.IsNullOrEmpty()");
+ if ((uint)index >= (uint)Count) throw new ArgumentOutOfRangeException(nameof(index));
+
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ var currentItem = InnerList[index];
+
+ if (currentItem.Id != newValue.Id)
+ throw new ArgumentException($@"UpdateUnsafe; currentItem.Id != item.Id! Id: {newValue.Id}", nameof(newValue));
+
+ TrackingItems.AddTrackingItem(TrackingState.Update, newValue, currentItem);
+ InnerList[index] = newValue;
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+
+
+ ///
+ /// RemoveMessageTag
+ ///
+ ///
+ ///
+ public bool Remove(T item)
+ {
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ var index = IndexOf(item);
+
+ if (index < 0) return false;
+
+ RemoveAt(index);
+ return true;
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+
+ public async Task Remove(T item, bool autoSave)
+ {
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ var result = Remove(item);
+
+ if (!autoSave || !result) return result;
+
+ await SaveItem(item, TrackingState.Remove);
+ return true;
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public bool TryRemove(Guid id, out T? item)
+ {
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ return TryGetValue(id, out item) && Remove(item);
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+
+ }
+
+ ///
+ /// RemoveMessageTag
+ ///
+ ///
+ ///
+ /// ///
+ ///
+ public void RemoveAt(int index)
+ {
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ var currentItem = InnerList[index];
+ if (currentItem.Id.IsNullOrEmpty()) throw new ArgumentNullException(nameof(currentItem), $@"RemoveAt->item.Id.IsNullOrEmpty(); index: {index}");
+
+ TrackingItems.AddTrackingItem(TrackingState.Remove, currentItem, currentItem);
+ InnerList.RemoveAt(index);
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+
+ public async Task RemoveAt(int index, bool autoSave)
+ {
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ var currentItem = InnerList[index];
+ RemoveAt(index);
+
+ if (autoSave)
+ {
+ await SaveItem(currentItem, TrackingState.Remove);
+ }
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+ ///
+ ///
+ ///
+ ///
+ public List> GetTrackingItems()
+ {
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ return TrackingItems.ToList();
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+
+ public void SetTrackingStateToUpdate(T item)
+ {
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ if (TrackingItems.TryGetTrackingItem(item.Id, out var trackingItem))
+ {
+ if (trackingItem.TrackingState != TrackingState.Add)
+ trackingItem.TrackingState = TrackingState.Update;
+
+ return;
+ }
+
+ if (!TryGetValue(item.Id, out var originalItem)) return;
+ TrackingItems.AddTrackingItem(TrackingState.Update, item, originalItem);
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public bool TryGetTrackingItem(Guid id, [NotNullWhen(true)] out TrackingItem? trackingItem)
+ {
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ return TrackingItems.TryGetTrackingItem(id, out trackingItem);
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+
+ }
+
+ ///
+ ///
+ ///
+ /// Unsaved items
+ public async Task>> SaveChanges()
+ {
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ foreach (var trackingItem in TrackingItems.ToList())
+ {
+ try
+ {
+ await SaveTrackingItemUnsafe(trackingItem);
+ }
+ catch(Exception ex)
+ {
+ TryRollbackItem(trackingItem.CurrentValue.Id, out _);
+ }
+ }
+
+ return TrackingItems.ToList();
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+
+ public async Task SaveChangesAsync()
+ {
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ foreach (var trackingItem in TrackingItems.ToList())
+ {
+ try
+ {
+ await SaveTrackingItemUnsafeAsync(trackingItem);
+ }
+ catch(Exception ex)
+ {
+ TryRollbackItem(trackingItem.CurrentValue.Id, out _);
+ }
+ }
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task SaveItem(Guid id)
+ {
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ T resultItem = null!;
+
+ if (TryGetTrackingItem(id, out var trackingItem))
+ resultItem = await SaveTrackingItemUnsafe(trackingItem);
+
+ if (resultItem == null) throw new NullReferenceException($"SaveItem; resultItem == null");
+ return resultItem;
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+
+ public async Task SaveItem(Guid id, TrackingState trackingState)
+ {
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ T resultItem = null!;
+
+ if (TryGetValue(id, out var item))
+ resultItem = await SaveItem(item, trackingState);
+
+ if (resultItem == null) throw new NullReferenceException($"SaveItem; resultItem == null");
+ return resultItem;
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+
+ public Task SaveItem(T item, TrackingState trackingState) => SaveItemUnsafe(item, trackingState);
+
+ protected Task SaveTrackingItemUnsafe(TrackingItem trackingItem)
+ => SaveItemUnsafe(trackingItem.CurrentValue, trackingItem.TrackingState);
+
+ protected Task SaveTrackingItemUnsafeAsync(TrackingItem trackingItem)
+ => SaveItemUnsafeAsync(trackingItem.CurrentValue, trackingItem.TrackingState);
+
+ protected async Task SaveItemUnsafe(T item, TrackingState trackingState)
+ {
+ var messageTag = SignalRCrudTags.GetMessageTagByTrackingState(trackingState);
+ if (messageTag == SignalRTags.None) throw new ArgumentException($"SaveItemUnsafe; messageTag == SignalRTags.None");
+
+ var result = await SignalRClient.PostDataAsync(messageTag, item);
+ if (result == null) throw new NullReferenceException($"SaveItemUnsafe; result == null");
+
+ await ProcessSavedResponseItem(result, trackingState);
+
+ return result;
+ }
+
+ protected Task SaveItemUnsafeAsync(T item, TrackingState trackingState)
+ {
+ var messageTag = SignalRCrudTags.GetMessageTagByTrackingState(trackingState);
+ if (messageTag == SignalRTags.None) return Task.CompletedTask;
+
+ return SignalRClient.PostDataAsync(messageTag, item, response =>
+ {
+ Monitor.Enter(_syncRoot);
+
+ try
+ {
+ if (response.Status != SignalResponseStatus.Success || response.ResponseData == null)
+ {
+ if (TryRollbackItem(item.Id, out _)) return;
+
+ throw new NullReferenceException($"SaveItemUnsafeAsync; result.Status != SignalResponseStatus.Success || result.ResponseData == null; Status: {SignalResponseStatus.Success}");
+ }
+
+ ProcessSavedResponseItem(response.ResponseData, trackingState).Forget();
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ });
+ }
+
+ private async Task ProcessSavedResponseItem(T? resultItem, TrackingState trackingState)
+ {
+ if (resultItem == null) return;
+
+ if (TryGetTrackingItem(resultItem.Id, out var trackingItem))
+ TrackingItems.Remove(trackingItem);
+
+ if (TryGetIndex(resultItem.Id, out var index))
+ InnerList[index] = resultItem;
+
+ var eventArgs = new ItemChangedEventArgs(resultItem, trackingState);
+ if (OnDataSourceItemChanged != null) await OnDataSourceItemChanged.Invoke(eventArgs);
+ }
+
+ protected void RollbackItemUnsafe(TrackingItem trackingItem)
+ {
+ if (TryGetIndex(trackingItem.CurrentValue.Id, out var index))
+ {
+ if (trackingItem.TrackingState == TrackingState.Add) InnerList.RemoveAt(index);
+ else InnerList[index] = trackingItem.OriginalValue!;
+ }
+ else if (trackingItem.TrackingState != TrackingState.Add)
+ InnerList.Add(trackingItem.OriginalValue!);
+
+ TrackingItems.Remove(trackingItem);
+ }
+
+ public bool TryRollbackItem(Guid id, out T? originalValue)
+ {
+ Monitor.Enter(_syncRoot);
+ try
+ {
+ if (TryGetTrackingItem(id, out var trackingItem))
+ {
+ originalValue = trackingItem.OriginalValue;
+
+ RollbackItemUnsafe(trackingItem);
+ return true;
+ }
+
+ originalValue = null;
+ return false;
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+
+ public void Rollback()
+ {
+ Monitor.Enter(_syncRoot);
+ try
+ {
+ foreach (var trackingItem in TrackingItems.ToList())
+ RollbackItemUnsafe(trackingItem);
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+
+ public int Count
+ {
+ get
+ {
+ Monitor.Enter(_syncRoot);
+ try
+ {
+ return InnerList.Count;
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+
+ }
+ }
+
+ public void Clear() => Clear(true);
+
+ public void Clear(bool clearChangeTracking)
+ {
+ Monitor.Enter(_syncRoot);
+ try
+ {
+ if (clearChangeTracking) TrackingItems.Clear();
+ InnerList.Clear();
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+
+ public int IndexOf(Guid id)
+ {
+ Monitor.Enter(_syncRoot);
+ try
+ {
+ return InnerList.FindIndex(x => x.Id == id);
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+
+ }
+
+ public int IndexOf(T item) => IndexOf(item.Id);
+ public bool TryGetIndex(Guid id, out int index) => (index = IndexOf(id)) > -1;
+
+ public bool Contains(T item) => IndexOf(item) > -1;
+
+ public bool TryGetValue(Guid id, [NotNullWhen(true)] out T? item)
+ {
+ Monitor.Enter(_syncRoot);
+ try
+ {
+ item = InnerList.FirstOrDefault(x => x.Id == id);
+ return item != null;
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+ }
+
+ public void CopyTo(T[] array) => CopyTo(array, 0);
+
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ Monitor.Enter(_syncRoot);
+ try
+ {
+ InnerList.CopyTo(array, arrayIndex);
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+
+ }
+
+ public int BinarySearch(int index, int count, T item, IComparer? comparer)
+ {
+ throw new NotImplementedException($"BinarySearch");
+ if (index < 0)
+ throw new ArgumentOutOfRangeException(nameof(index));
+ if (count < 0)
+ throw new ArgumentOutOfRangeException(nameof(count));
+ if (Count - index < count)
+ throw new ArgumentException("Invalid length");
+
+ //Monitor.Enter(_syncRoot);
+ //try
+ //{
+ // return InnerList.BinarySearch(index, count, item, comparer);
+ //}
+ //finally
+ //{
+ // Monitor.Exit(_syncRoot);
+ //}
+ }
+
+ public int BinarySearch(T item) => BinarySearch(0, Count, item, null);
+ public int BinarySearch(T item, IComparer? comparer) => BinarySearch(0, Count, item, comparer);
+
+ public IEnumerator GetEnumerator()
+ {
+ Monitor.Enter(_syncRoot);
+ try
+ {
+ //return InnerList.ToList().GetEnumerator();
+ return InnerList.GetEnumerator();
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+
+ }
+
+ public ReadOnlyCollection AsReadOnly() => new(this);
+ private static bool IsCompatibleObject(object? value) => (value is T) || (value == null && default(T) == null);
+
+
+ #region IList, ICollection
+
+ bool IList.IsReadOnly => false;
+
+ object? IList.this[int index]
+ {
+ get => this[index];
+ set
+ {
+ if (default(T) != null && value == null) throw new NullReferenceException(nameof(value));
+
+ try
+ {
+ this[index] = (T)value!;
+ }
+ catch (InvalidCastException)
+ {
+ throw new InvalidCastException(nameof(value));
+ }
+ }
+ }
+
+ int IList.Add(object? item)
+ {
+ if (default(T) != null && item == null) throw new NullReferenceException(nameof(item));
+
+ try
+ {
+ Add((T)item!);
+ }
+ catch (InvalidCastException)
+ {
+ throw new InvalidCastException(nameof(item));
+ }
+
+ return Count - 1;
+ }
+
+ void IList.Clear() => Clear(true);
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ bool IList.Contains(object? item) => IsCompatibleObject(item) && Contains((T)item!);
+ int IList.IndexOf(object? item) => (IsCompatibleObject(item)) ? IndexOf((T)item!) : -1;
+
+ void IList.Insert(int index, object? item)
+ {
+ if (default(T) != null && item == null) throw new NullReferenceException(nameof(item));
+
+ try
+ {
+ Insert(index, (T)item!);
+ }
+ catch (InvalidCastException)
+ {
+ throw new InvalidCastException(nameof(item));
+ }
+ }
+
+ void IList.Remove(object? item)
+ {
+ if (IsCompatibleObject(item)) Remove((T)item!);
+ }
+
+ void ICollection.Clear() => Clear(true);
+
+ void ICollection.CopyTo(Array array, int arrayIndex)
+ {
+ if ((array != null) && (array.Rank != 1))
+ {
+ throw new ArgumentException();
+ }
+
+ try
+ {
+ Monitor.Enter(_syncRoot);
+ try
+ {
+ //TODO: _list.ToArray() - ez nem az igazi... - J.
+ Array.Copy(InnerList.ToArray(), 0, array!, arrayIndex, InnerList.Count);
+ }
+ finally
+ {
+ Monitor.Exit(_syncRoot);
+ }
+
+ }
+ catch (ArrayTypeMismatchException)
+ {
+ throw new ArrayTypeMismatchException();
+ }
+ }
+
+ int ICollection.Count => Count;
+ int ICollection.Count => Count;
+ bool ICollection.IsReadOnly => false;
+ void IList.RemoveAt(int index) => RemoveAt(index);
+ int IReadOnlyCollection.Count => Count;
+
+ #endregion IList, ICollection
+ }
+
+ public class ItemChangedEventArgs where T : IId
+ {
+ internal ItemChangedEventArgs(T item, TrackingState trackingState)
+ {
+ Item = item;
+ TrackingState = trackingState;
+ }
+
+ public T Item { get; }
+ public TrackingState TrackingState { get; }
+ }
+}
diff --git a/TIAMWebApp/Shared/Utility/SignalRDataSourceAsync.cs b/TIAMWebApp/Shared/Utility/SignalRDataSourceAsync.cs
new file mode 100644
index 00000000..d435cb51
--- /dev/null
+++ b/TIAMWebApp/Shared/Utility/SignalRDataSourceAsync.cs
@@ -0,0 +1,104 @@
+using System.Diagnostics;
+using AyCode.Core.Enums;
+using AyCode.Core.Helpers;
+using AyCode.Core.Interfaces;
+using AyCode.Services.SignalRs;
+using TIAM.Services;
+using TIAMWebApp.Shared.Application.Services;
+
+namespace TIAMWebApp.Shared.Application.Utility;
+
+[Serializable]
+[DebuggerDisplay("Count = {Count}")]
+public class SignalRDataSourceAsync : SignalRDataSource where T : class, IId
+{
+ public Action>? OnItemChanged;
+ public Action>? OnDataSourceLoaded;
+
+ public SignalRDataSourceAsync(AcSignalRClientBase signalRClient, SignalRCrudTags signalRCrudTags, Guid? contextId = null, Action>? onDataSourceLoaded = null, bool autoLoadDataSource = false)
+ : base(signalRClient, signalRCrudTags, contextId)
+ {
+ OnDataSourceLoaded = onDataSourceLoaded;
+
+ if (autoLoadDataSource) LoadDataSourceAsync();
+ }
+
+ public void LoadDataSourceAsync(bool clearChangeTracking = true)
+ {
+ if (SignalRCrudTags.GetAllMessageTag == SignalRTags.None) throw new ArgumentException($"_signalRCrudTags.GetAllMessageTag == SignalRTags.None;");
+
+ //Monitor.Exit(SyncRoot); //Exception test - J.
+
+ //Monitor.Enter(SyncRoot);
+ //try
+ //{
+ // SignalRClient.GetAllAsync>(SignalRCrudTags.GetAllMessageTag, ContextId, response =>
+ // {
+ // try
+ // {
+ // if (response.Status == SignalResponseStatus.Error) throw new Exception($"LoadDataSourceAsync; response.Status == SignalResponseStatus.Error");
+ // if (response.ResponseData == null) throw new NullReferenceException($"response.ResponseData == null");
+
+ // Clear(clearChangeTracking);
+ // InnerList.AddRange(response.ResponseData);
+ // }
+ // finally
+ // {
+ // Monitor.Exit(SyncRoot);
+ // }
+
+ // OnDataSourceLoaded?.Invoke(this);
+ // }).Forget();
+ //}
+ //catch (Exception)
+ //{
+ // Monitor.Exit(SyncRoot);
+ // throw;
+ //}
+ }
+
+
+ //public T Add(T item, int messageTag) => PostDataToServerAsync(item, messageTag, TrackingState.Add).GetAwaiter().GetResult();
+ //public Task AddAsync(T item, int messageTag) => PostDataToServerAsync(item, messageTag, TrackingState.Add);
+
+
+ //public Task UpdateAsync(T item, int messageTag) => PostDataToServerAsync(item, messageTag, TrackingState.Update);
+
+ //public Task RemoveAsync(T item, int messageTag) => PostDataToServerAsync(item, messageTag, TrackingState.Remove);
+
+ //public Task RemoveAsync(Guid id, int messageTag)
+ //{
+ // var item = _list.FirstOrDefault(x => x.Id == id);
+
+ // return item == null ? Task.CompletedTask : RemoveAsync(item, messageTag);
+ //}
+
+ //protected virtual Task PostDataToServerAsync(T item, int messageTag, TrackingState trackingState)
+ //{
+ // if (messageTag == 0) return Task.CompletedTask;
+
+ // logger.Info($"{_listLogName} PostDataToServerAsync called; transferId " + item.Id);
+
+ // if (item.Id.IsNullOrEmpty()) item.Id = Guid.NewGuid();
+
+ // _list.UpdateCollection(item, trackingState == TrackingState.Remove); //egyből látszódik a változás a grid-ben, nem csak a callback lefutásakor! felhasználóbarátabb... - J.
+
+ // await _signalRClient.PostDataAsync(messageTag, item, async repsonse =>
+ // {
+ // if (repsonse.Status != SignalResponseStatus.Success || repsonse.ResponseData == null)
+ // {
+ // RefreshDataSourceAsync().Forget();
+ // return;
+ // }
+
+ // _list.UpdateCollection(repsonse.ResponseData, trackingState == TrackingState.Remove);
+
+ // var eventArgs = new ItemChangedEventArgs(repsonse.ResponseData, trackingState);
+ // OnItemChanged.Invoke(eventArgs);
+ // });
+
+ // //transfer = await devAdminSignalClient.PostDataAsync(SignalRTags.UpdateTransferAsync, transfer);
+
+ // return Task.CompletedTask;
+ //}
+}
\ No newline at end of file
diff --git a/TIAMWebApp/Shared/Utility/TrackingItemHelpers.cs b/TIAMWebApp/Shared/Utility/TrackingItemHelpers.cs
new file mode 100644
index 00000000..34b5eadc
--- /dev/null
+++ b/TIAMWebApp/Shared/Utility/TrackingItemHelpers.cs
@@ -0,0 +1,47 @@
+using System.Reflection;
+using System.Text.Json;
+using AyCode.Core.Extensions;
+
+namespace TIAMWebApp.Shared.Application.Utility;
+
+public static class TrackingItemHelpers
+{
+ public static T JsonClone(T source) => source.ToJson().JsonTo()!;
+
+ public static T ReflectionClone(T source)
+ {
+ var type = source!.GetType();
+
+ if (type.IsPrimitive || typeof(string) == type)
+ return source;
+
+ if (type.IsArray)
+ {
+ var elementType = Type.GetType(type.FullName!.Replace("[]", string.Empty))!;
+ var array = (source as Array)!;
+ var cloned = Array.CreateInstance(elementType, array.Length);
+
+ for (var i = 0; i < array.Length; i++)
+ cloned.SetValue(ReflectionClone(array.GetValue(i)), i);
+
+ return (T)Convert.ChangeType(cloned, type);
+ }
+
+ var clone = Activator.CreateInstance(type);
+
+ while (type != null && type != typeof(object))
+ {
+ foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
+ {
+ var fieldValue = field.GetValue(source);
+ if (fieldValue == null) continue;
+
+ field.SetValue(clone, ReflectionClone(fieldValue));
+ }
+
+ type = type.BaseType;
+ }
+
+ return (T)clone!;
+ }
+}
\ No newline at end of file