Skip to content

fix(budget): preserve original refundedAt timestamp when editing expense - #281

Draft
dccakes wants to merge 1 commit into
mainfrom
claude/sharp-feynman-u1b8ku
Draft

dccakes wants to merge 1 commit into
mainfrom
claude/sharp-feynman-u1b8ku

Conversation

@dccakes

@dccakes dccakes commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Daily Product Improvement

📝 TLDR

Preserve the original refundedAt timestamp when editing an existing refundable expense instead of overwriting it with the current time on every save.

📝 Description

When a user records that a refund was received on a deposit expense, the refundedAt field is set to the timestamp of that moment. However, when editing the same expense later — even just to fix the description or amount — re-submitting the form with "Refund received" still toggled on silently overwrote refundedAt with new Date() (the time of the edit), discarding the real refund date.

This is a data integrity bug: the original refund date is lost without any warning to the user.

This Change

  • In expense-form.tsx, the refundedAt computation now uses expense?.refundedAt ?? new Date() so the original timestamp is preserved when it exists
  • A new refund (no prior timestamp) still correctly records the current time
  • No UI changes — purely a backend payload correction

🧪 Testing

  • Manually tested functionality
  • Added/updated tests

🧪 How to test as a user

  1. Create a new Budget expense, mark it as "This is a deposit" → "Refundable" → "Refund received". Save. Note the refundedAt timestamp in the database or via the API.
  2. Reopen the same expense for editing, change any field (e.g. description), and save again without touching the refund toggles.
  3. Verify refundedAt still matches the original timestamp — it should not update to the edit time.

🎨 Style and code quality

  • Ran biome check on all new and updated files — no errors

📦 Dependencies

  • No new dependencies

Recommendations Reviewed

Area: src/components/budget/expense-form.tsx
Issue: When editing an existing expense with a recorded refund date, refundedAt was overwritten with new Date() on every save.
Recommendation: Use expense?.refundedAt ?? new Date() to preserve the original timestamp.
Type: Backend cleanup / data integrity
Priority: High
Effort: Small


Area: src/app/(authenicated)/budget/page.tsx, vendors/page.tsx
Issue: API errors are silently swallowed and the user is redirected to / with no explanation, bypassing the route error boundary.
Recommendation: Let errors propagate so error.tsx catches them and shows a "Try again" CTA in context.
Type: Product flow / UX
Priority: High
Effort: Medium


Area: src/components/guest-list/guests-view.tsx
Issue: filteredHouseholds is stored in local state and re-synced via useEffect. When background query invalidations fire, the effect resets the active search filter, clearing text the user typed.
Recommendation: Replace duplicated local state with a useMemo-derived value so the filter is never destroyed by a refetch.
Type: Frontend cleanup / UX
Priority: Medium
Effort: Medium


Area: src/components/vendor/vendor-detail-panel.tsx
Issue: The scratchpad textarea has no unsaved-changes guard. Edits are silently discarded when the panel is closed without clicking "Save Scratchpad."
Recommendation: Add an isDirty check and a discard-confirmation dialog on panel close, matching the pattern already used in the guest detail drawer.
Type: UX
Priority: Medium
Effort: Medium


Area: src/components/vendor/vendor-detail-panel.tsx (lines 159–163)
Issue: isSubmitting and isUploading are always set to true/false together — they are effectively one boolean but stored as two.
Recommendation: Collapse into a single isBusy state variable.
Type: Frontend cleanup
Priority: Low
Effort: Small


Area: src/components/budget/index.tsx, src/components/vendor/index.tsx
Issue: No staleTime is set on queries that receive initialData from the server, causing an immediate background refetch on every mount.
Recommendation: Add staleTime: 30_000 (matching the pattern used in events and checklist) so the server-rendered data is respected for 30 s.
Type: Frontend cleanup
Priority: Low
Effort: Small


Selected Improvement

Preserve original refundedAt timestamp when editing a budget expense — src/components/budget/expense-form.tsx line 93.

Why This Was Selected

This is the highest-severity, lowest-effort fix available today that has no open PR. It is a silent data-corruption bug: every time a user edits a refundable deposit that has already been marked as refunded, the real refund date is overwritten with the current wall-clock time. The fix is a single expression change, is easy to verify, and carries zero risk of regressions in unrelated areas.

Changes Made

  • expense-form.tsx line 93: changed new Date() to expense?.refundedAt ?? new Date() inside the refundedAt assignment so existing refund timestamps are preserved on edit.

Files Changed

  • src/components/budget/expense-form.tsx

Verification

  • Biome check: no errors
  • TypeScript typecheck: no new errors (pre-existing baseUrl deprecation warning is unrelated to this change)
  • Logic review: expense is only defined when mode === 'edit' (line 32); in mode === 'create' mode expense is undefined, so expense?.refundedAt evaluates to undefined and the nullish coalescing operator correctly falls through to new Date() — no regression on create.

Future Recommendations

Silent redirect on API error (budget, vendors)

  • Priority: High
  • Effort: Medium
  • Reason not included: touches two separate page files and requires verifying that the route error boundaries handle all thrown error shapes gracefully — broader scope than a single targeted fix.
  • Should become GitHub issue: Yes

Guest list search filter reset on data refresh

  • Priority: Medium
  • Effort: Medium
  • Reason not included: requires refactoring a 1000-line component; safe but not a one-liner.
  • Should become GitHub issue: No (manageable in a future daily run)

Vendor scratchpad unsaved-changes guard

  • Priority: Medium
  • Effort: Medium
  • Reason not included: requires adding dialog state and a close interceptor; medium scope.
  • Should become GitHub issue: No (manageable in a future daily run)

GitHub Issues Created or Proposed

No GitHub issues created. The silent-redirect issue (budget/vendors) is worth tracking but is already clearly described here; a future daily run can open an issue if it remains unaddressed.


Generated by Claude Code

When updating a refundable deposit that already has a recorded refund
date, the form was overwriting refundedAt with new Date() on every save.
This silently replaced the real refund timestamp with the edit time.

Now uses the existing expense.refundedAt when available, only falling
back to new Date() for newly received refunds.
@vercel

vercel Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
scv Error Error Sep 1, 2026 10:18am UTC

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Jest Test Coverage

Coverage Summary

Lines Statements Branches Functions
Coverage: 83%
83.11% (36264/43629) 82.14% (3813/4642) 69.49% (1073/1544)

This branch had an error being deployed

1 failed deployment
Preview — a1521f66 Deployed Sep 1, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant