Learnings
Key insights discovered during the frontend implementation of Item Image Upload.
TanStack Query integration
Section titled “TanStack Query integration”- What worked: Mutation hooks (
useMutation) for upload, reachability check, and external image fetch compose cleanly. Each hook exposesmutateAsync,isPending,error, andreset— the bridge hook (useItemImageUploader) simply composes three mutations into oneImageUploaderinterface. - Surprise:
mutateAsyncreferences from TanStack are stable across renders, so manualuseMemo/useCallbackwrapping is unnecessary and conflicts with React Compiler’spreserve-manual-memoizationrule. Let the compiler handle memoization.
Design-system typed provider pattern (FD-01)
Section titled “Design-system typed provider pattern (FD-01)”- What worked: The
ImageUploadProvider+useImageUploadercontext pattern eliminated 3 callback props per consumer component and removed parallel-implementation drift betweenItemCardEditorandImageUploadDialog. - 5.0.0 migration was smooth: Consumer changes were mechanical — replace
3 props with a
<Provider value={uploader}>wrapper. Test mocks needed updating but followed a predictable pattern. - Lesson: When a component needs more than 2 injectable behaviors that share a common concern, context is the right abstraction from the start. The 3-callback approach accumulated technical debt across 4.11.x versions.
AG Grid cell editor lifecycle (FD-20)
Section titled “AG Grid cell editor lifecycle (FD-20)”- Key discovery: AG Grid’s cell-editor lifecycle is fundamentally
incompatible with React portal-based modal editors.
cellEditorPopup: truewraps the editor in a 0x0 container (hidden subtree defeats Radix focus traps). Without it,stopEditingWhenCellsLoseFocustears down the editor when the portal steals focus. - Pattern established: Grid becomes the trigger (double-click / Enter /
F2), the wrapper component owns the editor lifecycle, and commit flows back
through
applyTransaction+ dirty-row tracking. This pattern is generalizable to any portalled modal editor in AG Grid.
Canvas export and react-easy-crop coordinate spaces
Section titled “Canvas export and react-easy-crop coordinate spaces”- Hard lesson:
react-easy-crop’scroppedAreaPixelsis always clamped to the image’s natural pixel bounds, even at zoom < 1. At zoom = 0.5, it returns{x:0, y:0, width:W, height:H}(the full image), not a rect extending beyond bounds. The zoom scalar is a separate value that must be applied in the canvas export. - Three failed fix attempts before correctly diagnosing the coordinate
space: (1) clamping negative coords that were never negative, (2) removing
zoom entirely, (3) two-path draw that still took the wrong path because
onCropCompletefires with non-zero width at zoom < 1. - Takeaway: Unit tests with 1x1 mock images cannot validate coordinate geometry. The only reliable validation for canvas export is visual verification at realistic dimensions, or a Storybook play function that asserts pixel colors on the output canvas.
CDN dual-mode auth (FD-22)
Section titled “CDN dual-mode auth (FD-22)”- Problem: Amplify preview deployments and localhost are not on
*.arda.cards— CloudFront signed cookies (domain-scoped) don’t work. - Solution:
CdnAuthProvidertransparently detects the origin and switches between signed cookies (production) and signed URLs (preview / localhost). Components useuseResolvedImageUrland never know which mode is active. - Surprise: The
ResolvedImageCellDisplaywrapper was needed for AG Grid because cell renderers can’t call hooks directly — the wrapper provides the hook context.
BFF route structure (FD-02)
Section titled “BFF route structure (FD-02)”- What worked:
src/server/for server-only code,src/api/for SPA API functions,src/app/api/for thin Next.js route re-exports. Clean separation of concerns. - All routes need
export const runtime = 'nodejs'— without it, Next.js may attempt Edge deployment, causing runtime errors ondns,crypto, and AWS SDK imports. This was caught by Copilot review. - SSRF protection must check DNS resolution — hostname validation alone is insufficient; a public hostname can resolve to a private IP.
Rebase and conflict management
Section titled “Rebase and conflict management”- CHANGELOG conflicts are inevitable in multi-branch workflows. The resolution pattern is: keep both version entries, ensure descending semver order, verify CLQ accepts the result.
- Squash-merge + rebase = lost entries: When a branch is squash-merged to main and another branch rebases onto main, the squash’d CHANGELOG entry can be silently dropped if both branches modified the same region. Always verify CHANGELOG completeness after rebase.
Copyright: (c) Arda Systems 2025-2026, All rights reserved
Copyright: © Arda Systems 2025-2026, All rights reserved