import { useState } from 'react'; /** * Feature flags for in-progress UI that ships behind a toggle. * * Resolution order (first defined wins): * 1. localStorage override → `localStorage.wpa_feature_flags = '{"billing":true}'` * (or use `setFeatureOverride('billing', true)` from the console) * 2. build env var → `VITE_FF_BILLING=true` * 3. default → ON in dev, OFF in production builds */ export type FeatureFlag = | 'billing' | 'workflows'; const ENV_OVERRIDES: Record = { billing: import.meta.env.VITE_FF_BILLING, workflows: import.meta.env.VITE_FF_WORKFLOWS, }; const DEFAULTS: Record = { billing: import.meta.env.DEV, workflows: import.meta.env.DEV, }; const STORAGE_KEY = 'wpa_feature_flags'; /** Every known flag (derived from DEFAULTS, so it can't drift out of sync). */ export const ALL_FEATURE_FLAGS = Object.keys(DEFAULTS) as FeatureFlag[]; const parseBool = (value: string | undefined): boolean | undefined => { if (value === undefined) return undefined; return value === 'true' || value === '1'; }; const readLocalOverrides = (): Partial> => { try { const raw = localStorage.getItem(STORAGE_KEY); return raw ? JSON.parse(raw) : {}; } catch { return {}; } }; export const isFeatureEnabled = (flag: FeatureFlag): boolean => { const local = readLocalOverrides()[flag]; if (typeof local === 'boolean') return local; const env = parseBool(ENV_OVERRIDES[flag]); if (typeof env === 'boolean') return env; return DEFAULTS[flag]; }; /** Dev helper: persist a per-flag override (pass `null` to clear). Reload to apply. */ export const setFeatureOverride = (flag: FeatureFlag, value: boolean | null) => { const overrides = readLocalOverrides(); if (value === null) { delete overrides[flag]; } else { overrides[flag] = value; } try { localStorage.setItem(STORAGE_KEY, JSON.stringify(overrides)); } catch { // ignore storage failures } }; /** Read a flag once on mount (reload to pick up an override change). */ export const useFeatureFlag = (flag: FeatureFlag): boolean => useState(() => isFeatureEnabled(flag))[0]; // Expose a tiny console helper for flipping flags — available in production too // (incl. the WP plugin build), since defaults are OFF there and this is the only // way to enable in-progress UI without a rebuild. It only reads/writes the same // localStorage override you could set by hand. Reload to apply a change. if (typeof window !== 'undefined') { (window as unknown as Record).wpaFeatures = { isEnabled: isFeatureEnabled, set: setFeatureOverride, /** Set every flag on (true) or off (false). Reload to apply. */ setAll: (value: boolean) => ALL_FEATURE_FLAGS.forEach((flag) => setFeatureOverride(flag, value)), enableAll: () => ALL_FEATURE_FLAGS.forEach((flag) => setFeatureOverride(flag, true)), disableAll: () => ALL_FEATURE_FLAGS.forEach((flag) => setFeatureOverride(flag, false)), /** Clear all overrides, reverting to build defaults. Reload to apply. */ reset: () => ALL_FEATURE_FLAGS.forEach((flag) => setFeatureOverride(flag, null)), /** Snapshot of every flag's currently resolved value. */ list: () => Object.fromEntries(ALL_FEATURE_FLAGS.map((flag) => [flag, isFeatureEnabled(flag)])), }; }