Skip to content

Goal: Component Preparation

Prepare arda-frontend-app for the image upload frontend project by removing legacy dead code, extracting shared utilities from duplicated patterns, and adding local development tooling for multi-package workflows.

The arda-frontend-app contains a legacy ItemTable component (src/app/items/ItemTable.tsx) built with @tanstack/react-table that has been superseded by ItemTableAGGrid. The items page (page.tsx) imports ItemTableAGGrid; ItemTable is dead code with only its own test file (src/tests/itemTable.test.tsx) as a consumer. The @tanstack/react-table dependency should be removed.

The codebase also contains significant code duplication — particularly in the API route layer (43+ route handlers sharing identical boilerplate) and in ardaClient.ts (9 near-identical lookup functions). Extracting shared utilities reduces maintenance burden and establishes clean patterns before adding new features.

Additionally, the app depends on @arda-cards/design-system (from ux-prototype) and will soon depend on @arda-cards/api-proxy. Local development across these packages requires a streamlined workflow that doesn’t risk modifying package.json. A dev:local script has been developed for this purpose (see tools/dev-local.sh in arda-frontend-app).

  • arda-frontend-app (Arda-cards/arda-frontend-app) — remove legacy code, extract utilities, add local development tooling
  • documentation (Arda-cards/documentation) — project planning documents
  1. All checks must pass after each commitnpm run lint, npx tsc --noEmit, npx jest --no-coverage --watchAll=false --forceExit, npm run build
  2. Parts are ordered — each part establishes a clean baseline for the next
  3. package.json dev:local entry must not interfere with CI — the script references sibling directories that only exist in worktree layouts; CI doesn’t invoke dev:local
  4. CHANGELOG must be updated per repository conventions

Remove dead code and its dependency to establish a clean baseline.

  • Verify no consumers — grep the codebase to confirm ItemTable and @tanstack/react-table have no active consumers beyond the component itself and its test.
  • Remove ItemTable.tsx (src/app/items/ItemTable.tsx)
  • Remove associated test (src/tests/itemTable.test.tsx)
  • Remove @tanstack/react-table dependency from package.json via npm uninstall
  • Verify AG Grid table (ItemTableAGGrid) continues to function correctly after removal
#DeliverableLocation
1Remove ItemTable.tsxsrc/app/items/ItemTable.tsx (delete)
2Remove itemTable.test.tsxsrc/tests/itemTable.test.tsx (delete)
3Remove @tanstack/react-table dependencypackage.json (modify)
  1. ItemTable.tsx and itemTable.test.tsx deleted; no references remain.
  2. @tanstack/react-table removed from package.json; no imports remain. Confirmed by grep before removal.
  3. ItemTableAGGrid continues to function correctly.
  4. Full local checks pass: lint, typecheck, unit tests, build.

Extract repeated pure TypeScript patterns into shared utility modules. Each group targets code with strong internal cohesion around the types it manages. Groups are independent and can be reviewed separately.

Group A — API Route Infrastructure (Server)

Section titled “Group A — API Route Infrastructure (Server)”

File: src/lib/api-route-utils.ts (new)

The 43+ Next.js API route handlers in src/app/api/arda/ share identical boilerplate: generate a request ID, build upstream headers, forward the request, parse the response by content-type, and return a NextResponse.

PatternOccurrencesEst. Reduction
Inline GUID generation (generateGuid)43 routes~250 lines
Response content-type parsing (text → conditional JSON.parse)48 routes~200 lines
Response status → NextResponse mapping18+ routes~60 lines

Extracted API:

  • generateRequestId(): string
  • parseUpstreamResponse(response: Response): Promise<{ data: unknown; contentType: string }>
  • forwardAsNextResponse(upstream: Response, data: unknown): NextResponse

Est. reduction: ~510 lines

Testing: src/lib/api-route-utils.test.ts (new)

  • generateRequestId — returns valid UUID v4 format; successive calls produce distinct values.
  • parseUpstreamResponse — returns parsed JSON when content-type is application/json; returns { raw: text } for other content types; handles empty response body.
  • forwardAsNextResponse — maps upstream 2xx to 200; preserves upstream 4xx/5xx status; includes ok and status fields in body.

Existing route-handler tests are not modified — they continue to exercise the routes end-to-end and now implicitly cover the extracted functions.

Group B — Lookup Response Parsing (Client)

Section titled “Group B — Lookup Response Parsing (Client)”

File: src/lib/ardaClient.ts (refactor in place)

All 9 lookup functions (lookupSuppliers, lookupUnits, lookupTypes, lookupSubtypes, lookupUseCases, lookupFacilities, lookupDepartments, lookupLocations, lookupSublocations) share the same triple-fallback parsing logic, differing only by endpoint URL and field name.

