mirror of
https://github.com/hanzoai/billing.git
synced 2026-10-30 18:46:54 +00:00
`loadBillingData` fans out ten reads and records a reason per panel. Three panels rendered theirs — credits, transactions, accounts. The other seven took the reason and dropped it, so a failed read reached the customer as a fact about their account: no cards on file, no subscription, no invoices, no spend alerts, and a balance of $0. Two of those are statements about money that only a READ balance may make. The overview printed $0.00 in three places from `balance === null` and fired the "Your credit balance is $0 — add funds to continue" banner beside it; the credits panel printed the same zero as a total. Both now render an em dash and state the reason, because an absent balance is our unknown and never the customer's debt. The payment read carried further than its own panel. `setupIncomplete` is built from the card list and the subscription, so an unread pair made the overview BECOME the onboarding card — a customer with a card on file walled behind "add a payment method", which adding a second card would not clear. Setup is now incomplete only when a successful read says it is, and the overview's payment tile reads "Unavailable" rather than inviting a purchase it cannot know is needed. Each panel takes the shape TransactionsPanel already had: `error` plus `onRetry`, or `<datum>Error` where one component shows more than one read and a single slot could not say which failed — the convention `usageError` set. A component's own submit failure keeps its own slot (`saveError`), so an outage that happened before the customer clicked is never blamed on their click. `why` now reads CommerceApiError.status instead of the message alone: 401 and 403 are a refusal and say to sign in again, anything else names the status, and an error with no status never reached the server and says that plainly. Measured against api.hanzo.ai: mounted routes answer 401 unauthenticated, so the three cases are distinguishable at the point the reason is written. Pinned in lib/billing/panel-errors.test.tsx, both halves, because each was right alone while the pair was broken: every panel states the reason it is given and still prints its real emptiness after a successful read, and every recorded reason reaches a component as a named prop. The prop sites are listed one by one rather than matched by a pattern — `payment` feeds two components, and a rule asking only for "at least one site per read" stayed green when either was deleted.
8 lines
286 B
TypeScript
8 lines
286 B
TypeScript
import { defineConfig } from 'vitest/config'
|
|
|
|
// `@/` is the app's own root, exactly as tsconfig `paths` declares it. A panel
|
|
// imports `@/lib/money`, so a test that renders one needs the same alias.
|
|
export default defineConfig({
|
|
resolve: { alias: { '@': import.meta.dirname } },
|
|
})
|