/** * FeatureGate Component * * Conditionally renders children based on entitlement checks. * Used to show/hide features based on the business's subscription plan. */ import React from 'react'; import { useEntitlements } from '../hooks/useEntitlements'; // ============================================================================ // FeatureGate - For boolean feature checks // ============================================================================ interface FeatureGateProps { /** * Single feature code to check */ feature?: string; /** * Multiple feature codes to check */ features?: string[]; /** * If true, ALL features must be enabled. If false, ANY feature being enabled is sufficient. * Default: true (all required) */ requireAll?: boolean; /** * Content to render when feature(s) are enabled */ children: React.ReactNode; /** * Content to render when feature(s) are NOT enabled */ fallback?: React.ReactNode; /** * Content to render while entitlements are loading */ loadingFallback?: React.ReactNode; } /** * Conditionally render content based on feature entitlements. * * @example * ```tsx * // Single feature check * * * * * // With fallback * } * > * * * * // Multiple features (all required) * * * * * // Multiple features (any one) * * * * ``` */ export const FeatureGate: React.FC = ({ feature, features, requireAll = true, children, fallback = null, loadingFallback = null, }) => { const { hasFeature, isLoading } = useEntitlements(); // Show loading state if provided if (isLoading) { return <>{loadingFallback}; } // Determine which features to check const featuresToCheck = features ?? (feature ? [feature] : []); if (featuresToCheck.length === 0) { // No features specified, render children return <>{children}; } // Check features const hasAccess = requireAll ? featuresToCheck.every((f) => hasFeature(f)) : featuresToCheck.some((f) => hasFeature(f)); if (hasAccess) { return <>{children}; } return <>{fallback}; }; // ============================================================================ // LimitGate - For integer limit checks // ============================================================================ interface LimitGateProps { /** * The limit feature code to check (e.g., 'max_users') */ limit: string; /** * Current usage count */ currentUsage: number; /** * Content to render when under the limit */ children: React.ReactNode; /** * Content to render when at or over the limit */ fallback?: React.ReactNode; /** * Content to render while entitlements are loading */ loadingFallback?: React.ReactNode; } /** * Conditionally render content based on usage limits. * * @example * ```tsx * } * > * * * ``` */ export const LimitGate: React.FC = ({ limit, currentUsage, children, fallback = null, loadingFallback = null, }) => { const { getLimit, isLoading } = useEntitlements(); // Show loading state if provided if (isLoading) { return <>{loadingFallback}; } const maxLimit = getLimit(limit); // If limit is null, treat as unlimited if (maxLimit === null) { return <>{children}; } // Check if under limit if (currentUsage < maxLimit) { return <>{children}; } return <>{fallback}; }; // ============================================================================ // Helper Components // ============================================================================ interface UpgradePromptProps { /** * Feature name to display */ feature?: string; /** * Custom message */ message?: string; /** * Upgrade URL (defaults to /settings/billing) */ upgradeUrl?: string; } /** * Default upgrade prompt component. * Can be used as a fallback in FeatureGate/LimitGate. */ export const UpgradePrompt: React.FC = ({ feature, message, upgradeUrl = '/settings/billing', }) => { const displayMessage = message || (feature ? `Upgrade your plan to access ${feature}` : 'Upgrade your plan to access this feature'); return (
{displayMessage}
View upgrade options →
); }; export default FeatureGate;