PatternOccurrencesEst. Reduction
Lookup response parsing (3-layer fallback)9 functions~300 lines
Type guard filters (typeof x === 'string')15+ (within the 9)(included above)

Extracted API (private to ardaClient.ts):

  • lookupField(endpoint: string, fieldName: string, params: URLSearchParams): Promise<string[]>

Est. reduction: ~300 lines

Testing: covered by existing ardaClient.*.test.ts files

  • The 9 public lookup functions keep their signatures — existing tests continue to pass unchanged.
  • Add a focused test block for lookupField (the new private-turned-exported helper) covering: plain string array response, named-field response ({ data: { units: [...] } }), results[].name fallback, empty/missing data returns [].

Group C — JWT / Token Consolidation (Mixed)

Section titled “Group C — JWT / Token Consolidation (Mixed)”

File: src/lib/jwt.ts (consolidate into existing file)

jwt.ts already provides decodeJWTPayload() and extractTokenFromRequest(), but client-side files (authThunks.ts, tokenRefresh.ts, AuthInit.tsx, AuthContext.tsx) inline their own decode and format-validation logic. A client-safe decodeJWTPayload export (no NextRequest dependency) can serve both sides.

PatternOccurrencesEst. Reduction
JWT payload decoding (atob(token.split('.')[1]))11 places / 4 files~20 lines
JWT format validation (split('.').length !== 3)4+ places~10 lines

Action: Replace inline usages with imports from jwt.ts. No new file needed.

Est. reduction: ~30 lines (small line count, but eliminates a security-sensitive pattern being hand-rolled in multiple places)

Testing: covered by existing jwt.test.ts

  • decodeJWTPayload and format validation are already tested in jwt.test.ts. No new test file needed.
  • Verify existing tests in authThunks / tokenRefresh / AuthInit still pass after replacing inline decode with the imported function — no changes to those test files.

File: src/lib/storage.ts (new)

localStorage access and JSON.parse are always used together with identical try/catch error-swallowing wrappers. Both deal with untrusted serialized data at the browser boundary.

PatternOccurrencesEst. Reduction
localStorage JSON get/set with try/catch24+ places~100 lines
Safe JSON.parse with fallback8+ places~30 lines

Extracted API:

  • safeJsonParse<T>(text: string, fallback: T): T
  • getStorageItem<T>(key: string, fallback: T): T
  • setStorageItem(key: string, value: unknown): void

Est. reduction: ~130 lines

Testing: src/lib/storage.test.ts (new)

  • safeJsonParse — returns parsed object for valid JSON; returns fallback for malformed JSON; returns fallback for empty string.
  • getStorageItem — returns parsed value when key exists; returns fallback when key is missing; returns fallback when stored value is malformed JSON.
  • setStorageItem — serializes and stores value; handles localStorage quota errors without throwing.

Existing tests in consumers (e.g., ItemTableAGGrid, SidebarVisibilityContext) are not modified — they continue to exercise the call sites end-to-end.

File: src/lib/errors.ts (new)

Error-to-string narrowing is repeated in both server-side API routes and client-side auth handlers.

PatternOccurrencesEst. Reduction
Error message extraction (instanceof Error ? .message : String(error))20+ places~80 lines

Extracted API:

  • extractErrorMessage(error: unknown): string

Est. reduction: ~80 lines

Testing: src/lib/errors.test.ts (new)

  • extractErrorMessage — extracts .message from Error instances; returns the string directly for string errors; calls String() on other types (numbers, objects, null, undefined).

File: src/lib/formatters.ts (new)

Date, currency, and quantity formatting currently live in src/components/table/columnPresets.tsx (an AG Grid file) and are duplicated in ardaClient.ts. Pure formatting logic has no business being coupled to AG Grid column definitions.

PatternOccurrencesEst. Reduction
Date/currency/quantity formatting8+ places~50 lines

Extracted API:

  • formatDate(date: string | undefined): string
  • formatDateTime(date: string | undefined): string
  • formatCurrency(value: Money | undefined): string
  • formatQuantity(quantity: Quantity | undefined): string

Est. reduction: ~50 lines

Testing: src/lib/formatters.test.ts (new)

  • formatDate — formats ISO string to locale date; returns '-' for undefined.
  • formatDateTime — formats ISO string to locale date+time; returns '-' for undefined.
  • formatCurrency — formats Money as $X.XX CUR; returns '-' for undefined or null value.
  • formatQuantity — formats as amount unit; returns '-' (or default) for undefined.

Existing tests in columnPresets or AG Grid components are not modified.

Scattered process.env.NEXT_PUBLIC_* comparisons are consolidated into named boolean constants, split by execution context.

