AyCode.Blazor/AyCode.Blazor.Components/docs/MGGRID/MGGRID_TODO.md

9.4 KiB

MGGRID — TODO

For known issues / bugs see MGGRID_ISSUES.md.

Server-side grid filtering — design in ADR 0004 (AyCode.Core/docs/adr/0004-signalr-datasource-server-side-filtering.md, in AyCode.Core repo).

Landed (2026-07-16/17), local mode: the declarative filter panel — FilterPanel slot, MgGridFilterPanelTagBox (+ IMgGridFilterPanelComponent), per-field IN criteria via SetFilterPanelCriteria → DevExpress SetFieldFilterCriteria, toolbar Show/Hide toggle, built-in Clear button, layout persistence + visibility reconcile. See MGGRID_PARAMETERS.md → Filter Panel.

Still open (not yet broken into per-step entries — reference the ADR when implementation begins): FilterMode (Local/Server) + criteria forwarding into SignalParams.FilterCriteria; [FilterCriteriaParam] hub injection + registration-time convention validation + mismatch warning (AyCode.Services / .Server); the filter-service (whitelist, parse/length hard-reject); [NotGridFilterable] (AyCode.Core, deferred); v2 paging. Plus ACBLAZOR-GRID-T-W8Q3 below (the layout envelope), which the ADR designates as the successor to the current restore heal.

Priority legend

  • P0 blocker · P1 important · P2 nice-to-have · P3 idea

ACBLAZOR-GRID-T-W8Q3: MgGridLayoutSave envelope + pull-model filter-panel restore

Priority: P1 · Type: Refactor (framework-first) · Origin: 2026-07-17 multi-agent restore-race audit · Related: MGGRID_ISSUES.md#acblazor-grid-i-d4t7; design in AyCode.Core/docs/adr/0004-signalr-datasource-server-side-filtering.md → "Planned v2"

Filter-panel criteria currently persists inside the DevExpress layout (GridPersistentLayout.FilterCriteria), so on restore the criteria reaches the grid before the panel can display it. That forces the framework to detect when the editor is able to show it — the source of two shipped edge-gate bugs and of the current level-triggered snapshot heal (MgGridFilterPanelTagBox).

Replace with an envelope that gives each piece of state a single owner:

public class MgGridLayoutSave
{
    public int Version { get; set; } = 1;   // 0 = legacy raw GridPersistentLayout JSON (no envelope)
    public string? GridLayoutJson { get; set; }
    public string? FilterPanelLayoutJson { get; set; }
}
  • Save: for each registered component whose OwnsCriteria matches its field slot, REMOVE that criteria from the DX layout and serialize it into FilterPanelLayoutJson as field → invariant-culture value strings.
  • Restore (all three funnels — Grid_LayoutAutoLoading, LoadUserLayoutAsync, ResetLayoutAsync): apply the panel-free DX layout; park the panel map; hand an entry to its component only when it is ready (registered AND Data non-empty), which then sets criteria + selection atomically.
  • Version: 0 (absent property) = legacy raw layout → one-time migration extracting owned-shaped IN criteria (the only place shape-sniffing survives). Bump only for semantic changes — System.Text.Json ignores unknown properties, so additive sections need no bump.
  • Never-claimed entries stay parked for the session and are re-persisted on save (a temporarily absent panel must not silently destroy a saved filter).

Acceptance criteria

  • A restored panel filter never filters rows while its UI cannot show it (closes the rows-filtered/chips-empty window).
  • ACBLAZOR-GRID-I-D4T7 closed by construction (an ownerless entry never reaches the grid).
  • Reload(), the snapshot-heal state, the pending-reconcile flag and IMgGridBase.RefreshFilterPanel() all become deletable; RefreshFilterPanel kept one release as a deprecated no-op shim, consumer call sites removed (e.g. GridShippingDocument.ReloadDataFromDb).
  • Legacy (v0) layouts in localStorage and on the server still load.
  • Interleaving tests: fill-order swap, criteria-before-data, data-before-criteria, no-panel restore.

ACBLAZOR-GRID-T-V4P7: Generic ID generation in MgGridBase.SetNewId

Priority: P2 · Type: Refactor (framework-first) · Origin: 2026-04-24 in-code TODO audit · Area: Components/Grids/MgGridBase.cs:460

SetNewId(TDataItem dataItem) branches on dataItem.Id is Guid vs dataItem.Id is int to produce a new ID, and converts through TypeConverter back to TId. The in-code comment //TODO: int !!! - J. flags this as unfinished — the logic is not cleanly generic over TId.

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)))!;
    }
}

