Analysis: Frontend Implementation for Item Image Upload
Executive Summary
Section titled “Executive Summary”This document compares the current state of three repositories — api-proxy, ux-prototype, and arda-frontend-app — against the specification defined in the project goal. It identifies all gaps that must be closed before the frontend implementation for Item Image Upload is complete.
The analysis finds that api-proxy is the closest to complete: the implementation code is already written and the only remaining gap is a CHANGELOG update and a GitHub Packages publish. The ux-prototype design system requires targeted updates to five image components and the introduction of a minimal lifecycle framework; no structural changes to the package are required. arda-frontend-app has the largest gap: it needs dead-code removal, new directory structure, a new third-party dependency, four BFF route handlers, server utilities, four TanStack Query hooks, a CDN cookie lifecycle provider, grid column wiring, and form field wiring. Nothing that exists today must be moved or restructured — all new code lands in new directories.
The total work is significant but well-defined. No ambiguity remains about what needs to be built; the open question for TypeaheadCellEditor (callback vs. hook-style interface) is the only design decision still pending within scope.
Specification vs. Implementation Comparison
Section titled “Specification vs. Implementation Comparison”Repository 1: api-proxy (@arda-cards/api-proxy)
Section titled “Repository 1: api-proxy (@arda-cards/api-proxy)”The specification requires a new createImageUploadUrl() method on ItemProxy, the ImageUploadUrlRequest and ImageUploadUrlResponse types, and unit tests following the existing proxy test pattern.
Current state. All three deliverables are implemented and passing CI on the jmpicnic/image-upload-frontend branch. The package has not been published to GitHub Packages at the required version.
Gaps.
| Area | Gap | Notes |
|---|---|---|
| CHANGELOG | No entry for the new method and types | Required by CI gate on all PRs |
| GitHub Packages publish | Package not published at the version consuming repos need | Blocking for arda-frontend-app BFF routes |
No code changes are needed.
Repository 2: ux-prototype (@arda-cards/design-system)
Section titled “Repository 2: ux-prototype (@arda-cards/design-system)”The specification (Phase 4a) requires: (1) a minimal lifecycle framework — ValidationResult, FieldError, EditLifecycleCallbacks<T>, EditableComponentProps<T>, and useDraft<T>; (2) updates to five image components for FD-01 compliance and production readiness; and (3) a new published version on GitHub Packages.
Current state. 19 image components are present across PR #63 and the seb/inline-card-image-upload branch. Components use raw callback props (onUpload: (file: Blob) => Promise<string> and onCheckReachability: (url: string) => Promise<boolean>). No lifecycle framework types exist anywhere in the package. The package has not been published at the version this project requires.
Gaps.
Lifecycle framework (does not exist)
Section titled “Lifecycle framework (does not exist)”| Area | Gap | Spec reference |
|---|---|---|
ValidationResult type | Not present | FD-03 |
FieldError type | Not present | FD-03 |
EditLifecycleCallbacks<T> type | Not present | FD-03 |
EditableComponentProps<T> type | Not present | FD-03 |
useDraft<T> hook | Not present | FD-03 |
The full framework (useComposedDraft<T>, createCellEditorFactory<T>, EditablePanel<T>) is deferred to #77 and is out of scope for this project.
ImageUploadDialog
Section titled “ImageUploadDialog”| Area | Gap | Spec reference |
|---|---|---|
| Progress indicator | Uses a simulated 50 ms timer; spec requires an indeterminate indicator | FD-04 |
UploadError state | Not present; needed for production retry flow | Phase 4a |
| Lifecycle callbacks | Does not adopt EditLifecycleCallbacks<ImageUploadResult> | FD-03 |
ImageCellEditor
Section titled “ImageCellEditor”| Area | Gap | Spec reference |
|---|---|---|
| Factory signature | Does not accept typed provider hooks (useImageUpload, useCheckReachability); uses raw callback props | FD-01, section 6.3 |
TypeaheadCellEditor
Section titled “TypeaheadCellEditor”| Area | Gap | Spec reference |
|---|---|---|
| Interface style | Uses lookup: (string) => Promise<Option[]> callback; spec requires evaluation of whether to move to a hook-style interface consistent with FD-01 | FD-01 (design decision pending) |
This is the one open design decision within scope. The outcome determines whether TypeaheadCellEditor requires a signature change in this project or is left as-is with a deferred ticket.
ItemGridColumns and ItemGridLookups
Section titled “ItemGridColumns and ItemGridLookups”| Area | Gap | Spec reference |
|---|---|---|
ItemGridLookups interface coverage | Covers only 2 of 9+ lookup fields | Phase 4a |
| Image editor wiring | ImageCellEditor factory not wired into column definitions | Phase 4a |
| Image editor hook parameters | Column definitions do not thread typed provider hooks into the factory | FD-01 |
ImageFormField
Section titled “ImageFormField”| Area | Gap | Spec reference |
|---|---|---|
| Props type | Does not extend EditableComponentProps<string | null> | FD-03 |
contextErrors | Does not accept or display contextual errors from a parent | FD-03 |
Package publish
Section titled “Package publish”| Area | Gap | Notes |
|---|---|---|
| GitHub Packages publish | Package not published at the version arda-frontend-app needs to consume | Blocking for Phase 4 in arda-frontend-app |
| CHANGELOG | No entry for this project’s changes | Required by CI gate |
Repository 3: arda-frontend-app
Section titled “Repository 3: arda-frontend-app”The specification covers: legacy dead-code removal (FD-07, first commit), new directory structure (FD-02), @tanstack/react-query adoption, four BFF route handlers, server-side utilities, four TanStack Query hooks, a CDN cookie lifecycle provider, QueryClientProvider in the provider stack, grid column integration, ImageFormField wiring in ItemFormPanel, and dependency updates.
Current state. The app is a Next.js 16 BFF-architecture application using Redux for all state. @tanstack/react-table is present as a dead dependency (ItemTable.tsx is superseded by ItemTableAGGrid but not removed). No @tanstack/react-query. No src/server/, src/api/, or src/providers/ directories. src/lib/ mixes server and client code without boundary enforcement. ItemFormPanel has imageUrl in the type system with a disabled dropzone placeholder. The item grid uses ItemTableAGGrid (AG Grid) but has no image cell renderers or editors wired.
Gaps.
Legacy cleanup (first commit — FD-07)
Section titled “Legacy cleanup (first commit — FD-07)”| Area | Gap | Spec reference |
|---|---|---|
ItemTable.tsx | Dead code; not removed | FD-07 |
src/tests/itemTable.test.tsx | Dead test file; not removed | FD-07 |
@tanstack/react-table dependency | Present in package.json; no other consumers verified | FD-07 |
This commit must be isolated, pass all CI checks independently, and land before any new code is added.
Dependencies
Section titled “Dependencies”| Area | Gap | Spec reference |
|---|---|---|
@tanstack/react-query | Not present | FD-01, Phase 4a |
@tanstack/react-query-devtools | Not present | Phase 4a |
@arda-cards/design-system | Version bump needed to consume lifecycle framework and updated components | Phase 4a |
@arda-cards/api-proxy | Not a direct dependency of arda-frontend-app; BFF routes need it | Phase 3a |
Note: react-easy-crop, react-dropzone, browser-image-compression, and heic2any are design system dependencies and are not added to arda-frontend-app directly.
Directory structure (FD-02)
Section titled “Directory structure (FD-02)”| Directory | Gap | Spec reference |
|---|---|---|
src/server/ | Does not exist; needed for BFF route handlers and server utilities | FD-02 |
src/api/ | Does not exist; needed for SPA API functions layer | FD-02 |
src/hooks/image-upload/ | Does not exist; needed for TanStack mutation hooks | FD-02 |
src/hooks/cdn/ | Does not exist; needed for useCdnCookies | FD-02 |
src/providers/ | Does not exist; needed for QueryClientProvider and CdnCookieProvider | FD-02 |
BFF route handlers (Phase 3a and 3b)
Section titled “BFF route handlers (Phase 3a and 3b)”| Route | Gap | Spec reference |
|---|---|---|
POST /api/image-upload | Not present; proxies to Backend via api-proxy, adds auth and tenant headers | Phase 3a |
POST /api/storage/check-url | Not present; SSRF-protected URL reachability check | Phase 3a |
POST /api/storage/fetch-url | Not present; SSRF-protected image fetch proxy, max 10 MB, 10s timeout | Phase 3a |
POST /api/storage/cdn-cookies | Not present; generates CloudFront custom-policy cookies scoped to tenant | Phase 3b |
Server-side utilities (Phase 3a and 3b)
Section titled “Server-side utilities (Phase 3a and 3b)”| Utility | Gap | Spec reference |
|---|---|---|
cloudfront-signer.ts | Not present; signs CloudFront custom policy with RSA key from Secrets Manager or env | Phase 3b |
ssrf-validator.ts | Not present; HTTPS-only, private IP rejection, managed storage host rejection | Phase 3a |
rate-limiter.ts | Not present; in-memory per-instance, per-tenant sliding-window counter | Phase 3a |
TanStack Query hooks (Phase 4a — FD-01 wiring layer)
Section titled “TanStack Query hooks (Phase 4a — FD-01 wiring layer)”| Hook | Gap | Spec reference |
|---|---|---|
useImageUpload | Not present; TanStack mutation over BFF image-upload route | FD-01, Phase 4a |
useCheckReachability | Not present; TanStack mutation over BFF check-url route | FD-01, Phase 4a |
useFetchExternalImage | Not present; TanStack mutation over BFF fetch-url route | FD-01, Phase 4a |
useCdnCookies | Not present; TanStack query with proactive background refresh at ~50% TTL | Phase 4b |
Provider stack
Section titled “Provider stack”| Provider | Gap | Spec reference |
|---|---|---|
QueryClientProvider | Not present in provider stack | Phase 4a |
CdnCookieProvider | Not present; CDN cookie lifecycle manager | Phase 4b |
SPA API functions (Phase 4a)
Section titled “SPA API functions (Phase 4a)”| File | Gap | Spec reference |
|---|---|---|
src/api/image-upload.ts | Not present; plain fetch() wrappers over BFF routes | FD-02, Phase 4a |
Grid integration (Phase 4b)
Section titled “Grid integration (Phase 4b)”| Area | Gap | Spec reference |
|---|---|---|
ImageCellDisplay in column defs | Not wired; no image thumbnail in item grid | Phase 4b |
ImageCellEditor factory in column defs | Not wired; no inline image editor on double-click / Enter | Phase 4b |
ImageHoverPreview | Not wired; no hover preview with ~500ms delay | Phase 4b |
| Typed provider hooks threaded into column defs | No hook wiring exists at column definition assembly point | FD-01, Phase 4b |
Form integration (Phase 4c)
Section titled “Form integration (Phase 4c)”| Area | Gap | Spec reference |
|---|---|---|
ImageFormField in ItemFormPanel | Present in type system, disabled placeholder; not wired to ImageFormField component | Phase 4c |
ITEM_IMAGE_CONFIG constant | Not present; entity-specific configuration (1:1 aspect ratio, formats, 10 MB max, 200px min) | Phase 4a |
Route handler tests
Section titled “Route handler tests”| Area | Gap | Spec reference |
|---|---|---|
| BFF route handler tests | Not present (routes do not exist yet) | Phase 3a, 3b |
CloudFrontSignerTest | Not present | Phase 3b |
CdnCookiesRouteTest | Not present | Phase 3b |
CHANGELOG
Section titled “CHANGELOG”| Area | Gap | Notes |
|---|---|---|
| CHANGELOG entries | No entries for any of the above changes | Required by CI gate |
Summary Table
Section titled “Summary Table”| Specification Requirement | Repository | Status | Phase | Notes |
|---|---|---|---|---|
createImageUploadUrl() method on ItemProxy | api-proxy | ok | Prep | Implemented on branch |
ImageUploadUrlRequest / ImageUploadUrlResponse types | api-proxy | ok | Prep | Implemented on branch |
| Proxy unit tests | api-proxy | ok | Prep | Implemented on branch |
| api-proxy CHANGELOG entry | api-proxy | gap | Prep | Required before publish |
| api-proxy publish to GitHub Packages | api-proxy | gap | Prep | Blocking for app BFF routes |
ValidationResult, FieldError types | ux-prototype | gap | 4a | Lifecycle framework does not exist |
EditLifecycleCallbacks<T>, EditableComponentProps<T> types | ux-prototype | gap | 4a | Lifecycle framework does not exist |
useDraft<T> hook | ux-prototype | gap | 4a | Lifecycle framework does not exist |
ImageUploadDialog — indeterminate progress indicator | ux-prototype | gap | 4a | Currently simulated 50 ms timer |
ImageUploadDialog — UploadError state | ux-prototype | gap | 4a | Not present |
ImageUploadDialog — EditLifecycleCallbacks adoption | ux-prototype | gap | 4a | Still uses raw callbacks |
ImageCellEditor — typed provider hook factory signature | ux-prototype | gap | 4a | Currently raw callback props |
TypeaheadCellEditor — hook-style interface evaluation | ux-prototype | partial | 4a | Design decision pending |
ItemGridLookups — full lookup field coverage | ux-prototype | gap | 4a | Only 2 of 9+ fields covered |
ItemGridColumns — image editor hook wiring | ux-prototype | gap | 4a | Not wired |
ImageFormField — EditableComponentProps and contextErrors | ux-prototype | gap | 4a | Not adopted |
| ux-prototype CHANGELOG entry | ux-prototype | gap | 4a | Required before publish |
@arda-cards/design-system publish to GitHub Packages | ux-prototype | gap | 4a | Blocking for app Phase 4 |
ItemTable.tsx removal (dead code) | arda-frontend-app | gap | FD-07 | Must be first commit |
itemTable.test.tsx removal | arda-frontend-app | gap | FD-07 | Must be first commit |
@tanstack/react-table dependency removal | arda-frontend-app | gap | FD-07 | Must be first commit |
@tanstack/react-query dependency | arda-frontend-app | gap | 4a | Not present |
@tanstack/react-query-devtools dependency | arda-frontend-app | gap | 4a | Not present |
@arda-cards/design-system version bump | arda-frontend-app | gap | 4a | After design system publish |
@arda-cards/api-proxy dependency | arda-frontend-app | gap | 3a | Needed by BFF routes |
src/server/ directory | arda-frontend-app | gap | FD-02 | Does not exist |
src/api/ directory | arda-frontend-app | gap | FD-02 | Does not exist |
src/hooks/image-upload/ directory | arda-frontend-app | gap | FD-02 | Does not exist |
src/hooks/cdn/ directory | arda-frontend-app | gap | FD-02 | Does not exist |
src/providers/ directory | arda-frontend-app | gap | FD-02 | Does not exist |
POST /api/image-upload BFF route | arda-frontend-app | gap | 3a | Not present |
POST /api/storage/check-url BFF route | arda-frontend-app | gap | 3a | Not present |
POST /api/storage/fetch-url BFF route | arda-frontend-app | gap | 3a | Not present |
POST /api/storage/cdn-cookies BFF route | arda-frontend-app | gap | 3b | Not present |
cloudfront-signer.ts utility | arda-frontend-app | gap | 3b | Not present |
ssrf-validator.ts utility | arda-frontend-app | gap | 3a | Not present |
rate-limiter.ts utility | arda-frontend-app | gap | 3a | Not present |
src/api/image-upload.ts SPA API functions | arda-frontend-app | gap | 4a | Not present |
useImageUpload hook | arda-frontend-app | gap | 4a | Not present |
useCheckReachability hook | arda-frontend-app | gap | 4a | Not present |
useFetchExternalImage hook | arda-frontend-app | gap | 4a | Not present |
useCdnCookies hook | arda-frontend-app | gap | 4b | Not present |
QueryClientProvider in provider stack | arda-frontend-app | gap | 4a | Not present |
CdnCookieProvider in provider stack | arda-frontend-app | gap | 4b | Not present |
ITEM_IMAGE_CONFIG constant | arda-frontend-app | gap | 4a | Not present |
ImageCellDisplay in grid column defs | arda-frontend-app | gap | 4b | Not wired |
ImageCellEditor factory in grid column defs | arda-frontend-app | gap | 4b | Not wired |
ImageHoverPreview in grid | arda-frontend-app | gap | 4b | Not wired |
ImageFormField wiring in ItemFormPanel | arda-frontend-app | gap | 4c | Placeholder disabled |
| BFF route handler tests | arda-frontend-app | gap | 3a/3b | Routes do not exist yet |
| arda-frontend-app CHANGELOG entries | arda-frontend-app | gap | All | Required by CI gate |
Prioritized Recommendations
Section titled “Prioritized Recommendations”Priority 1 — Blocking (must complete before any other work proceeds)
Section titled “Priority 1 — Blocking (must complete before any other work proceeds)”-
Legacy cleanup in
arda-frontend-app(FD-07, first commit). RemoveItemTable.tsx,itemTable.test.tsx, and the@tanstack/react-tabledependency. This commit must be isolated and pass all checks before any new code is added. It unblocks a clean baseline for all subsequent work. -
Publish
api-proxyto GitHub Packages. Add a CHANGELOG entry and publish the existingjmpicnic/image-upload-frontendbranch content. This unblocks BFF route development inarda-frontend-app, which importsapi-proxyas a runtime dependency. -
Introduce lifecycle framework types in
ux-prototype. AddValidationResult,FieldError,EditLifecycleCallbacks<T>,EditableComponentProps<T>, anduseDraft<T>. These types must exist before any component updates in this project or any downstream consumer can reference them. -
Update the five image components in
ux-prototype. With the lifecycle framework in place, apply the FD-01 and FD-03 changes toImageUploadDialog,ImageCellEditor,ImageFormField,ItemGridColumns/ItemGridLookups, and (after the design decision is resolved)TypeaheadCellEditor. This is prerequisite to resolving theTypeaheadCellEditorinterface question — that decision should be made before component work begins. -
Publish
@arda-cards/design-systemto GitHub Packages. After component updates pass CI (lint, typecheck, unit tests, Storybook build, VRT). This unblocks all Phase 4 work inarda-frontend-app.
Priority 2 — Core functionality (after Priority 1 is complete)
Section titled “Priority 2 — Core functionality (after Priority 1 is complete)”-
Create
src/server/directory structure and BFF route handlers (Phase 3a/3b). Add@arda-cards/api-proxydependency, createsrc/server/, implementssrf-validator.ts,rate-limiter.ts,cloudfront-signer.ts, and all four route handlers. Write route handler tests. -
Add
@tanstack/react-queryand createsrc/hooks/andsrc/providers/(Phase 4a). Install the dependency, addQueryClientProviderto the provider stack, createsrc/api/image-upload.ts, and implementuseImageUpload,useCheckReachability,useFetchExternalImage, anduseCdnCookieshooks. AddITEM_IMAGE_CONFIG. -
Bump
@arda-cards/design-systemand wire grid and form (Phase 4b/4c). Update the design system dependency, addCdnCookieProvider, wireImageCellDisplay,ImageCellEditor, andImageHoverPreviewinto item grid column definitions with typed provider hooks, and wireImageFormFieldintoItemFormPanel.
Priority 3 — Release
Section titled “Priority 3 — Release”-
CHANGELOG entries for all three repositories. Each repository requires a CHANGELOG entry covering its changes before CI will allow a PR to merge. Draft these entries in parallel with implementation; finalize before raising PRs.
-
PRs and merge ordering. Follow the merge sequence defined in the release-lifecycle conventions:
api-proxyPR merges first (already publish-ready),ux-prototypePR second (design system publish gates app),arda-frontend-appPR last. ThedocumentationPR for project completion artifacts can be merged independently once the implementation PRs are merged.
Copyright: (c) Arda Systems 2025-2026, All rights reserved
Copyright: © Arda Systems 2025-2026, All rights reserved