using AyCode.Core; using AyCode.Core.Enums; using AyCode.Core.Helpers; using AyCode.Core.Interfaces; using AyCode.Core.Loggers; using AyCode.Services.Server.SignalRs; using AyCode.Services.SignalRs; using AyCode.Utils.Extensions; using DevExpress.Blazor; using Microsoft.AspNetCore.Components; using System.ComponentModel; using System.Reflection; namespace AyCode.Blazor.Components.Components.Grids; public interface IMgGridBase : IGrid { } public abstract class MgGridBase : DxGrid, IMgGridBase where TSignalRDataSource : AcSignalRDataSource> where TDataItem : class, IId where TId : struct where TLoggerClient : AcLoggerBase { private readonly EqualityComparer _equalityComparerId = EqualityComparer.Default; private readonly TypeConverter _typeConverterId = TypeDescriptor.GetConverter(typeof(TId)); protected bool IsFirstInitializeParameters; protected bool IsFirstInitializeParameterCore; private TSignalRDataSource? _dataSource = null!; private AcObservableCollection? _dataSourceParam = []; private string _gridLogName; public MgGridBase() : base() { } protected bool HasIdValue(TDataItem dataItem) => HasIdValue(dataItem.Id); protected bool HasIdValue(TId id) => !_equalityComparerId.Equals(id, default); protected bool IdEquals(TId id1, TId id2) => _equalityComparerId.Equals(id1, id2); [Parameter] public TLoggerClient Logger { get; set; } [Parameter] public string GridName { get; set; } [Parameter] public IId? ParentDataItem { get; set; } [Parameter] public string? KeyFieldNameToParentId { get; set; } [Parameter] public object[]? ContextIds { get; set; } public bool IsMasterGrid => ParentDataItem == null; protected PropertyInfo? KeyFieldPropertyInfoToParent; private string? _filterText = null; [Parameter] public string? FilterText { get => _filterText; set { _filterText = value; if (_dataSource != null && _dataSource.FilterText != value) { _dataSource.FilterText = value; ReloadDataSourceAsync().Forget(); } } } [Parameter] public AcSignalRClientBase SignalRClient { get; set; } [Parameter] public int GetAllMessageTag { get; set; } [Parameter] public int GetItemMessageTag { get; set; } [Parameter] public int AddMessageTag { get; set; } [Parameter] public int UpdateMessageTag { get; set; } [Parameter] public int RemoveMessageTag { get; set; } protected new EventCallback DataItemDeleting { get; set; } [Parameter] public EventCallback OnGridItemDeleting { get; set; } protected new EventCallback EditModelSaving { get; set; } [Parameter] public EventCallback OnGridEditModelSaving { get; set; } protected new EventCallback EditStart { get; set; } [Parameter] public EventCallback OnGridEditStart { get; set; } protected new EventCallback CustomizeEditModel { get; set; } [Parameter] public EventCallback OnGridCustomizeEditModel { get; set; } [Parameter] public EventCallback> OnDataSourceChanged { get; set; } [Parameter] public EventCallback> OnGridItemChanging { get; set; } /// /// After the server has responded! /// [Parameter] public EventCallback> OnGridItemChanged { get; set; } [Parameter] [DefaultValue(null)] [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "BL0007:Component parameters should be auto properties", Justification = "")] public IList DataSource { get { if (_dataSource == null && Data != null) { Logger.Error($"{_gridLogName} Use the DataSource parameter instead of Data!"); throw new NullReferenceException($"{_gridLogName} Use the DataSource parameter instead of Data!"); } return _dataSource!; } set { _dataSourceParam = value as AcObservableCollection; if (_dataSource != null) // && _dataSourceParam is List workingReferenceList) { SetWorkingReferenceList(_dataSourceParam); } } } private void SetWorkingReferenceList(AcObservableCollection? referenceList) { _dataSource?.SetWorkingReferenceList(referenceList); SetGridData(referenceList); } public void SetGridData(object? data) { if (ReferenceEquals(Data, data)) return; BeginUpdate(); Data = data; EndUpdate(); } 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"); } var crudTags = new SignalRCrudTags(GetAllMessageTag, GetItemMessageTag, AddMessageTag, UpdateMessageTag, RemoveMessageTag); _dataSource = (TSignalRDataSource)Activator.CreateInstance(typeof(TSignalRDataSource), SignalRClient, crudTags, ContextIds)!; _dataSource.FilterText = FilterText; //_dataSource = new SignalRDataSource(SignalRClient, crudTags, ContextIds) { FilterText = FilterText }; SetGridData(_dataSource.GetReferenceInnerList()); _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); if (!changedEventArgs.CancelStateChangeInvoke) { //BeginUpdate(); await InvokeAsync(StateHasChanged); //TODO: bezárja a DetailRow-t! pl: az email-nél IsReaded=true update... - J. //EndUpdate(); } } private Task OnDataSourceLoaded() { Logger.Debug($"{_gridLogName} OnDataSourceLoaded; Count: {_dataSource?.Count}"); //if(_dataSourceParam.GetType() == typeof()AcObservableCollection) SetGridData(_dataSource!.GetReferenceInnerList()); //else Reload(); //_dataSource.LoadItem(_dataSource.First().Id).Forget(); return OnDataSourceChanged.InvokeAsync(_dataSource); } protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { if (_dataSource == null) return; if (_dataSourceParam != null) await _dataSource.LoadDataSource(_dataSourceParam, true, true); else _dataSource.LoadDataSourceAsync(true).Forget(); } await base.OnAfterRenderAsync(firstRender); } private void SetNewId(TDataItem dataItem) { //TODO: int !!! - J. if (dataItem.Id is Guid) { dataItem.Id = (TId)(_typeConverterId.ConvertTo(Guid.NewGuid(), typeof(TId)))!; } else if (dataItem.Id is int) { var newId = -1 * AcDomain.NextUniqueInt32; dataItem.Id = (TId)(_typeConverterId.ConvertTo(newId, typeof(TId)))!; } } public Task AddDataItem(TDataItem dataItem) { if (!HasIdValue(dataItem)) SetNewId(dataItem); return _dataSource.Add(dataItem, true); } public Task AddDataItemAsync(TDataItem dataItem) { if (!HasIdValue(dataItem)) SetNewId(dataItem); _dataSource.Add(dataItem); return SaveChangesToServerAsync(); } public Task InsertDataItem(int index, TDataItem dataItem) { if (!HasIdValue(dataItem)) SetNewId(dataItem); return _dataSource.Insert(index, dataItem, true); } public Task InsertDataItemAsync(int index, TDataItem dataItem) { if (!HasIdValue(dataItem)) SetNewId(dataItem); _dataSource.Insert(index, dataItem); return SaveChangesToServerAsync(); } protected PropertyInfo? GetDataItemPropertyInfo(string propertyName) => typeof(TDataItem).GetProperty(propertyName); private async Task OnCustomizeEditModel(GridCustomizeEditModelEventArgs e) { var editModel = (e.EditModel as TDataItem)!; if (e.IsNew) { if (!HasIdValue(editModel)) SetNewId(editModel); if (ParentDataItem != null && !KeyFieldNameToParentId.IsNullOrWhiteSpace()) { KeyFieldPropertyInfoToParent ??= GetDataItemPropertyInfo(KeyFieldNameToParentId); KeyFieldPropertyInfoToParent!.SetValue(editModel, ParentDataItem.Id); } e.EditModel = editModel; } await OnGridCustomizeEditModel.InvokeAsync(e); } private async Task OnEditStart(GridEditStartEventArgs e) { var dataItem = (e.DataItem as TDataItem)!; await OnGridEditStart.InvokeAsync(e); } //void Grid_CustomizeEditModel(GridCustomizeEditModelEventArgs e) //{ // var model = e.EditModel as EditableWorkOrder; // if (model == null) // { // model = new EditableWorkOrder(); // model.WorkOrderNum = "123"; // model.Description = "hey"; // e.EditModel = model; // } //} private async Task OnItemSaving(GridEditModelSavingEventArgs e) { var dataItem = (e.EditModel as TDataItem)!; if (e.IsNew) { if (!HasIdValue(dataItem)) SetNewId(dataItem); //if (ParentDataItem != null && !KeyFieldNameToParentId.IsNullOrWhiteSpace()) //{ // Type examType = typeof(TDataItem); // // Change the static property value. // PropertyInfo piShared = examType.GetProperty(KeyFieldNameToParentId); // piShared.SetValue(dataItem, ParentDataItem.Id); //} } var logText = e.IsNew ? "add" : "update"; Logger.Debug($"{_gridLogName} OnItemSaving {logText}; Id: {dataItem.Id}"); await OnGridEditModelSaving.InvokeAsync(e); if (e.Cancel) { Logger.Debug($"{_gridLogName} OnItemSaving {logText} canceled; Id: {dataItem.Id}"); return; } if (e.IsNew) { if (EditNewRowPosition is GridEditNewRowPosition.FixedOnTop or GridEditNewRowPosition.Top) await AddDataItemAsync(dataItem); else await InsertDataItemAsync(0, dataItem); } else await UpdateDataItemAsync(dataItem); //var equalityComparer = EqualityComparer.Default; //var index = CollectionExtensions.FindIndex(_dataSource, x => equalityComparer.Equals(x.Id, dataItem.Id)); //_dataSource.UpdateCollectionByIndex(index, dataItem, false); //_dataSource.UpdateCollectionById(dataItem.Id, false); } 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)) 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.Debug($"{_gridLogName} OnItemDeleting"); await OnGridItemDeleting.InvokeAsync(e); if (e.Cancel) { Logger.Debug($"{_gridLogName} OnItemDeleting canceled"); return; } var dataItem = (e.DataItem as TDataItem)!; await RemoveDataItem(dataItem); } private void OnCustomizeElement(GridCustomizeElementEventArgs e) { if (e.ElementType == GridElementType.DetailCell) { e.Style = "padding: 0.5rem; opacity: 0.75"; } else if (false && e.ElementType == GridElementType.DataCell && e.Column.Name == nameof(IId.Id)) { e.Column.Visible = AcDomain.IsDeveloperVersion; e.Column.ShowInColumnChooser = AcDomain.IsDeveloperVersion; } } protected override async Task SetParametersAsyncCore(ParameterView parameters) { await base.SetParametersAsyncCore(parameters); if (!IsFirstInitializeParameterCore) { //if (typeof(TDataItem) is IId || typeof(TDataItem) is IId) KeyFieldName = "Id"; base.DataItemDeleting = EventCallback.Factory.Create(this, OnItemDeleting); base.EditModelSaving = EventCallback.Factory.Create(this, OnItemSaving); base.CustomizeEditModel = EventCallback.Factory.Create(this, OnCustomizeEditModel); base.EditStart = EventCallback.Factory.Create(this, OnEditStart); CustomizeElement += OnCustomizeElement; //ShowFilterRow = true; //PageSize = 4; //ShowGroupPanel = true; //AllowSort = false; TextWrapEnabled = false; AllowSelectRowByClick = true; HighlightRowOnHover = true; AutoCollapseDetailRow = true; AutoExpandAllGroupRows = false; //KeyboardNavigationEnabled = true; //var dataColumns = GetDataColumns(); //var idColumn = dataColumns.FirstOrDefault(x => x.FieldName == nameof(IId.Id)); //if (idColumn != null) //{ // idColumn.ShowInColumnChooser = AcDomain.IsDeveloperVersion; // idColumn.Visible = !AcDomain.IsDeveloperVersion; //} IsFirstInitializeParameterCore = true; } } protected override void OnParametersSet() { if (!IsFirstInitializeParameters) { if (GridName.IsNullOrWhiteSpace()) GridName = $"{typeof(TDataItem).Name}Grid"; _gridLogName = $"[{GridName}]"; IsFirstInitializeParameters = true; } base.OnParametersSet(); } //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(TId id) => RemoveDataItem(id, RemoveMessageTag); public Task RemoveDataItem(TId id, int messageTag) { return _dataSource.Remove(id, true); } public Task ReloadDataSourceAsync() { return _dataSource.LoadDataSourceAsync(false); } } public class GridDataItemChangingEventArgs : GridDataItemChangedEventArgs where TDataItem : class { internal GridDataItemChangingEventArgs(IMgGridBase grid, TDataItem dataItem, TrackingState trackingState) : base(grid, dataItem, trackingState) { } public bool IsCanceled { get; set; } } public class GridDataItemChangedEventArgs where TDataItem : class { internal GridDataItemChangedEventArgs(IMgGridBase grid, TDataItem dataItem, TrackingState trackingState) { Grid = grid ?? throw new ArgumentNullException(nameof(grid)); DataItem = dataItem; TrackingState = trackingState; } public IMgGridBase Grid { get; } public TDataItem DataItem { get; } public TrackingState TrackingState { get; } public bool CancelStateChangeInvoke { get; set; } }