Problems:

  • Runtime type switch on a generic parameter defeats the point of generics.
  • Silent no-op for any other TId (e.g., long, short, custom struct) — no compile error, no throw.
  • The negative-int convention (-1 * AcDomain.NextUniqueInt32) is not expressed as a contract; consumers cannot override.

Fix options

  • (a) Introduce IAcNewIdGenerator<TId> framework abstraction (in AyCode.Core) with default implementations for Guid and int. MgGridBase takes it via DI or generic parameter. Consumer-specific TId types register their generator.
  • (b) Virtual protected method: protected virtual TId GenerateNewId() on MgGridBase, with default implementations for Guid and int preserved. Consumer overrides for custom TId.
  • (c) Static strategy map keyed by typeof(TId) — registered once per app startup, resolved at runtime.

Acceptance criteria

  • No runtime type-switch in MgGridBase.
  • Throws explicit NotSupportedException (or similar) for unregistered TId types.
  • Existing Guid and int consumers unaffected.
  • Remove the //TODO: int !!! - J. comment.

ACBLAZOR-GRID-T-S2L9: Implement local grouping in MgGridSignalRDataSource.GetGroupInfoAsync

Priority: P3 · Type: Feature · Origin: 2026-04-24 in-code TODO audit · Area: Components/Grids/MgGridSignalRDataSource.cs:202

public override async Task<IList<GridCustomDataSourceGroupInfo>> GetGroupInfoAsync(
    GridCustomDataSourceGroupingOptions options,
    CancellationToken cancellationToken)
{
    _logger?.Debug("[MgGridSignalRDataSource] GetGroupInfoAsync");

    // TODO: Implement local grouping when needed
    return await base.GetGroupInfoAsync(options, cancellationToken);
}

Currently delegates to the DevExpress base implementation, which for a server-side GridCustomDataSource triggers a server round-trip. With MgGridSignalRDataSource already holding a local cache (see MGGRID_DATASOURCE.md), grouping over the cached rows would avoid that round-trip.

Not urgent — only light grouping usage so far; the base path works. Promote to P2 if a consumer hits perceptible grouping latency.

Acceptance criteria

  • Local grouping computed from the cached list when the full dataset is cached.
  • Falls back to base (server) path when the cache is partial / paginated.
  • Unit / integration test with a grouped column.
  • Remove the // TODO: Implement local grouping when needed comment.

ACBLAZOR-GRID-T-E3B6: Fogyasztói template-ek izolálása (ErrorBoundary) + DataItem-kontraktus dokumentálása

Priority: P2 · Type: Resilience + Docs · Origin: 2026-07-13 runtime incidens (fogyasztói InfoPanel-template NRE → WASM renderer crit)

A template-hosztok (MgGridInfoPanel Header/BeforeColumns/Columns/AfterColumns/Footer template-hívásai — pl. MgGridInfoPanel.razor:44 —, és általában a fogyasztói RenderFragment-hívások) védtelenül futnak: egy fogyasztói template-ben dobott kivétel unhandled render exceptionként a WASM renderert üti meg (crit: …WebAssemblyRenderer), rosszabb esetben az egész app-ot viszi — nem csak az érintett panelt.

Trigger-incidens: add/save után a kijelölt sor a szerver-válasz nyers (reláció nélkül betöltött) entitása a grid-reloadig; a fogyasztói template null navigációt dereferált → NRE a renderben. A fogyasztói fix (null-guard a template-ben) megvan, de a keret két hiányossága marad:

  1. ErrorBoundary a template-hívások köré — a hibás template a panelen belül hibablokká szelídüljön (+ logolás), a grid/app maradjon életben. Figyelem: az ErrorBoundary az eseménykezelő-kivételeket is elkapja; recovery-stratégia kell (pl. Recover() a következő DataItem-váltásnál).
  2. DataItem-kontraktus kimondása a doksiban (MGGRID_INFOPANEL / README): a template-nek részlegesen töltött DataItem-mel KELL számolnia (null navigációs property-k) — add/save után a DataItem a szerver-válasz nyers entitása lehet, amíg a reload le nem cseréli.

Acceptance criteria

  • Fogyasztói template-kivétel nem eszkalálódik unhandled render exceptionné; a panel hibablokkot mutat és logol.
  • A következő sikeres render (pl. DataItem-váltás) után a panel magától helyreáll.
  • A DataItem-kontraktus dokumentálva.

TODO entry template

## ACBLAZOR-GRID-T-XXXX: Short title
**Priority:** P0 / P1 / P2 / P3 · **Type:** Bug fix / Feature / Refactor / Docs · **Related:** `MGGRID_ISSUES.md#acblazor-grid-i-XXXX` (if applicable)

Description of what and why, including the trigger (user request, audit finding, incident).

Options / sub-tasks / acceptance criteria.