feature flags

This commit is contained in:
2026-01-07 22:05:53 +01:00
parent 1b63fa646c
commit 606b64cec7
530 changed files with 2092 additions and 2943 deletions

View File

@@ -1,47 +1,57 @@
'use client';
import { useState, useEffect, ReactNode } from 'react';
import { FeatureFlagService } from '@/lib/feature/FeatureFlagService';
/**
* ModeGuard - Conditional rendering component based on application mode
* ModeGuard - Conditional rendering component based on feature availability
*
* Usage:
* <ModeGuard mode="pre-launch">
* <PreLaunchContent />
* <ModeGuard feature="platform.dashboard">
* <DashboardContent />
* </ModeGuard>
*
* <ModeGuard mode="alpha">
* <FullPlatformContent />
* <ModeGuard feature="alpha_features">
* <AlphaContent />
* </ModeGuard>
*/
export type GuardMode = 'pre-launch' | 'alpha';
interface ModeGuardProps {
mode: GuardMode;
feature: string;
children: ReactNode;
fallback?: ReactNode;
}
/**
* Client-side mode guard component
* Note: For initial page load, rely on middleware for route protection
* This component is for conditional UI rendering within accessible pages
* Client-side feature guard component
* Uses API-driven feature flags instead of environment mode
*/
export function ModeGuard({ mode, children, fallback = null }: ModeGuardProps) {
export function ModeGuard({ feature, children, fallback = null }: ModeGuardProps) {
const [isMounted, setIsMounted] = useState(false);
const [currentMode, setCurrentMode] = useState<GuardMode>('pre-launch');
const [isEnabled, setIsEnabled] = useState(false);
useEffect(() => {
setIsMounted(true);
setCurrentMode(getClientMode());
}, []);
// Check feature availability using API-driven service
const checkFeature = async () => {
try {
const service = await FeatureFlagService.fromAPI();
setIsEnabled(service.isEnabled(feature));
} catch (error) {
console.error('Error checking feature:', error);
setIsEnabled(false);
}
};
checkFeature();
}, [feature]);
if (!isMounted) {
return <>{fallback}</>;
}
if (currentMode !== mode) {
if (!isEnabled) {
return <>{fallback}</>;
}
@@ -49,40 +59,57 @@ export function ModeGuard({ mode, children, fallback = null }: ModeGuardProps) {
}
/**
* Get mode on client side from injected environment variable
* Falls back to 'pre-launch' if not available
* Hook to check feature availability in client components
*/
function getClientMode(): GuardMode {
if (typeof window === 'undefined') {
return 'pre-launch';
}
const mode = process.env.NEXT_PUBLIC_GRIDPILOT_MODE;
if (mode === 'alpha') {
return 'alpha';
}
return 'pre-launch';
export function useFeature(feature: string): boolean {
const [isEnabled, setIsEnabled] = useState(false);
useEffect(() => {
const checkFeature = async () => {
try {
const service = await FeatureFlagService.fromAPI();
setIsEnabled(service.isEnabled(feature));
} catch (error) {
console.error('Error checking feature:', error);
setIsEnabled(false);
}
};
checkFeature();
}, [feature]);
return isEnabled;
}
/**
* Hook to get current mode in client components
* Hook to check multiple features
*/
export function useAppMode(): GuardMode {
return getClientMode();
}
export function useFeatures(features: string[]): Record<string, boolean> {
const [featureStates, setFeatureStates] = useState<Record<string, boolean>>({});
/**
* Hook to check if in pre-launch mode
*/
export function useIsPreLaunch(): boolean {
return getClientMode() === 'pre-launch';
}
useEffect(() => {
const checkFeatures = async () => {
try {
const service = await FeatureFlagService.fromAPI();
const states: Record<string, boolean> = {};
features.forEach(feature => {
states[feature] = service.isEnabled(feature);
});
setFeatureStates(states);
} catch (error) {
console.error('Error checking features:', error);
const states: Record<string, boolean> = {};
features.forEach(feature => {
states[feature] = false;
});
setFeatureStates(states);
}
};
checkFeatures();
}, [features]);
/**
* Hook to check if in alpha mode
*/
export function useIsAlpha(): boolean {
return getClientMode() === 'alpha';
return featureStates;
}