Feature Flags
The app imports feature-flag hooks from src/feature-flags/splitio-react-replacement.jsx. That module keeps the old
Split-shaped component and hook API intact while removing the runtime dependency on Split.
Code should import this local module directly. We no longer rely on a Vite alias for the old Split package.
Current storage contract
The compatibility layer reads the active shop from Redux, then fetches DB-backed assignments from:
GET /feature-flags/bodyshops/:bodyshopId
That endpoint verifies the Firebase user can access the bodyshop through Hasura permissions, then returns cached Redis
data when present or refreshes from feature_flags + bodyshop_feature_flags.
On successful backend responses, the client stores the last-known flag payload in browser localStorage for the active
bodyshop. If the backend cannot be reached later, the client uses that bodyshop-scoped browser cache for up to 24 hours.
If there is no browser cache, unknown flags resolve to "off".
Recommended backend payload shape:
{
"flags": {
"Enhanced_Payroll": {
"treatment": "on",
"config": null,
"activeDate": null,
"deactiveDate": null
},
"Demo_Feature": {
"treatment": "on",
"config": null,
"activeDate": "2026-06-01T13:00:00-04:00",
"deactiveDate": "2026-06-05T17:00:00-04:00"
}
}
}
Supported values:
true,"true",1,"on"-> treatment"on"false,"false",0,"off"-> treatment"off"- ISO-ish future date strings ->
"on"until the date passes { "treatment": "on" | "off" | "control" | "any-custom-treatment", "config": ... }- Scheduled demo windows using
activeDateanddeactiveDate
Unknown flags default to "off".
Backend registry
Canonical feature flag definitions live in the Hasura-backed feature_flags table and are exposed to the admin panel
through GET /adm/feature-flags.
Per-shop assignments live in bodyshop_feature_flags. The admin panel reads them through
GET /adm/bodyshops/:bodyshopId/feature-flags and saves them through POST /adm/updateshop.
Hasura invalidates the Redis cache through /feature-flags/cache/invalidate when bodyshop_feature_flags or
feature_flags changes. Assignment changes clear the affected shop cache for the current cache version; definition
changes increment a global feature flag cache version so old per-shop cache entries become invisible and expire by TTL.
The backend also emits feature-flags-changed over the existing Socket.IO connection. SocketProvider bridges that
socket message to a browser event, and SplitFactoryProvider refetches flags when the event is global or matches the
active bodyshop. This keeps already-open tabs in sync with admin edits and Hasura-triggered invalidation.
For manual frontend testing, the global footer displays Test Feature Flag Enabled when TEST_FLAG resolves to
the on treatment.