feat(billing): add useBillingStore and /billing/state integration

T32: Single frontend source of truth for subscription / plan / feature
state. New Zustand `useBillingStore` fetches `/billing/state` (auto-fetch
on login via authStore, reset on logout), exposes `refetch` for
post-Checkout refresh, and is supported by a `useBillingPoll` hook
that re-fetches every 60s while authenticated. The new `billingApi`
client transforms the snake_case backend payload to camelCase at a
single boundary so the rest of the frontend never sees `plan_billing`
or `enabled_features`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 20:44:20 -04:00
parent 80baf89b00
commit 7a9cb4b03b
9 changed files with 330 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
import { useEffect } from 'react'
import { useAuthStore } from '@/store/authStore'
import { useBillingStore } from '@/store/billingStore'
const POLL_INTERVAL_MS = 60_000
/**
* Re-fetches billing state every 60s while a user is logged in.
*
* Mount once at the top of the authenticated dashboard tree. Polling
* automatically pauses when the auth store reports no logged-in user.
*
* Note: this is a v1 simple-interval implementation; a later task may
* swap to SSE / visibility-aware polling.
*/
export function useBillingPoll(): void {
const isAuthenticated = useAuthStore((s) => s.isAuthenticated)
useEffect(() => {
if (!isAuthenticated) return
const id = window.setInterval(() => {
void useBillingStore.getState().refetch()
}, POLL_INTERVAL_MS)
return () => {
window.clearInterval(id)
}
}, [isAuthenticated])
}
export default useBillingPoll