Feature Flags
Feature flag constants and types for enabling/disabling features across the WorkPayCore frontend application.
Feature Flag Constants
FEATURE_FLAGS
Central registry of all feature flags used in the application.
export const FEATURE_FLAGS = {
TA_V2: 'revamped-time-and-attendance',
'local-to-global-payroll-migration': 'local-to-global-payroll-migration',
employee_expense_report: 'employee-based-monthly-expense-report',
'revamped-run-payroll-interface': 'revamped-run-payroll-interface',
multi_category_expense: 'multi-category-expense-request',
PAYMENTS_V2: 'revamped-payouts-module',
revamped_payslip: 'revamped-payslip',
wallet_pin_reset: 'wallet-pin-reset',
'employee-history': 'employee-history',
} as const;
Available Feature Flags
TA_V2
Flag: revamped-time-and-attendance
Purpose: Enable the new Time & Attendance interface Impact: Switches to
the redesigned T&A module with improved UX
local-to-global-payroll-migration
Flag: local-to-global-payroll-migration
Purpose: Enable migration tools for payroll systems Impact: Shows
migration utilities for transitioning payroll data
employee_expense_report
Flag: employee-based-monthly-expense-report
Purpose: Enable employee-specific expense reporting Impact: Allows
employees to generate their own monthly expense reports
revamped-run-payroll-interface
Flag: revamped-run-payroll-interface
Purpose: Enable the new payroll processing interface Impact: Uses the
redesigned payroll run interface
multi_category_expense
Flag: multi-category-expense-request
Purpose: Enable multi-category expense requests Impact: Allows expenses
to be categorized across multiple categories
PAYMENTS_V2
Flag: revamped-payouts-module
Purpose: Enable the new payments/payouts module Impact: Switches to the
redesigned payments interface
revamped_payslip
Flag: revamped-payslip
Purpose: Enable the new payslip design Impact: Uses the redesigned
payslip layout and functionality
wallet_pin_reset
Flag: wallet-pin-reset
Purpose: Enable wallet PIN reset functionality Impact: Shows wallet PIN
reset options in user settings
employee-history
Flag: employee-history
Purpose: Enable employee history tracking Impact: Shows detailed
employee history and audit trails
TypeScript Types
TFeatFlagKeys
Type representing all available feature flag keys.
export type TFeatFlagKeys = keyof typeof FEATURE_FLAGS;
// Type: 'TA_V2' | 'local-to-global-payroll-migration' | 'employee_expense_report' | ...
TFeatFlagValues
Type representing all feature flag values (the actual flag strings).
export type TFeatFlagValues =
(typeof FEATURE_FLAGS)[keyof typeof FEATURE_FLAGS];
// Type: 'revamped-time-and-attendance' | 'local-to-global-payroll-migration' | ...
Usage Examples
Basic Feature Flag Check
import { FEATURE_FLAGS } from '@/utils/constants/feature-flags';
// Check if a feature is enabled (assuming you have a helper function)
const isTimeAttendanceV2Enabled = isFeatureEnabled(FEATURE_FLAGS.TA_V2);
const TimeAttendanceComponent = () => {
if (isTimeAttendanceV2Enabled) {
return <TimeAttendanceV2 />;
}
return <LegacyTimeAttendance />;
};
Conditional Component Rendering
import { FEATURE_FLAGS, TFeatFlagKeys } from '@/utils/constants/feature-flags';
interface FeatureGateProps {
feature: TFeatFlagKeys;
children: React.ReactNode;
fallback?: React.ReactNode;
}
const FeatureGate: React.FC<FeatureGateProps> = ({
feature,
children,
fallback = null,
}) => {
const flagValue = FEATURE_FLAGS[feature];
const isEnabled = useFeatureFlag(flagValue);
return isEnabled ? <>{children}</> : <>{fallback}</>;
};
// Usage
const PayrollInterface = () => (
<div>
<h1>Payroll</h1>
<FeatureGate
feature='revamped-run-payroll-interface'
fallback={<LegacyPayrollInterface />}
>
<NewPayrollInterface />
</FeatureGate>
</div>
);
Route-Based Feature Flags
import { FEATURE_FLAGS } from '@/utils/constants/feature-flags';
const AppRoutes = () => {
const isPaymentsV2Enabled = useFeatureFlag(FEATURE_FLAGS.PAYMENTS_V2);
return (
<Routes>
<Route path='/dashboard' element={<Dashboard />} />
{isPaymentsV2Enabled ? (
<Route path='/payments' element={<PaymentsV2 />} />
) : (
<Route path='/payments' element={<LegacyPayments />} />
)}
<Route path='/employees' element={<Employees />} />
</Routes>
);
};
Feature Flag Hook
import {
FEATURE_FLAGS,
TFeatFlagValues,
} from '@/utils/constants/feature-flags';
const useFeatureFlag = (flag: TFeatFlagValues): boolean => {
const [isEnabled, setIsEnabled] = useState(false);
useEffect(() => {
// This would typically call your feature flag service
const checkFeatureFlag = async () => {
try {
const enabled = await featureFlagService.isEnabled(flag);
setIsEnabled(enabled);
} catch (error) {
console.error('Failed to check feature flag:', error);
setIsEnabled(false);
}
};
checkFeatureFlag();
}, [flag]);
return isEnabled;
};
// Usage in components
const ExpenseReporting = () => {
const isEmployeeReportEnabled = useFeatureFlag(
FEATURE_FLAGS.employee_expense_report,
);
return (
<div>
<h2>Expense Reports</h2>
{isEmployeeReportEnabled && (
<button onClick={generateEmployeeReport}>
Generate My Monthly Report
</button>
)}
<ExpenseList />
</div>
);
};
Multi-Feature Component
const PayslipViewer = ({ payslip }) => {
const isRevampedPayslip = useFeatureFlag(FEATURE_FLAGS.revamped_payslip);
const isWalletPinReset = useFeatureFlag(FEATURE_FLAGS.wallet_pin_reset);
return (
<div className='payslip-viewer'>
{isRevampedPayslip ? (
<RevampedPayslipLayout payslip={payslip} />
) : (
<LegacyPayslipLayout payslip={payslip} />
)}
{isWalletPinReset && <WalletPinResetButton />}
</div>
);
};
Feature Flag Provider
import { FEATURE_FLAGS } from '@/utils/constants/feature-flags';
interface FeatureFlags {
[key: string]: boolean;
}
const FeatureFlagContext = createContext<FeatureFlags>({});
export const FeatureFlagProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const [flags, setFlags] = useState<FeatureFlags>({});
useEffect(() => {
const loadFeatureFlags = async () => {
const flagPromises = Object.entries(FEATURE_FLAGS).map(
async ([key, value]) => {
const enabled = await featureFlagService.isEnabled(value);
return [key, enabled];
},
);
const results = await Promise.all(flagPromises);
const flagsObject = Object.fromEntries(results);
setFlags(flagsObject);
};
loadFeatureFlags();
}, []);
return (
<FeatureFlagContext.Provider value={flags}>
{children}
</FeatureFlagContext.Provider>
);
};
export const useFeatureFlags = () => useContext(FeatureFlagContext);
Best Practices
Flag Naming
- Use descriptive names that clearly indicate the feature
- Follow consistent patterns (kebab-case for values)
- Include version numbers for major rewrites (V2, V3)
- Avoid negatives (use
enable_featurenotdisable_old_feature)
Flag Management
- Centralize flag definitions in this constants file
- Use TypeScript types for type safety
- Document flag purposes and impacts
- Clean up old flags after feature rollout
Implementation
- Fail safely - default to stable behavior if flag check fails
- Cache flag values to avoid repeated API calls
- Provide fallbacks for when features are disabled
- Test both states of feature flags
Integration Patterns
Environment-Based Defaults
const getDefaultFeatureFlags = () => {
if (isDev) {
// Enable all features in development
return Object.values(FEATURE_FLAGS).reduce(
(acc, flag) => ({
...acc,
[flag]: true,
}),
{},
);
}
// Production defaults
return {
[FEATURE_FLAGS.TA_V2]: false,
[FEATURE_FLAGS.PAYMENTS_V2]: true,
// ... other defaults
};
};
A/B Testing Integration
const useABTestFeatureFlag = (flag: TFeatFlagValues, userId: string) => {
const [isEnabled, setIsEnabled] = useState(false);
useEffect(() => {
const variant = getABTestVariant(flag, userId);
setIsEnabled(variant === 'enabled');
}, [flag, userId]);
return isEnabled;
};
Related Utilities
- Browser Utils - For environment-specific flag behavior
- Hooks - For feature flag state management
Complete Type Definitions
export const FEATURE_FLAGS = {
TA_V2: 'revamped-time-and-attendance',
'local-to-global-payroll-migration': 'local-to-global-payroll-migration',
employee_expense_report: 'employee-based-monthly-expense-report',
'revamped-run-payroll-interface': 'revamped-run-payroll-interface',
multi_category_expense: 'multi-category-expense-request',
PAYMENTS_V2: 'revamped-payouts-module',
revamped_payslip: 'revamped-payslip',
wallet_pin_reset: 'wallet-pin-reset',
'employee-history': 'employee-history',
} as const;
export type TFeatFlagKeys = keyof typeof FEATURE_FLAGS;
export type TFeatFlagValues =
(typeof FEATURE_FLAGS)[keyof typeof FEATURE_FLAGS];