Add URL link support to grid columns and info panel

- Introduced UrlLink parameter to MgGridDataColumn for clickable cell links with property placeholder support.
- Updated MgGridInfoPanel to render links for columns with UrlLink.
- Added unit tests for UrlLink rendering and value replacement.
- Added DynamicColumnAddingEventArgs and OnDynamicColumnAttributeAdding for dynamic column customization.
- Refactored layout storage key logic and enabled persistent info panel splitter size in MgGridWithInfoPanel.
- Updated product/order grids to use MgGridDataColumn with UrlLink and switched ProductDtos to AcObservableCollection for reactivity.
- Added AddPartner method to IFruitBankDataControllerCommon and FruitBankSignalRClient.
- Miscellaneous fixes: logger initialization, code cleanup, and improved comments.
This commit is contained in:
Loretta 2025-12-22 14:37:56 +01:00
parent 324f171377
commit 22821a4b27
8 changed files with 98 additions and 68 deletions

View File

@ -15,6 +15,7 @@ public interface IFruitBankDataControllerCommon
#region Partner
public Task<List<Partner>?> GetPartners();
public Task<Partner?> GetPartnerById(int id);
public Task<Partner?> AddPartner(Partner partner);
public Task<Partner?> UpdatePartner(Partner partner);
#endregion Partner

View File

@ -1,6 +1,7 @@
@using AyCode.Blazor.Components.Components.Grids
@using AyCode.Core.Helpers
@using AyCode.Utils.Extensions
@using FruitBank.Common
@using FruitBank.Common.Dtos
@using FruitBank.Common.Models
@using FruitBankHybrid.Shared.Components.Grids.GenericAttributes
@ -17,7 +18,8 @@
ColumnResizeMode="GridColumnResizeMode.NextColumn"
FilterMenuButtonDisplayMode="@(IsMasterGrid ? GridFilterMenuButtonDisplayMode.Never : GridFilterMenuButtonDisplayMode.Always)">
<Columns>
<DxGridDataColumn FieldName="Id" SortIndex="0" SortOrder="GridColumnSortOrder.Descending" />
<MgGridDataColumn FieldName="Id" SortIndex="0" SortOrder="GridColumnSortOrder.Descending"
UrlLink=@(FruitBankConstClient.BaseUrl+"/Admin/Order/Edit/{Id}") />
<DxGridDataColumn FieldName="CustomerId" />
<DxGridDataColumn FieldName="@("Customer.Company")" Caption="Company" ReadOnly="true" />
<DxGridDataColumn FieldName="OrderStatus" />

View File

@ -1,12 +1,19 @@
using DevExpress.Blazor;
using FruitBank.Common.Dtos;
using FruitBank.Common.SignalRs;
using FruitBankHybrid.Shared.Components.Grids;
namespace FruitBankHybrid.Shared.Components;
public class GridProductDto : MgGridBase
public class GridProductDto : FruitBankGridBase<ProductDto>
{
protected override void CustomizeElementHideDetailButton(GridCustomizeElementEventArgs e)
public GridProductDto() : base()
{
//Felülírjuk, h ne adja hozzá a "hideDetailButton" css class-t!
return;
GetAllMessageTag = SignalRTags.GetProductDtos;
//AddMessageTag = SignalRTags.AddPartner;
//UpdateMessageTag = SignalRTags.UpdatePartner;
//RemoveMessageTag = SignalRTags.;
}
}

View File

