AG Grid Issues Analysis
Consolidated issue list for the AG Grid implementation in arda-frontend-app, merging the deep-dive inventory and the compliance audit. Includes a phased implementation plan for restoring normal AG Grid behavior.
Date: 2026-02-23
High-Risk Issues
Section titled “High-Risk Issues”H1 — Two Competing Column Persistence Systems
Section titled “H1 — Two Competing Column Persistence Systems”Files: ArdaGrid.tsx lines 524–745, ItemTableAGGrid.tsx lines 1831–1994
Both ArdaGrid and ItemTableAGGrid write to localStorage key items-grid-${activeTab} at overlapping times, overwriting each other.
Impact: Column order resets after switching tabs. View dropdown visibility toggles sometimes revert.
Resolution: One owner for persistence. Either ArdaGrid owns it entirely (pass enableColumnStatePersistence={false} from ItemTableAGGrid), or ItemTableAGGrid owns it and ArdaGrid persistence is disabled.
H2 — ~20 setTimeout Calls Used for State Coordination
Section titled “H2 — ~20 setTimeout Calls Used for State Coordination”Files: ArdaGrid.tsx, ItemTableAGGrid.tsx
Timing delays coordinate state between layers instead of proper AG Grid lifecycle events. Delays in use: 0, 50, 100, 150, 200, 300, 400, 500, 600, 1500, 2000ms.
Impact: On slow machines the 200ms assumption that columns are ready breaks. Any change to render timing breaks coordination.
Resolution: Use AG Grid lifecycle events: onGridReady for column state, onFirstDataRendered for selection restore.
H3 — Column Visibility Has Three Sources of Truth
Section titled “H3 — Column Visibility Has Three Sources of Truth”Files: ArdaGrid.tsx, ItemTableAGGrid.tsx
Three places all fire around mount time and set visibility:
- React render —
hide: !isVisiblefromcolumnVisibilityprop on each column def. ArdaGridpersistence restore —api.applyColumnState()200ms after mount.ItemTableAGGrid handleGridReady—api.setColumnsVisible()for each column.
Impact: Columns flash visible then hide on first load. Tab switch causes visibility bleed.
Resolution: Make columnVisibility prop the single runtime source of truth. Only persist column order and width — not visibility — in localStorage.
H4 — Select Editors Missing destroy() — Memory Leak
Section titled “H4 — Select Editors Missing destroy() — Memory Leak”File: ItemTableAGGrid.tsx — 5 class-based select editors
afterGuiAttached() adds event listeners that are never removed because destroy() is not implemented.
Resolution: Replace all 5 with AG Grid’s built-in agSelectCellEditor (see compliance-refactor.md H1 for the before/after pattern).
H5 — QuickActionsCell Fires N+1 API Requests on Mount
Section titled “H5 — QuickActionsCell Fires N+1 API Requests on Mount”File: columnPresets.tsx — QuickActionsCell renderer
50 visible rows = 50 extra API calls on every page load.
Resolution: Move card count fetching to the grid data source.
H6 — Select-All Fires N Selection-Changed Events
Section titled “H6 — Select-All Fires N Selection-Changed Events”Files: columnPresets.tsx, DesktopScanView.tsx
node.setSelected() per node fires one event per row.
Resolution: Use api.setNodesSelected({ nodes, newValue }) or AG Grid’s built-in rowSelection.headerCheckbox.
H7 — exportDataAsExcel Calls Enterprise API
Section titled “H7 — exportDataAsExcel Calls Enterprise API”File: ArdaGrid.tsx
Enterprise API called with only Community modules registered — throws at runtime.
Resolution: Remove from ArdaGrid API, or replace with exportDataAsCsv().
H8 — Full Data Refresh Triggered on Every Cell Edit
Section titled “H8 — Full Data Refresh Triggered on Every Cell Edit”File: ItemTableAGGrid.tsx
Edit → publish → onRefreshRequested() → full data reload → selection lost → polling loop. Rapid edits create draft conflicts.
Resolution: Use gridApi.applyTransaction({ update: [updatedItem] }) for incremental updates. Debounce onRefreshRequested.
Medium-Risk Issues
Section titled “Medium-Risk Issues”| ID | Issue | Location | Resolution |
|---|---|---|---|
| M1 | getOrderedColumnDefs reads localStorage on every render — not in useMemo | ItemTableAGGrid.tsx | Wrap in useMemo([activeTab, columnVisibility]) |
| M2 | Module-level lastSelectedRowIndex shared across all grid instances | columnPresets.tsx, DesktopScanView.tsx | Move to useRef per instance; eliminated by H6 fix |
| M3 | headerStyle silently ignored — not a valid ColDef property | columnPresets.tsx | Replace with headerClass |
| M4 | Inline cellRenderer arrow functions recreate component on every render | columnPresets.tsx | Use valueFormatter for text-only columns |
| M5 | Direct mutation of rowData objects bypasses React immutability | ItemTableAGGrid.tsx | Use applyTransaction |
| M6 | document.querySelector('.ag-theme-arda') matches wrong grid when scan modal is open | ItemTableAGGrid.tsx | Use a React ref to the grid container |
| M7 | afterGuiAttached focus-click loop in select editors | ItemTableAGGrid.tsx | Eliminated by H4 fix |
| M8 | QuickActions buttons overflow at 480px breakpoint | ArdaGrid.css | Increase min-row-height or clip overflow |
Low-Risk Issues
Section titled “Low-Risk Issues”| ID | Issue | Location | Fix |
|---|---|---|---|
| L1 | suppressMultiRangeSelection removed in v33+ | ArdaGrid.tsx | Remove |
| L2 | suppressSizeToFit deprecated in v31 | columnPresets.tsx | Replace with suppressAutoSize |
| L3 | RowNode import should be IRowNode in v34 | columnPresets.tsx | Update import |
| L4 | rowSelection: 'single' deprecated syntax | ArdaGrid.tsx | Update to { mode: 'singleRow' } |
| L5 | SelectAllHeaderComponent typed as any | columnPresets.tsx | Add proper typing |
| L6 | Supplier URL link missing onMouseDown stopPropagation | columnPresets.tsx | Add handler |
| L7 | 300ms single-click delay feels sluggish | ItemTableAGGrid.tsx | Reduce to 200ms |
| L8 | Field-to-ViewKey mapping duplicated 3 times in same file | ItemTableAGGrid.tsx | Extract single FIELD_VIEW_MAP constant |
Patterns to Keep
Section titled “Patterns to Keep”- Typeahead editors implement full
destroy()lifecycle withsetTimeout(() => root.unmount(), 0)— correct for React 19. createPortalfor modals and typeahead dropdowns escapesoverflow: hiddencells.stopPropagationon interactive elements prevents unwanted row selection.isCancelAfterEnd()+wasCancelledflag correctly handles ESC.- Draft deduplication via
draftPromisesMapRefprevents concurrent draft creation. publishingRowsRefprevents concurrent publishes of the same row.
Phased Implementation Plan
Section titled “Phased Implementation Plan”Phase 1 — Stop the Bleeding
Section titled “Phase 1 — Stop the Bleeding”Fix bugs causing crashes and memory leaks with no visible UI change.
- Step 1.1 — Remove
exportDataAsExcelfromArdaGridinterface anduseImperativeHandle(H7). - Step 1.2 — Add
destroy()to all 5 select editors, storing the focus handler as a named reference so it can be removed (H4). - Step 1.3 — Fix deprecated API warnings: update
rowSelectionto new object syntax, removesuppressMultiRangeSelection(L1, L4).
Phase 2 — Fix State Ownership
Section titled “Phase 2 — Fix State Ownership”Eliminate competing writes to localStorage and the three-source visibility problem.
- Step 2.1 — Pass
enableColumnStatePersistence={false}toArdaGridfromItemTableAGGrid(H1). - Step 2.2 — Remove
hide: !isVisiblefrombaseColumnDefs; lethandleGridReadybe the sole visibility setter (H3). - Step 2.3 — Remove double-save
setTimeout(saveGridStateOnReady, 400/600)calls (H2 partial). - Step 2.4 — Wrap
getOrderedColumnDefs()inuseMemo([activeTab])(M1).
Phase 3 — Fix Selection and Rendering Performance
Section titled “Phase 3 — Fix Selection and Rendering Performance”- Step 3.1 — Replace
node.setSelected()loop withapi.setNodesSelected({ nodes, newValue })(H6). - Step 3.2 — Move
lastSelectedRowIndextouseRefper grid instance (M2). - Step 3.3 — Switch text-only columns from
cellRenderertovalueFormatter(M4).
Phase 4 — Fix the Edit → Refresh → Flicker Loop
Section titled “Phase 4 — Fix the Edit → Refresh → Flicker Loop”- Step 4.1 — Use
applyTransactionfor notes mutations instead of direct mutation (M5, H8 partial). - Step 4.2 — Debounce
onRefreshRequestedto batch rapid edits into one reload (H8). - Step 4.3 — Replace selection polling loop with
onFirstDataRenderedevent (H2 partial).
Phase 5 — Code Reduction
Section titled “Phase 5 — Code Reduction”- Step 5.1 — Extract single
FIELD_VIEW_MAPconstant from 3 duplicated mappings (L8). - Step 5.2 — Replace
headerStylewithheaderClass(M3). - Step 5.3 — Replace
document.querySelectorwith a React ref (M6).
Phases 1 and 2 are the minimum needed to stop the most disruptive bugs. Phases 3–5 improve performance and reduce maintenance burden. Phase 4 Step 2 (H5 QuickActionsCell refactor) is the most complex and may be deferred to a separate sprint.
Impact Summary
Section titled “Impact Summary”| Change | Lines Removed | Lines Added |
|---|---|---|
| H4: Replace 5 select editors | ~500 | ~15 |
| H6: Replace custom select-all | ~189 | ~5 |
| Typeahead editor factory | ~1,170 | ~30 |
M4: valueFormatter for text columns | ~50 | ~50 |
| M5: Immutable rowData updates | ~5 | ~5 |
| H5: Batch card data fetching | ~30 | ~50 |
| H7: Fix or remove Excel export | ~5 | ~3 |
| H1+H3: Single persistence owner | ~200 | ~20 |
H2: Remove setTimeout coordination | ~60 | ~0 |
| L1–L8, M3, M6, M8 cleanup | ~30 | ~20 |
| Total | ~2,239 | ~198 |
Net reduction: approximately 2,040 lines of custom code replaced by AG Grid built-ins and cleaner patterns.
Copyright: © Arda Systems 2025-2026, All rights reserved