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 IEnumerable<IAcLogWriterClientBase> LogWriters
@inject FruitBankSignalRClient FruitBankSignalRClient @inject FruitBankSignalRClient FruitBankSignalRClient
<GridShippingDocumentBase @ref="Grid" CssClass="@GridCss" DataSource="@ShippingDocuments" SignalRClient="FruitBankSignalRClient" AutoSaveLayoutName="GridShippingDocument" <MgGridWithInfoPanel ShowInfoPanel="@IsMasterGrid">
ParentDataItem="@ParentDataItem" Logger="_logger" ValidationEnabled="false" EditMode="GridEditMode.EditRow" OnGridFocusedRowChanged="Grid_FocusedRowChanged"> <GridContent>
<Columns> <GridShippingDocumentBase @ref="Grid" CssClass="@GridCss" DataSource="@ShippingDocuments" SignalRClient="FruitBankSignalRClient" AutoSaveLayoutName="GridShippingDocument"
<DxGridDataColumn FieldName="Id" SortIndex="0" SortOrder="GridColumnSortOrder.Descending" ReadOnly="true" /> ParentDataItem="@ParentDataItem" Logger="_logger" ValidationEnabled="false" EditMode="GridEditMode.EditRow" OnGridFocusedRowChanged="Grid_FocusedRowChanged">
<DxGridDataColumn FieldName="PartnerId" Caption="Partner" Visible="@(!ParentDataItemIsPartner)" ReadOnly="@ParentDataItemIsPartner"> <Columns>
<EditSettings> <DxGridDataColumn FieldName="Id" SortIndex="0" SortOrder="GridColumnSortOrder.Descending" ReadOnly="true" />
<DxComboBoxSettings Data="Partners" <DxGridDataColumn FieldName="PartnerId" Caption="Partner" Visible="@(!ParentDataItemIsPartner)" ReadOnly="@ParentDataItemIsPartner">
ValueFieldName="Id" <EditSettings>
TextFieldName="Name" <DxComboBoxSettings Data="Partners"
DropDownBodyCssClass="dd-body-class" ValueFieldName="Id"
ListRenderMode="ListRenderMode.Entire" TextFieldName="Name"
SearchMode="ListSearchMode.AutoSearch" DropDownBodyCssClass="dd-body-class"
SearchFilterCondition="ListSearchFilterCondition.Contains" ListRenderMode="ListRenderMode.Entire"
ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto"> SearchMode="ListSearchMode.AutoSearch"
<Columns> SearchFilterCondition="ListSearchFilterCondition.Contains"
<DxListEditorColumn FieldName="@nameof(Partner.Id)" /> ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto">
<DxListEditorColumn FieldName="@nameof(Partner.Name)" /> <Columns>
<DxListEditorColumn FieldName="@nameof(Partner.TaxId)" /> <DxListEditorColumn FieldName="@nameof(Partner.Id)" />
</Columns> <DxListEditorColumn FieldName="@nameof(Partner.Name)" />
</DxComboBoxSettings> <DxListEditorColumn FieldName="@nameof(Partner.TaxId)" />
</EditSettings> </Columns>
</DxGridDataColumn> </DxComboBoxSettings>
<DxGridDataColumn FieldName="ShippingId" Caption="Shipping" Visible="@(!ParentDataItemIsShipping)" ReadOnly="@ParentDataItemIsShipping"> </EditSettings>
<EditSettings> </DxGridDataColumn>
<DxComboBoxSettings Data="Shippings" <DxGridDataColumn FieldName="ShippingId" Caption="Shipping" Visible="@(!ParentDataItemIsShipping)" ReadOnly="@ParentDataItemIsShipping">
ValueFieldName="Id" <EditSettings>
TextFieldName="ShippingDate" <DxComboBoxSettings Data="Shippings"
DropDownBodyCssClass="dd-body-class" ValueFieldName="Id"
ListRenderMode="ListRenderMode.Entire" TextFieldName="ShippingDate"
SearchMode="ListSearchMode.AutoSearch" DropDownBodyCssClass="dd-body-class"
SearchFilterCondition="ListSearchFilterCondition.Contains" ListRenderMode="ListRenderMode.Entire"
ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto"> SearchMode="ListSearchMode.AutoSearch"
<Columns> SearchFilterCondition="ListSearchFilterCondition.Contains"
<DxListEditorColumn FieldName="@nameof(Shipping.Id)" /> ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto">
<DxListEditorColumn FieldName="@nameof(Shipping.ShippingDate)" /> <Columns>
<DxListEditorColumn FieldName="@nameof(Shipping.LicencePlate)" /> <DxListEditorColumn FieldName="@nameof(Shipping.Id)" />
</Columns> <DxListEditorColumn FieldName="@nameof(Shipping.ShippingDate)" />
</DxComboBoxSettings> <DxListEditorColumn FieldName="@nameof(Shipping.LicencePlate)" />
</EditSettings> </Columns>
</DxGridDataColumn> </DxComboBoxSettings>
</EditSettings>
</DxGridDataColumn>
<DxGridDataColumn FieldName="DocumentIdNumber" /> <DxGridDataColumn FieldName="DocumentIdNumber" />
<DxGridDataColumn FieldName="ShippingDate" /> <DxGridDataColumn FieldName="ShippingDate" />
<DxGridDataColumn FieldName="Country" /> <DxGridDataColumn FieldName="Country" />
<DxGridDataColumn FieldName="TotalPallets" /> <DxGridDataColumn FieldName="TotalPallets" />
<DxGridDataColumn FieldName="IsAllMeasured" ReadOnly="true" /> <DxGridDataColumn FieldName="IsAllMeasured" ReadOnly="true" />
<DxGridDataColumn FieldName="Comment" /> <DxGridDataColumn FieldName="Comment" />
<DxGridDataColumn FieldName="PdfFileName" /> <DxGridDataColumn FieldName="PdfFileName" />
<DxGridDataColumn FieldName="Created" ReadOnly="true" /> <DxGridDataColumn FieldName="Created" ReadOnly="true" />
<DxGridDataColumn FieldName="Modified" ReadOnly="true" /> <DxGridDataColumn FieldName="Modified" ReadOnly="true" />
<DxGridCommandColumn Visible="@(!IsMasterGrid)" Width="120"/> <DxGridCommandColumn Visible="@(!IsMasterGrid)" Width="120"/>
</Columns> </Columns>
<DetailRowTemplate> <DetailRowTemplate>
@if (IsMasterGrid) @if (IsMasterGrid)
{ {
var shippingDocument = ((ShippingDocument)context.DataItem); var shippingDocument = ((ShippingDocument)context.DataItem);
<DxTabs ActiveTabIndexChanged="(i) => OnActiveTabChanged(i)"> <DxTabs ActiveTabIndexChanged="(i) => OnActiveTabChanged(i)">
<DxTabPage Text="Szállítmány tételek"> <DxTabPage Text="Szállítmány tételek">
@{ @{
var observableShippingItem = new AcObservableCollection<ShippingItem>(shippingDocument?.ShippingItems ?? []); var observableShippingItem = new AcObservableCollection<ShippingItem>(shippingDocument?.ShippingItems ?? []);
<GridShippingItemTemplate ShippingItems="@observableShippingItem" ParentDataItem="@shippingDocument" /> <GridShippingItemTemplate ShippingItems="@observableShippingItem" ParentDataItem="@shippingDocument" />
} }
</DxTabPage> </DxTabPage>
<DxTabPage Text="Mérések"> <DxTabPage Text="Mérések">
@{ @{
var shippingItemPallets = shippingDocument?.ShippingItems?.SelectMany(oi => oi.ShippingItemPallets ?? []).ToList() ?? []; var shippingItemPallets = shippingDocument?.ShippingItems?.SelectMany(oi => oi.ShippingItemPallets ?? []).ToList() ?? [];
<GridShippingItemPallets ShippingItemPallets="@shippingItemPallets" IsMasterGrid="false" /> <GridShippingItemPallets ShippingItemPallets="@shippingItemPallets" IsMasterGrid="false" />
} }
</DxTabPage> </DxTabPage>
</DxTabs> </DxTabs>
} }
</DetailRowTemplate> </DetailRowTemplate>
<ToolbarTemplate> <ToolbarTemplate>
@if (IsMasterGrid) @if (IsMasterGrid)
{ {
<MgGridToolbarTemplate Grid="Grid" OnReloadDataClick="() => ReloadDataFromDb(true)"> <MgGridToolbarTemplate Grid="Grid" OnReloadDataClick="() => ReloadDataFromDb(true)">
<ToolbarItemsExtended> <ToolbarItemsExtended>
<DxToolbarItem BeginGroup="true"> <DxToolbarItem BeginGroup="true">
<Template Context="ctxToolbarItemFileUpload"> <Template Context="ctxToolbarItemFileUpload">
@* <DxUpload Name="myFile" <FileUpload OnFileUploaded="OnFileUploaded"></FileUpload>
UploadMode="@UploadMode.Instant" </Template>
AllowMultiFileUpload="false" </DxToolbarItem>
ShowSelectButton="true" </ToolbarItemsExtended>
MaxFileSize="15000000"> </MgGridToolbarTemplate>
</DxUpload> *@ }
<FileUpload OnFileUploaded="OnFileUploaded"></FileUpload> </ToolbarTemplate>
</Template> <GroupSummary>
</DxToolbarItem> <DxGridSummaryItem SummaryType="GridSummaryItemType.Sum"
@* <DxToolbarItem BeginGroup="true"> FieldName="Quantity"
<Template Context="toolbar_item_context"> FooterColumnName="Quantity" />
<DxSearchBox @bind-Text="GridSearchText" <DxGridSummaryItem SummaryType="GridSummaryItemType.Sum"
BindValueMode="BindValueMode.OnInput" FieldName="NetWeight"
ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto" FooterColumnName="NetWeight" />
aria-label="Search" /> <DxGridSummaryItem SummaryType="GridSummaryItemType.Sum"
</Template> FieldName="PriceInclTax"
</DxToolbarItem>*@ FooterColumnName="PriceInclTax" />
</ToolbarItemsExtended> </GroupSummary>
</MgGridToolbarTemplate> </GridShippingDocumentBase>
} </GridContent>
</ToolbarTemplate> <ChildContent>
<GroupSummary> <GridShippingDocumentInfoPanel />
<DxGridSummaryItem SummaryType="GridSummaryItemType.Sum" </ChildContent>
FieldName="Quantity" </MgGridWithInfoPanel>
FooterColumnName="Quantity" />
<DxGridSummaryItem SummaryType="GridSummaryItemType.Sum"
FieldName="NetWeight"
FooterColumnName="NetWeight" />
<DxGridSummaryItem SummaryType="GridSummaryItemType.Sum"
FieldName="PriceInclTax"
FooterColumnName="PriceInclTax" />
</GroupSummary>
</GridShippingDocumentBase>
@code { @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 DatabaseClient Database { get; set; }
[Inject] public required LoggedInModel LoggedInModel { get; set; } [Inject] public required LoggedInModel LoggedInModel { get; set; }
@ -152,7 +141,6 @@
public bool ParentDataItemIsPartner => (ParentDataItem is Partner); public bool ParentDataItemIsPartner => (ParentDataItem is Partner);
string GridCss => !IsMasterGrid ? "hide-toolbar" : string.Empty; string GridCss => !IsMasterGrid ? "hide-toolbar" : string.Empty;
const string ExportFileName = "ExportResult"; const string ExportFileName = "ExportResult";
string _localStorageKey = "GridShippingDocument_"; string _localStorageKey = "GridShippingDocument_";
@ -229,7 +217,6 @@
if (OnUploadedFileParsed != null) if (OnUploadedFileParsed != null)
await OnUploadedFileParsed(result); await OnUploadedFileParsed(result);
//await InvokeAsync(StateHasChanged);
} }
async Task Grid_FocusedRowChanged(GridFocusedRowChangedEventArgs args) async Task Grid_FocusedRowChanged(GridFocusedRowChangedEventArgs args)
@ -244,22 +231,5 @@
protected async Task OnActiveTabChanged(int activeTabIndex) protected async Task OnActiveTabChanged(int activeTabIndex)
{ {
_activeTabIndex = 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; AddMessageTag = SignalRTags.AddShippingDocument;
UpdateMessageTag = SignalRTags.UpdateShippingDocument; UpdateMessageTag = SignalRTags.UpdateShippingDocument;
// Set custom InfoPanel type
InfoPanelType = typeof(GridShippingDocumentInfoPanel);
//RemoveMessageTag = SignalRTags.; //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>