Skip to main content

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&lt;FeatureGateProps&gt; = ({
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 (
&lt;Routes&gt;
<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&lt;FeatureFlags&gt;({});

export const FeatureFlagProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const [flags, setFlags] = useState&lt;FeatureFlags&gt;({});

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

  1. Use descriptive names that clearly indicate the feature
  2. Follow consistent patterns (kebab-case for values)
  3. Include version numbers for major rewrites (V2, V3)
  4. Avoid negatives (use enable_feature not disable_old_feature)

Flag Management

  1. Centralize flag definitions in this constants file
  2. Use TypeScript types for type safety
  3. Document flag purposes and impacts
  4. Clean up old flags after feature rollout

Implementation

  1. Fail safely - default to stable behavior if flag check fails
  2. Cache flag values to avoid repeated API calls
  3. Provide fallbacks for when features are disabled
  4. 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;
};

  • 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];