Skip to content

Add earnings analytics: payment tracking + sitter dashboard#24

Merged
bradburch merged 18 commits into
custom-servicesfrom
earnings-analytics
Jul 12, 2026
Merged

Add earnings analytics: payment tracking + sitter dashboard#24
bradburch merged 18 commits into
custom-servicesfrom
earnings-analytics

Conversation

@bradburch

Copy link
Copy Markdown
Owner

Summary

  • New Payments table (migration 0008 + schema.sql lockstep): multiple whole-dollar payments per booking (deposits/partials), recorded by the sitter with method/date/note; deleting a record is the correction mechanism (no refunds/negative amounts by design)
  • Repo layer: guarded atomic insertPayment (pending allowed; blocked/cancelled/foreign refused), triple-id-scoped deletePayment, listPaymentsForBooking, PaidTotal join on the bookings list, and getAnalytics (12-month zero-filled monthly revenue, all-time by-service + top-clients, outstanding balances over confirmed bookings)
  • Admin routes: POST/GET/DELETE /:slug/admin/bookings/:id/payments, GET /:slug/admin/analytics (camelCase payload + JS-derived tiles), paidTotal on the bookings list
  • Frontend: shared PaymentsPanel (list + record form + delete), Bookings rows show paid state with an inline panel, and a new Earnings section — stat tiles, hand-rolled 12-month SVG revenue chart, by-service and top-client breakdowns (all-time), outstanding table with inline payment recording. No new dependencies.

Design spec: docs/superpowers/specs/2026-07-11-earnings-analytics-design.md (committed on this branch).

Test plan

  • 307/307 tests across 46 files (new: payments-repo, analytics repo+route, payments-routes)
  • typecheck / eslint / prettier / build clean
  • Browser-verified: Earnings section render, tiles/chart/breakdowns, record + delete payment round-trip from both Bookings and Earnings, #earnings deep-link, fresh-tenant empty states
  • Run migrations/0008_payments.sql against remote D1 on deploy

Known follow-ups (deferred by review)

  • Payments on cancelled bookings have no UI surface (API supports deletion; needs a read-only ledger on cancelled rows)
  • Analytics endpoint doesn't trigger calendar reconciliation (Bookings page does; 120s TTL)

🤖 Generated with Claude Code

bradburch and others added 18 commits July 11, 2026 20:04
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fact-check pass: guarded INSERT idiom spelled out, paidTotal route glue,
icon/error-banner conventions corrected, migration test dropped to match
repo convention. Design pass: per-booking payments panel (list + delete),
deletePayment scoped to booking id, refund/cancel-after-payment decision
documented, isRealDate/isValidRate reuse, pending-deposit semantics,
all-time labels.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add client-side validation to prevent submission of zero/negative amounts
or empty dates. Validates amount is a positive integer and date is non-empty.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fix 1: paidText now returns null when paidTotal is 0 (no paid text shown until a payment exists).
Fix 2: PaymentsPanel mount now gated on row status (cancelled/declined), matching the toggle button condition to prevent stuck-open panels.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The final-task verification gate runs `npm run format`, which caught
whitespace-only drift left over from earlier tasks; no behavior change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…working

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ry bounds

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… in panel

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Change outstanding empty state to neutral "No outstanding balances."
- Hoist duplicated "No payments recorded yet." to module constant NO_PAYMENTS
- Add overflow: hidden with text-overflow: ellipsis to hbars grid cells and list items
- Hide SVG bar chart totals when >= 5 digits to prevent overlap

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@bradburch

Copy link
Copy Markdown
Owner Author

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

Four independent review passes over the 19-file diff (head 4bb317c): a shallow bug scan (SQL bind ordering, 12-month window math, route handlers, panel/section logic), a git-history pass (no conflicts with the booking-lifecycle Declined semantics from #21; insertPayment is the only write path into Payments), a prior-PR-feedback pass (consistent with fix patterns from #18/#21/#22: bounded queries, best-effort reconciliation, tenant-scoped guards), and a code-comment compliance pass (Model A invariants hold on the new table and queries). No CLAUDE.md files exist in this repo.

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

@bradburch bradburch merged commit 548a6e6 into custom-services Jul 12, 2026
1 of 2 checks passed
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