import { useQuery } from '@tanstack/react-query';
import api from '../api/client';
// ─── Types ───────────────────────────────────────────────────────────────────
export type Tier = 'starter' | 'launch' | 'growth' | 'command';
export type Feature =
| 'dashboard'
| 'forecasting'
| 'cascading_goals'
| 'strategic_intelligence'
| 'scale_optimization'
| 'multi_market'
| 'enterprise';
export interface TierLimits {
markets: number;
seats: number;
connectors: number;
}
export interface TierInfo {
tier: Tier;
features: Feature[];
limits: TierLimits;
}
// Tier ordering, lowest → highest (mirrors app/utils/feature_gate.py)
export const TIER_ORDER: Tier[] = ['starter', 'launch', 'growth', 'command'];
// Minimum tier that unlocks each feature (for upgrade prompts)
export const FEATURE_MIN_TIER: Record<Feature, Tier> = {
dashboard: 'starter',
forecasting: 'starter',
cascading_goals: 'launch',
strategic_intelligence: 'growth',
scale_optimization: 'growth',
multi_market: 'growth',
enterprise: 'command',
};
export const TIER_LABELS: Record<Tier, string> = {
starter: 'Starter',
launch: 'Launch',
growth: 'Growth',
command: 'Command',
};
const FALLBACK: TierInfo = {
tier: 'starter',
features: ['dashboard', 'forecasting'],
limits: { markets: 1, seats: 2, connectors: 1 },
};
// ─── Hook ────────────────────────────────────────────────────────────────────
/**
* useFeatureGate — fetches the current user's subscription tier and exposes
* feature-check helpers.
*
* const { hasFeature, tier, isLoading, requiredTier } = useFeatureGate();
* if (!hasFeature('multi_market')) return <UpgradePrompt feature="multi_market" />;
*/
export function useFeatureGate() {
const { data, isLoading, isError } = useQuery<TierInfo>({
queryKey: ['billing', 'tier'],
queryFn: async () => {
const res = await api.get<TierInfo>('/api/billing/tier');
return res.data;
},
staleTime: 5 * 60 * 1000,
retry: 1,
});
const info = data ?? FALLBACK;
const hasFeature = (feature: Feature): boolean => {
// While loading, don't flash upgrade prompts — treat as unlocked until we know.
if (isLoading) return true;
if (isError) return info.features.includes(feature);
return info.features.includes(feature);
};
const requiredTier = (feature: Feature): Tier => FEATURE_MIN_TIER[feature];
return {
tier: info.tier,
tierLabel: TIER_LABELS[info.tier],
features: info.features,
limits: info.limits,
hasFeature,
requiredTier,
isLoading,
isError,
};
}