# 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: ```csharp 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`. ```csharp 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` 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` ```csharp public override async Task> 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. ```