feat(billing): add FeatureGate, UpgradePrompt, EmailVerificationGate components

Three drop-in gating components for the self-serve signup flow.

- FeatureGate reads useFeature(flag) and renders children when enabled,
  else a fallback (default UpgradePrompt). UX-only — security boundary
  remains require_feature on the backend.
- UpgradePrompt resolves a feature key to display name + required plan
  via an inline catalog and links to /account/billing/select-plan.
- EmailVerificationGate gates protected content behind a 6-day grace
  period; renders a minimal EmailVerificationWall (resend + sign out)
  on Day 7+ unverified. Wall design will be refined in Task 37.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 21:01:53 -04:00
parent 0b5ed9aa10
commit ece82225f2
7 changed files with 517 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
import type { ReactNode } from 'react'
import { useFeature } from '@/hooks/useFeature'
import { UpgradePrompt } from './UpgradePrompt'
interface FeatureGateProps {
/** Feature flag key (e.g. `psa_integration`). Must match a backend `feature_flags.flag_key`. */
feature: string
/**
* Rendered when the feature is enabled for the current account.
*/
children: ReactNode
/**
* Rendered when the feature is disabled. Defaults to `<UpgradePrompt feature={feature} />`.
* Pass `null` to render nothing.
*/
fallback?: ReactNode
}
/**
* Conditionally renders `children` based on whether `feature` is enabled
* for the current account.
*
* This is a UX affordance — the security boundary is the backend
* `require_feature` dependency. Never trust this gate for authorization.
*/
export function FeatureGate({ feature, children, fallback }: FeatureGateProps) {
const enabled = useFeature(feature)
if (enabled) {
return <>{children}</>
}
// Use explicit fallback when provided, otherwise render the standard prompt.
// `null` is a valid fallback (renders nothing).
if (fallback !== undefined) {
return <>{fallback}</>
}
return <UpgradePrompt feature={feature} />
}
export default FeatureGate