Skip to content
Ritesh FirodiyaGet in touch

Work / Chitragupt / Wiki / Decisions

2026-08-10-inbox-wireframe-alignment

Decisioncanonicalverified 2026-08-10

DECISION.2026-08-10.INBOX-WIREFRAME-ALIGNMENT

Inbox React pages audited against all 3 wireframe variants; real gaps closed, one archetype scoped out

Decision

Audited the live Inbox implementation (apps/website/src/app/(app)/inbox/, apps/website/src/components/inbox/) against all wireframe variants under .context/designs/web/inbox/*.html — inbox-empty.html, inbox-populated.html, inbox-upgrade.html (the only 3 that exist; inbox.html, inbox-free.html, inbox-transfers.html, inbox-uploading.html, inbox-zip-expanding.html referenced by some .context/wiki/surfaces/inbox-*.md pages are mobile-only wireframes under .context/designs/mobile/app/inbox/ — those surface pages cite the wrong path, a pre-existing wiki-drift issue out of scope for this pass). Unlike the sibling Expense/Portfolio audit (2026-08-07-expense-portfolio-wireframe-alignment), Inbox's family-tier surface (/inbox/transfers) already existed and is real (listFamilyTransfers callable, acceptFamilyTransfer/denyFamilyTransfer, real PAN-match indicator) — no family-tier gap here.

Gaps found and fixed, component-for-component, with real derived data:

  • Receipt-to-transaction linkage never surfaced in the UI. apps/functions/src/inbox/link-evidence-to-ledger.ts (linkEvidenceToLedger, called from persist-confirmed-document.ts) stamps a real linked_ledger_entry_id on evidence-only docs (travel-receipt, utility-bill, motor-insurance-policy, generic-receipt) that matched a bank_debit/bank_credit row — per inbox-document-types DOC.EVIDENCE-ONLY-NOTE / [[receipt-to-transaction-linkage]]. The website's DocumentRecord/DocumentDetail types never read the field and no component rendered it. Added the field to store/documents.ts, a useLedgerEntry hook to store/ledger.ts, and a new LinkedTransactionNote component rendered in DocConfirmedView's "Used by" section — real matched amount/date/merchant, not a placeholder.
  • Fabricated upload-progress numbers. DocumentUploader.tsx showed a hardcoded ~${(remaining) * 12}s remaining countdown and a static "—%" per-file progress, because api/upload.ts used non-resumable uploadBytes (no progress events exist). Switched to uploadBytesResumable with a real state_changed progress callback threaded through uploadFiles; the uploader now shows real byte-transferred percentages, no invented timing.
  • "Needs your action" main-panel shelf missing the "Expiring policy" archetype. PILLAR.INBOX.NEEDS-ACTION.ARCHETYPES lists 5 archetypes (Locked · AIS/26AS mismatch · Uncategorised · Expiring policy renewal · OCR failure); NeedsActionShelf.tsx only handled the first, third, and fifth (pure document-status archetypes). The expiring-policy archetype's data (temporal.valid_until within 90 days) was already computed correctly but only in the sidebar's separate "Expiring in 90 days" shelf. Hoisted isExpiringSoon/EXPIRING_WINDOW_MS out of FolderTree.tsx (previously duplicated inline in FolderTree.tsx and DocumentList.tsx) into _bucket-helpers.ts as the single source of truth, and extended isNeedsAction to optionally include expiring-soon docs so the main panel, sidebar shelf count, and ?shelf=needs_action filter all agree.
  • Hardcoded, drifted doc-type counts. InboxFreeUpgradeBanner.tsx and DocViewOnlyView.tsx both said "23 more types" (28 total); the empty-state hint the wireframe specifies ("… 28 document types supported") didn't exist in the live empty state at all. The actual DOCUMENT_TYPES registry has 43 entries. Replaced both hardcoded literals with Object.keys(DOCUMENT_TYPES).length-derived counts, and added the missing empty-state hint line to DocumentList.tsx's zero-doc state.
  • Dead buttons. DocViewOnlyView.tsx's "Download" and "Delete" buttons (the free-tier single-doc page every Free-tier user hits before upgrading) had no onClick handlers at all. Wired real handlers reusing fetchDocumentDownloadUrl/deleteDocument from api/documents.ts, matching the pattern already used in DocConfirmedView.tsx and OpenOriginalButton.tsx.
  • Small fabricated/broken details. InlineTick.tsx computed a color variable per tone prop but never applied it (tick was always the same color regardless of tone="emerald" vs "blue") — fixed. DocConfirmedView.tsx's audit-trail line unconditionally claimed "no overrides" with no backend field tracking whether the user actually edited any parsed value before confirming — replaced with an accurate "Confirmed by you" status line instead of an unverified claim.

Scoped out, not fabricated: the wireframe's "AIS mismatch — AY 2026-27" needs-action archetype has no backend computation anywhere in the codebase — no job compares AIS entries against 26AS entries per TAN/section (the only reconciliation-adjacent work in the repo is _lib/reconcile-income-sources.ts, which is unrelated: it dedupes evidence-doc income against AIS, not AIS-vs-26AS). Building this needs new server-side logic (a per-AY diff over ledger_entries grouped by TAN/section), which is out of scope per this task's instruction not to build net-new backend computation. NeedsActionShelf.tsx intentionally omits this archetype rather than rendering a fake mismatch banner.

Why

Same rationale as the Expense/Portfolio pass: audit the live Inbox build against every wireframe variant that actually exists, close every gap with real data, and flag (rather than fake) anything that needs backend work not yet built.

Impact

  • apps/website/src/store/documents.ts, apps/website/src/store/ledger.ts — linked_ledger_entry_id field + useLedgerEntry hook.
  • apps/website/src/components/inbox/LinkedTransactionNote.tsx (new), DocConfirmedView.tsx, DocViewOnlyView.tsx, DocumentUploader.tsx, DocumentList.tsx, NeedsActionShelf.tsx, FolderTree.tsx, InboxFreeUpgradeBanner.tsx, InlineTick.tsx, _bucket-helpers.ts — the fixes above.
  • apps/website/src/api/upload.ts — uploadBytesResumable + progress callback.
  • [[pillar-inbox]], [[inbox-document-types]] — no factual change, still accurate; not re-synced beyond this pass.
  • AIS/26AS mismatch reconciliation remains unbuilt — needs a new backend job before any Inbox UI can show it. Not a silent gap: it is simply absent from the "Needs your action" list rather than faked.
  • Pre-existing wiki drift noted but not fixed in this pass: .context/wiki/surfaces/inbox-free.md, inbox-folders-default.md, inbox-transfers.md, inbox-uploading.md, inbox-zip-expanding.md cite website wireframe paths (inbox.html, inbox-free.html, etc.) that only exist under .context/designs/mobile/app/inbox/, not .context/designs/web/inbox/. A /wiki-lint pass is recommended to correct these citations or mark them mobile-only (Phase 8, out of V1 scope per 2026-05-30-v1-website-only).

Status

Active

Sources

  • .context/designs/web/inbox/inbox-empty.html
  • .context/designs/web/inbox/inbox.html
  • .context/designs/web/inbox/inbox-locked.html
  • 2026-08-07-expense-portfolio-wireframe-alignment — the sibling audit this follows the same methodology from
  • .context/wiki/entities/inbox-document-types.md
  • .context/wiki/entities/pillar-inbox.md

Every project of mine is written down like this.

Read the résumé