Refactor: decouple InfoPanel using MgGridWithInfoPanel

Major refactor to decouple InfoPanel logic from grid base. Introduces MgGridWithInfoPanel wrapper component to manage grid and InfoPanel layout and communication. InfoPanels are now customizable via Razor templates with named slots (header, footer, etc.), and grid-to-InfoPanel communication is routed through the wrapper. Removes legacy C#-only InfoPanel base classes and parameters from grid base. This improves flexibility, composability, and maintainability of grid+InfoPanel UIs.
This commit is contained in:
Loretta 2025-12-18 11:02:53 +01:00
parent 031fd9226b
commit 7f4052faa2
4 changed files with 132 additions and 178 deletions

View File

@ -16,127 +16,116 @@
@inject IEnumerable<IAcLogWriterClientBase> LogWriters
@inject FruitBankSignalRClient FruitBankSignalRClient
<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" />
<DxGridDataColumn FieldName="PartnerId" Caption="Partner" Visible="@(!ParentDataItemIsPartner)" ReadOnly="@ParentDataItemIsPartner">
<EditSettings>
<DxComboBoxSettings Data="Partners"
ValueFieldName="Id"
TextFieldName="Name"
DropDownBodyCssClass="dd-body-class"
ListRenderMode="ListRenderMode.Entire"
SearchMode="ListSearchMode.AutoSearch"
SearchFilterCondition="ListSearchFilterCondition.Contains"
ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto">
<Columns>
<DxListEditorColumn FieldName="@nameof(Partner.Id)" />
<DxListEditorColumn FieldName="@nameof(Partner.Name)" />
<DxListEditorColumn FieldName="@nameof(Partner.TaxId)" />
</Columns>
</DxComboBoxSettings>
</EditSettings>
</DxGridDataColumn>
<DxGridDataColumn FieldName="ShippingId" Caption="Shipping" Visible="@(!ParentDataItemIsShipping)" ReadOnly="@ParentDataItemIsShipping">
<EditSettings>
<DxComboBoxSettings Data="Shippings"
ValueFieldName="Id"
TextFieldName="ShippingDate"
DropDownBodyCssClass="dd-body-class"
ListRenderMode="ListRenderMode.Entire"
SearchMode="ListSearchMode.AutoSearch"
SearchFilterCondition="ListSearchFilterCondition.Contains"
ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto">
<Columns>
<DxListEditorColumn FieldName="@nameof(Shipping.Id)" />
<DxListEditorColumn FieldName="@nameof(Shipping.ShippingDate)" />
<DxListEditorColumn FieldName="@nameof(Shipping.LicencePlate)" />
</Columns>
</DxComboBoxSettings>
</EditSettings>
</DxGridDataColumn>
<MgGridWithInfoPanel ShowInfoPanel="@IsMasterGrid">
<GridContent>
<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" />
<DxGridDataColumn FieldName="PartnerId" Caption="Partner" Visible="@(!ParentDataItemIsPartner)" ReadOnly="@ParentDataItemIsPartner">
<EditSettings>
<DxComboBoxSettings Data="Partners"
ValueFieldName="Id"
TextFieldName="Name"
DropDownBodyCssClass="dd-body-class"
ListRenderMode="ListRenderMode.Entire"
SearchMode="ListSearchMode.AutoSearch"
SearchFilterCondition="ListSearchFilterCondition.Contains"
ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto">
<Columns>
<DxListEditorColumn FieldName="@nameof(Partner.Id)" />
<DxListEditorColumn FieldName="@nameof(Partner.Name)" />
<DxListEditorColumn FieldName="@nameof(Partner.TaxId)" />
</Columns>
</DxComboBoxSettings>
</EditSettings>
</DxGridDataColumn>
<DxGridDataColumn FieldName="ShippingId" Caption="Shipping" Visible="@(!ParentDataItemIsShipping)" ReadOnly="@ParentDataItemIsShipping">
<EditSettings>
<DxComboBoxSettings Data="Shippings"
ValueFieldName="Id"
TextFieldName="ShippingDate"
DropDownBodyCssClass="dd-body-class"
ListRenderMode="ListRenderMode.Entire"
SearchMode="ListSearchMode.AutoSearch"
SearchFilterCondition="ListSearchFilterCondition.Contains"
ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto">
<Columns>
<DxListEditorColumn FieldName="@nameof(Shipping.Id)" />
<DxListEditorColumn FieldName="@nameof(Shipping.ShippingDate)" />
<DxListEditorColumn FieldName="@nameof(Shipping.LicencePlate)" />
</Columns>
</DxComboBoxSettings>
</EditSettings>
</DxGridDataColumn>
<DxGridDataColumn FieldName="DocumentIdNumber" />
<DxGridDataColumn FieldName="ShippingDate" />
<DxGridDataColumn FieldName="Country" />
<DxGridDataColumn FieldName="DocumentIdNumber" />
<DxGridDataColumn FieldName="ShippingDate" />
<DxGridDataColumn FieldName="Country" />
<DxGridDataColumn FieldName="TotalPallets" />
<DxGridDataColumn FieldName="IsAllMeasured" ReadOnly="true" />
<DxGridDataColumn FieldName="Comment" />
<DxGridDataColumn FieldName="PdfFileName" />
<DxGridDataColumn FieldName="Created" ReadOnly="true" />
<DxGridDataColumn FieldName="Modified" ReadOnly="true" />
<DxGridCommandColumn Visible="@(!IsMasterGrid)" Width="120"/>
</Columns>
<DetailRowTemplate>
@if (IsMasterGrid)
{
var shippingDocument = ((ShippingDocument)context.DataItem);
<DxGridDataColumn FieldName="TotalPallets" />
<DxGridDataColumn FieldName="IsAllMeasured" ReadOnly="true" />
<DxGridDataColumn FieldName="Comment" />
<DxGridDataColumn FieldName="PdfFileName" />
<DxGridDataColumn FieldName="Created" ReadOnly="true" />
<DxGridDataColumn FieldName="Modified" ReadOnly="true" />
<DxGridCommandColumn Visible="@(!IsMasterGrid)" Width="120"/>
</Columns>
<DetailRowTemplate>
@if (IsMasterGrid)
{
var shippingDocument = ((ShippingDocument)context.DataItem);
<DxTabs ActiveTabIndexChanged="(i) => OnActiveTabChanged(i)">
<DxTabPage Text="Szállítmány tételek">
@{
var observableShippingItem = new AcObservableCollection<ShippingItem>(shippingDocument?.ShippingItems ?? []);
<GridShippingItemTemplate ShippingItems="@observableShippingItem" ParentDataItem="@shippingDocument" />
}
</DxTabPage>
<DxTabPage Text="Mérések">
@{
var shippingItemPallets = shippingDocument?.ShippingItems?.SelectMany(oi => oi.ShippingItemPallets ?? []).ToList() ?? [];
<GridShippingItemPallets ShippingItemPallets="@shippingItemPallets" IsMasterGrid="false" />
}
</DxTabPage>
<DxTabs ActiveTabIndexChanged="(i) => OnActiveTabChanged(i)">
<DxTabPage Text="Szállítmány tételek">
@{
var observableShippingItem = new AcObservableCollection<ShippingItem>(shippingDocument?.ShippingItems ?? []);
<GridShippingItemTemplate ShippingItems="@observableShippingItem" ParentDataItem="@shippingDocument" />
}
</DxTabPage>
<DxTabPage Text="Mérések">
@{
var shippingItemPallets = shippingDocument?.ShippingItems?.SelectMany(oi => oi.ShippingItemPallets ?? []).ToList() ?? [];
<GridShippingItemPallets ShippingItemPallets="@shippingItemPallets" IsMasterGrid="false" />
}
</DxTabPage>
</DxTabs>
}
</DetailRowTemplate>
<ToolbarTemplate>
@if (IsMasterGrid)
{
<MgGridToolbarTemplate Grid="Grid" OnReloadDataClick="() => ReloadDataFromDb(true)">
<ToolbarItemsExtended>
<DxToolbarItem BeginGroup="true">
<Template Context="ctxToolbarItemFileUpload">
@* <DxUpload Name="myFile"
UploadMode="@UploadMode.Instant"
AllowMultiFileUpload="false"
ShowSelectButton="true"
MaxFileSize="15000000">
</DxUpload> *@
<FileUpload OnFileUploaded="OnFileUploaded"></FileUpload>
</Template>
</DxToolbarItem>
@* <DxToolbarItem BeginGroup="true">
<Template Context="toolbar_item_context">
<DxSearchBox @bind-Text="GridSearchText"
BindValueMode="BindValueMode.OnInput"
ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto"
aria-label="Search" />
</Template>
</DxToolbarItem>*@
</ToolbarItemsExtended>
</MgGridToolbarTemplate>
}
</ToolbarTemplate>
<GroupSummary>
<DxGridSummaryItem SummaryType="GridSummaryItemType.Sum"
FieldName="Quantity"
FooterColumnName="Quantity" />
<DxGridSummaryItem SummaryType="GridSummaryItemType.Sum"
FieldName="NetWeight"
FooterColumnName="NetWeight" />
<DxGridSummaryItem SummaryType="GridSummaryItemType.Sum"
FieldName="PriceInclTax"
FooterColumnName="PriceInclTax" />
</GroupSummary>
</GridShippingDocumentBase>
</DxTabs>
}
</DetailRowTemplate>
<ToolbarTemplate>
@if (IsMasterGrid)
{
<MgGridToolbarTemplate Grid="Grid" OnReloadDataClick="() => ReloadDataFromDb(true)">
<ToolbarItemsExtended>
<DxToolbarItem BeginGroup="true">
<Template Context="ctxToolbarItemFileUpload">
<FileUpload OnFileUploaded="OnFileUploaded"></FileUpload>
</Template>
</DxToolbarItem>
</ToolbarItemsExtended>
</MgGridToolbarTemplate>
}
</ToolbarTemplate>
<GroupSummary>
<DxGridSummaryItem SummaryType="GridSummaryItemType.Sum"
FieldName="Quantity"
FooterColumnName="Quantity" />
<DxGridSummaryItem SummaryType="GridSummaryItemType.Sum"
FieldName="NetWeight"
FooterColumnName="NetWeight" />
<DxGridSummaryItem SummaryType="GridSummaryItemType.Sum"
FieldName="PriceInclTax"
FooterColumnName="PriceInclTax" />
</GroupSummary>
</GridShippingDocumentBase>
</GridContent>
<ChildContent>
<GridShippingDocumentInfoPanel />
</ChildContent>
</MgGridWithInfoPanel>
@code {
//EditRow dblClick
//https://supportcenter.devexpress.com/ticket/details/t1097648/datagrid-for-blazor-how-to-start-editing-a-row-and-save-changes-without-the-command-column
//[Inject] public required ObjectLock ObjectLock { get; set; }
[Inject] public required DatabaseClient Database { get; set; }
[Inject] public required LoggedInModel LoggedInModel { get; set; }
@ -152,7 +141,6 @@
public bool ParentDataItemIsPartner => (ParentDataItem is Partner);
string GridCss => !IsMasterGrid ? "hide-toolbar" : string.Empty;
const string ExportFileName = "ExportResult";
string _localStorageKey = "GridShippingDocument_";
@ -229,7 +217,6 @@
if (OnUploadedFileParsed != null)
await OnUploadedFileParsed(result);
//await InvokeAsync(StateHasChanged);
}
async Task Grid_FocusedRowChanged(GridFocusedRowChangedEventArgs args)
@ -244,22 +231,5 @@
protected async Task OnActiveTabChanged(int activeTabIndex)
{
_activeTabIndex = activeTabIndex;
return;
// switch (_activeTabIndex)
// {
// case 0:
// if(ProductDtos == null)
// ProductDtos = (await FruitBankSignalRClient.GetProductDtos() ?? []); //.Where(o => o.HasMeasuringAccess(LoggedInModel.CustomerDto?.Id, LoggedInModel.IsRevisor)).OrderBy(o => o.DateOfReceipt).ToList();
// break;
// case 1:
// if(OrderDtos == null)
// OrderDtos = (await FruitBankSignalRClient.GetAllOrderDtos() ?? []).OrderByDescending(o => o.Id).ToList(); //.Where(o => o.HasMeasuringAccess(LoggedInModel.CustomerDto?.Id, LoggedInModel.IsRevisor)).OrderBy(o => o.DateOfReceipt).ToList();
// break;
// case 2:
// if (OrderItemDtos == null)
// OrderItemDtos = (await FruitBankSignalRClient.GetAllOrderItemDtos() ?? []).OrderByDescending(o => o.Id).ToList(); //.Where(o => o.HasMeasuringAccess(LoggedInModel.CustomerDto?.Id, LoggedInModel.IsRevisor)).OrderBy(o => o.DateOfReceipt).ToList();
// break;
// }
}
}