Server file: src/lib/env.ts (existing — already provides BASE_URL, ARDA_API_KEY, etc.)

Client file: src/lib/env-spa.ts (new)

PatternOccurrencesEst. Reduction
Scattered process.env.NEXT_PUBLIC_* checks30+ places~50 lines

Extracted API (env-spa.ts):

  • const IS_PRODUCTION: boolean
  • const IS_STAGE: boolean
  • const IS_MOCK_MODE: boolean

The debugLog / debugError / debugWarn functions in utils.ts import from env-spa.ts, and scattered environment comparisons are replaced by readable constants.

Est. reduction: ~50 lines

Testing: src/lib/env-spa.test.ts (new)

  • Validates that IS_PRODUCTION, IS_STAGE, IS_MOCK_MODE reflect the corresponding process.env.NEXT_PUBLIC_* values.
  • Tests use jest.replaceProperty (or equivalent) to set env vars per case — no changes to existing test files.

Existing env.test.ts (server-side) is not modified.

GroupFileSideEst. Reduction
A — API Route Infrastructuresrc/lib/api-route-utils.ts (new)Server~510 lines
B — Lookup Parsingsrc/lib/ardaClient.ts (refactor)Client~300 lines
C — JWT/Tokensrc/lib/jwt.ts (consolidate)Mixed~30 lines
D — Safe Serializationsrc/lib/storage.ts (new)Client~130 lines
E — Error Handlingsrc/lib/errors.ts (new)Mixed~80 lines
F — Formatterssrc/lib/formatters.ts (new)Client~50 lines
G — Environment Constantssrc/lib/env.ts + src/lib/env-spa.tsServer + Client~50 lines
Total~1,150 lines
  1. Each extracted utility has its own unit test file with the cases listed in its group above.
  2. All new tests pass: npx jest --no-coverage --watchAll=false --forceExit.
  3. No existing test files are modified (except where a group explicitly notes adding cases to an existing suite, e.g., Group B).
  4. Public API signatures of refactored modules (ardaClient.ts, jwt.ts, utils.ts) are unchanged — existing consumers compile without edits beyond import paths.
  5. Full local checks pass after each group: lint, typecheck, unit tests, build.

Add dev:local workflow for multi-package local development.

  • tools/dev-local.sh — shell script that:
    1. Builds ux-prototype (vite, with type declarations) and api-proxy (tsc)
    2. Starts background watch-mode rebuilds (logs to scratch/)
    3. Links packages into node_modules via npm link --no-save (does not modify package.json)
    4. Starts Next.js dev server in mock mode with --webpack flag
    5. On exit (Ctrl+C, error, or termination), restores node_modules from registry via npm install
    6. Accepts optional port argument (default 3000)
  • package.json script entry"dev:local": "bash tools/dev-local.sh"
  • Makefile targetdev-local target that invokes npm run dev:local
  • README.md update — document the local development workflow, replacing the old manual file: reference approach
  • CLAUDE.md update — document the local development workflow for agents
#DeliverableLocation
1dev-local.sh scripttools/dev-local.sh (new)
2dev:local script entrypackage.json (modify)
3dev-local Makefile targetMakefile (modify)
4Updated README.md with local development docsREADME.md (modify)
5Updated CLAUDE.md with local development docsCLAUDE.md (modify)
  1. tools/dev-local.sh is executable and correctly builds, links, runs dev server, and restores on exit.
  2. npm run dev:local invokes the script.
  3. npm run dev:local -- 3001 runs on custom port.
  4. make dev-local invokes the script.
  5. README.md documents the new workflow, replacing the old file: approach.
  6. CLAUDE.md documents the workflow for agents.
  7. Full local checks pass: lint, typecheck, unit tests, build.

After all three parts are complete and all commits are local:

  1. Push the branch to origin.
  2. Create a pull request against main with a summary covering all three parts.
  3. Monitor checks — if any CI check fails, diagnose and fix before requesting review.
  4. Wait 20 minutes for reviewer comments.
  5. Present comments to the user as a numbered table with:
    • Comment summary
    • Recommended action (fix, follow-up ticket, or rationale for no action)
  6. Act on decisions — commit fixes, create follow-up tickets, or reply with rationale as appropriate.
  7. Resolve threads once each comment’s action is complete.

  • Image upload implementation (separate project: image-upload-frontend)
  • Code restructuring of src/lib/ (tracked in #734)
  • api-proxy code changes or publishing
  • ux-prototype component changes

This project is a prerequisite for the image-upload-frontend project (roadmap/completed/item-image-upload/), which lists it as an entry condition for Phase 3.5 (BFF routes).


Copyright: (c) Arda Systems 2025-2026, All rights reserved