@ -1,82 +1,93 @@
@using System.Threading
@using AyCode.Blazor.Components.Components.Grids
@using AyCode.Core.Helpers
@using AyCode.Core.Loggers
@using AyCode.Utils.Extensions
@using DevExpress.Internal.About
@using FruitBank.Common
@using FruitBank.Common.Dtos
@using FruitBank.Common.Models
@using FruitBankHybrid.Shared.Components.Grids.GenericAttributes
@using FruitBankHybrid.Shared.Components.Grids.Products
@using FruitBankHybrid.Shared.Databases
@using FruitBankHybrid.Shared.Services.Loggers
@using FruitBankHybrid.Shared.Services.SignalRs
@inject LoggedInModel LoggedInModel;
@inject IEnumerable<IAcLogWriterClientBase> LogWriters
@inject FruitBankSignalRClient FruitBankSignalRClient
<GridProductDto @ref="Grid" DataSource="ProductDtos" IsMasterGrid="IsMasterGrid" FocusedRowChanged="OnFocusedRowChanged"
CssClass="@GridCss" AutoSaveLayoutName="GridProductDtoTemplate">
<Columns>
<DxGridDataColumn FieldName="Id" SortIndex="0" SortOrder="GridColumnSortOrder.Ascending" />
<DxGridDataColumn FieldName="Name" />
<DxGridDataColumn FieldName="Price" />
<DxGridDataColumn FieldName="AvailableQuantity" ReadOnly="true" />
<DxGridDataColumn FieldName="StockQuantity" />
<DxGridDataColumn FieldName="IncomingQuantity" ReadOnly="true" />
<DxGridDataColumn FieldName="NetWeight" ReadOnly="true" />
<DxGridDataColumn FieldName="IsMeasurable" />
<DxGridDataColumn FieldName="Tare" Caption="Tára súly" ReadOnly="true" />
<DxGridDataColumn FieldName="AverageWeight" ReadOnly="true" />
<DxGridDataColumn FieldName="AverageWeightTreshold" ReadOnly="true" />
<DxGridCommandColumn Visible="!IsMasterGrid" Width="120"></DxGridCommandColumn>
</Columns>
<DetailRowTemplate>
@if (IsMasterGrid)
{
var productDto = ((ProductDto)(context.DataItem));
var productId = productDto.Id;
<MgGridWithInfoPanel ShowInfoPanel="@IsMasterGrid">
<GridContent>
<GridProductDto @ref="Grid" DataSource="ProductDtos" CssClass="@GridCss" AutoSaveLayoutName="GridProductDtoTemplate"
Logger="_logger" SignalRClient="FruitBankSignalRClient">
<Columns>
<MgGridDataColumn FieldName="Id" SortIndex="0" SortOrder="GridColumnSortOrder.Ascending"
UrlLink=@(FruitBankConstClient.BaseUrl + "/Admin/Product/Edit/{Id}") />
<DxGridDataColumn FieldName="Name" />
<DxGridDataColumn FieldName="Price" />
<DxGridDataColumn FieldName="AvailableQuantity" ReadOnly="true" />
<DxGridDataColumn FieldName="StockQuantity" />
<DxGridDataColumn FieldName="IncomingQuantity" ReadOnly="true" />
<DxGridDataColumn FieldName="NetWeight" ReadOnly="true" />
<DxGridDataColumn FieldName="IsMeasurable" />
<DxGridDataColumn FieldName="Tare" Caption="Tára súly" ReadOnly="true" />
<DxGridDataColumn FieldName="AverageWeight" ReadOnly="true" />
<DxGridDataColumn FieldName="AverageWeightTreshold" ReadOnly="true" />
<DxGridCommandColumn Visible="!IsMasterGrid" Width="120"></DxGridCommandColumn>
</Columns>
<DetailRowTemplate>
@if (IsMasterGrid)
{
var productDto = ((ProductDto)(context.DataItem));
var productId = productDto.Id;
<DxTabs ActiveTabIndexChanged="(i) => OnActiveTabChanged(i, productId)">
<DxTabPage Text="Készlet mennyiség változások" Visible="@LoggedInModel.IsAdministrator">
@{
var contextIds = new[] { (object)productDto.Id };
<GridStockQuantityHistoryDtoTemplate ContextIds="@(contextIds)" ParentDataItem="@productDto" />
}
</DxTabPage>
<DxTabPage Text="Rendelések melyben megtalálható" Visible="@LoggedInModel.IsAdministrator">
@{
//GetOrderDtosFromDbAsync(productId).Forget();
//var orderDtos = _orderDtos?.Where(o => o.OrderItemDtos.Any(oi => oi.ProductId == productId)).ToList() ?? [];
<GridDetailOrderDto OrderDtos="_currentOrderDtos" IsMasterGrid="false"></GridDetailOrderDto>
}
</DxTabPage>
<DxTabPage Text="Rendelés tételek" Visible="@LoggedInModel.IsAdministrator">
@{
//GetOrderItemDtosFromDbAsync(productId).Forget();
//var orderItemDtos = _orderItemDtos?.Where(oi => oi.ProductId == productId).ToList() ?? [];
<GridDetailOrderItemDto OrderItemDtos="_currentOrderItemDtos" IsMasterGrid="false" />
}
</DxTabPage>
<DxTabPage Text="Speciális jellemzők" Visible="@LoggedInModel.IsDeveloper">
@{
var genericAttributeDtos = new AcObservableCollection<GenericAttributeDto>(productDto.GenericAttributes);
<GridGenericAttribute ParentDataItem="@productDto" GenericAttributes="@genericAttributeDtos" />
}
</DxTabPage>
</DxTabs>
}
</DetailRowTemplate>
<ToolbarTemplate>
@if (IsMasterGrid)
{
<MgGridToolbarTemplate Grid="Grid" OnReloadDataClick="() => ReloadDataFromDb(true)" />
}
</ToolbarTemplate>
</GridProductDto>
<DxTabs ActiveTabIndexChanged="(i) => OnActiveTabChanged(i, productId)">
<DxTabPage Text="Készlet mennyiség változások" Visible="@LoggedInModel.IsAdministrator">
@{
var contextIds = new[] { (object)productDto.Id };
<GridStockQuantityHistoryDtoTemplate ContextIds="@(contextIds)" ParentDataItem="@productDto" />
}
</DxTabPage>
<DxTabPage Text="Rendelések melyben megtalálható" Visible="@LoggedInModel.IsAdministrator">
@{
//GetOrderDtosFromDbAsync(productId).Forget();
//var orderDtos = _orderDtos?.Where(o => o.OrderItemDtos.Any(oi => oi.ProductId == productId)).ToList() ?? [];
<GridDetailOrderDto OrderDtos="_currentOrderDtos" IsMasterGrid="false"></GridDetailOrderDto>
}
</DxTabPage>
<DxTabPage Text="Rendelés tételek" Visible="@LoggedInModel.IsAdministrator">
@{
//GetOrderItemDtosFromDbAsync(productId).Forget();
//var orderItemDtos = _orderItemDtos?.Where(oi => oi.ProductId == productId).ToList() ?? [];
<GridDetailOrderItemDto OrderItemDtos="_currentOrderItemDtos" IsMasterGrid="false" />
}
</DxTabPage>
<DxTabPage Text="Speciális jellemzők" Visible="@LoggedInModel.IsDeveloper">
@{
var genericAttributeDtos = new AcObservableCollection<GenericAttributeDto>(productDto.GenericAttributes);
<GridGenericAttribute ParentDataItem="@productDto" GenericAttributes="@genericAttributeDtos" />
}
</DxTabPage>
</DxTabs>
}
</DetailRowTemplate>
<ToolbarTemplate>
@if (IsMasterGrid)
{
<MgGridToolbarTemplate Grid="Grid" OnReloadDataClick="() => ReloadDataFromDb(true)" />
}
</ToolbarTemplate>
</GridProductDto>
</GridContent>
</MgGridWithInfoPanel>
@code {
[Inject] public required DatabaseClient Database { get; set; }
private LoggerClient<GridShippingDocument> _logger = null!;
string GridCss => !IsMasterGrid ? "hide-toolbar" : string.Empty;
private int _activeTabIndex;
@ -94,12 +105,13 @@
[Parameter] public bool IsMasterGrid { get; set; } = false;
[Parameter] public IEnumerable<ProductDto>? ProductDtos { get; set; }
[Parameter] public AcObservableCollection<ProductDto>? ProductDtos { get; set; }
//[Parameter] public List<OrderDto>? OrderDtos { get; set; }
//[Parameter] public List<OrderItemDto>? OrderItemDtos { get; set; }
protected override async Task OnInitializedAsync()
{
_logger = new LoggerClient<GridShippingDocument>(LogWriters.ToArray());
// if (IsMasterGrid)
// {
// using (await ObjectLock.GetSemaphore<ProductDto>().UseWaitAsync())
@ -122,7 +134,8 @@
LoadingPanelVisibility.Visible = true;
//using (await ObjectLock.GetSemaphore<ProductDto>().UseWaitAsync())
{
if (ProductDtos == null || !ProductDtos.Any() || forceReload) ProductDtos = await Database.ProductDtoTable.LoadDataAsync(!forceReload);
if (ProductDtos == null || !ProductDtos.Any() || forceReload)
ProductDtos = new AcObservableCollection<ProductDto>(await Database.ProductDtoTable.LoadDataAsync(!forceReload));
}
_orderDtosByProductId.Clear();

View File

@ -10,6 +10,7 @@
@using AyCode.Core.Helpers
@using AyCode.Core.Interfaces
@using AyCode.Core.Loggers
@using FruitBank.Common
@using FruitBank.Common.Models
@using FruitBankHybrid.Shared.Services.Loggers
@ -21,7 +22,7 @@
<GridShippingDocumentBase @ref="Grid" CssClass="@GridCss" DataSource="@ShippingDocuments" SignalRClient="FruitBankSignalRClient" AutoSaveLayoutName="GridShippingDocument"
ParentDataItem="@ParentDataItem" Logger="_logger" ValidationEnabled="false" EditMode="GridEditMode.EditRow" OnGridFocusedRowChanged="Grid_FocusedRowChanged">
<Columns>
<DxGridDataColumn FieldName="Id" SortIndex="0" SortOrder="GridColumnSortOrder.Descending" ReadOnly="true" />
<MgGridDataColumn FieldName="Id" SortIndex="0" SortOrder="GridColumnSortOrder.Descending" ReadOnly="true"/>
<DxGridDataColumn FieldName="PartnerId" Caption="Partner" Visible="@(!ParentDataItemIsPartner)" ReadOnly="@ParentDataItemIsPartner">
<EditSettings>
<DxComboBoxSettings Data="Partners"

View File

@ -47,6 +47,8 @@ public class MgGridBase : DxGrid, IMgGridBase
/// <inheritdoc />
public bool IsFullscreen => false;
public string LayoutStorageKey { get; }
/// <inheritdoc />
public void ToggleFullscreen()
{

View File

@ -13,6 +13,7 @@ using FruitBankHybrid.Shared.Services.SignalRs;
using Mango.Nop.Core.Loggers;
using Microsoft.AspNetCore.Components;
using System.Diagnostics;
using AyCode.Core.Helpers;
namespace FruitBankHybrid.Shared.Pages;
@ -26,7 +27,7 @@ public partial class OrdersAdmin : ComponentBase
[Inject] public required LoggedInModel LoggedInModel { get; set; }
public required IGrid gridOrder;
private IEnumerable<ProductDto>? ProductDtos { get; set; } = [];
private AcObservableCollection<ProductDto>? ProductDtos { get; set; } = [];
private List<OrderDto>? OrderDtos { get; set; } = [];
private List<OrderItemDto>? OrderItemDtos { get; set; } = [];
@ -51,7 +52,7 @@ public partial class OrdersAdmin : ComponentBase
LoadingPanelVisibility.Visible = true;
//using (await ObjectLock.GetSemaphore<ProductDto>().UseWaitAsync())
{
if (ProductDtos == null || !ProductDtos.Any() || forceReload) ProductDtos = await Database.ProductDtoTable.LoadDataAsync(!forceReload);
if (ProductDtos == null || !ProductDtos.Any() || forceReload) ProductDtos = new AcObservableCollection<ProductDto>(await Database.ProductDtoTable.LoadDataAsync(!forceReload));
}
//using (await ObjectLock.GetSemaphore<OrderItemDto>().UseWaitAsync())

View File

@ -68,6 +68,9 @@ namespace FruitBankHybrid.Shared.Services.SignalRs
public Task<Partner?> GetPartnerById(int id)
=> GetByIdAsync<Partner?>(SignalRTags.GetPartnerById, id);
public Task<Partner?> AddPartner(Partner partner)
=> PostDataAsync(SignalRTags.AddPartner, partner);
public Task<Partner?> UpdatePartner(Partner partner)
=> PostDataAsync(SignalRTags.UpdatePartner, partner);