View File

@ -19,9 +19,6 @@ public class GridShippingDocumentBase : FruitBankGridBase<ShippingDocument>, IGr
AddMessageTag = SignalRTags.AddShippingDocument;
UpdateMessageTag = SignalRTags.UpdateShippingDocument;
// Set custom InfoPanel type
InfoPanelType = typeof(GridShippingDocumentInfoPanel);
//RemoveMessageTag = SignalRTags.;
}

View File

@ -1,41 +0,0 @@
using AyCode.Blazor.Components.Components.Grids;
using Microsoft.AspNetCore.Components;
namespace FruitBankHybrid.Shared.Components.Grids.ShippingDocuments;
/// <summary>
/// Custom InfoPanel for ShippingDocument grid with customized header and footer
/// </summary>
public class GridShippingDocumentInfoPanel : MgInfoPanelTemplateBase
{
protected override RenderFragment? GetHeaderTemplate() => builder =>
{
builder.OpenElement(0, "div");
builder.AddAttribute(1, "class", "p-2 border-bottom");
builder.OpenElement(2, "h5");
builder.AddAttribute(3, "class", "mb-0");
builder.AddContent(4, "?? Szállítólevél részletei");
builder.CloseElement();
builder.CloseElement();
};
protected override RenderFragment? GetFooterTemplate() => builder =>
{
builder.OpenElement(0, "div");
builder.AddAttribute(1, "class", "p-2 border-top d-flex gap-2");
builder.OpenElement(2, "button");
builder.AddAttribute(3, "class", "btn btn-sm btn-outline-primary");
builder.AddContent(4, "??? Nyomtatás");
builder.CloseElement();
builder.OpenElement(5, "button");
builder.AddAttribute(6, "class", "btn btn-sm btn-outline-secondary");
builder.AddContent(7, "?? Export");
builder.CloseElement();
builder.CloseElement();
};
// GetColumnsTemplate() returns null by default ? uses auto-generated columns
}

View File

@ -0,0 +1,28 @@
@using AyCode.Blazor.Components.Components.Grids
@using DevExpress.Blazor
@using FruitBank.Common.Entities
<MgGridInfoPanel>
<HeaderTemplate Context="dataItem">
<div class="dxbl-grid-header-panel px-3 py-2 border-bottom d-flex align-items-center">
<span class="me-2">??</span>
<span class="fw-semibold">Szállítólevél részletei</span>
</div>
</HeaderTemplate>
<BeforeColumnsTemplate Context="dataItem">
@if (dataItem is ShippingDocument doc)
{
<div class="alert alert-info mb-2 py-1 px-2 small">
<strong>Partner:</strong> @doc.Partner?.Name
</div>
}
</BeforeColumnsTemplate>
<FooterTemplate Context="dataItem">
<div class="p-2 border-top d-flex gap-2">
<DxButton Text="Nyomtatás" IconCssClass="dx-icon dx-icon-print" RenderStyle="ButtonRenderStyle.Light" />
<DxButton Text="Export" IconCssClass="dx-icon dx-icon-export" RenderStyle="ButtonRenderStyle.Light" />
</div>
</FooterTemplate>
</MgGridInfoPanel>