@@ -14,6 +14,7 @@ import {
1414 type FeatureFlagKey ,
1515 type FlagControlType ,
1616 getAllFlagControlTypes ,
17+ lockedFlagsInPayload ,
1718 validatePartialFeatureFlags ,
1819} from "~/v3/featureFlags" ;
1920import { flags as getGlobalFlags , replaceGlobalFeatureFlags } from "~/v3/featureFlags.server" ;
@@ -29,6 +30,7 @@ import {
2930 DialogFooter ,
3031} from "~/components/primitives/Dialog" ;
3132import { cn } from "~/utils/cn" ;
33+ import { buildFlagChangeList } from "~/components/admin/flagChangeList" ;
3234import {
3335 UNSET_VALUE ,
3436 BooleanControl ,
@@ -111,17 +113,12 @@ export const action = dashboardAction(
111113
112114 const { isManagedCloud } = featuresForRequest ( request ) ;
113115
114- // On managed cloud, reject if payload includes locked flags
115- if ( isManagedCloud ) {
116- const lockedInPayload = Object . keys ( parsed . data . flags ) . filter ( ( key ) =>
117- GLOBAL_LOCKED_FLAGS . includes ( key )
116+ const lockedInPayload = lockedFlagsInPayload ( Object . keys ( parsed . data . flags ) , isManagedCloud ) ;
117+ if ( lockedInPayload . length > 0 ) {
118+ return json (
119+ { error : `Cannot modify locked flags: ${ lockedInPayload . join ( ", " ) } ` } ,
120+ { status : 400 }
118121 ) ;
119- if ( lockedInPayload . length > 0 ) {
120- return json (
121- { error : `Cannot modify locked flags: ${ lockedInPayload . join ( ", " ) } ` } ,
122- { status : 400 }
123- ) ;
124- }
125122 }
126123
127124 const validationResult = validatePartialFeatureFlags ( parsed . data . flags ) ;
@@ -137,6 +134,7 @@ export const action = dashboardAction(
137134 catalogKeys : Object . keys ( getAllFlagControlTypes ( ) ) as FeatureFlagKey [ ] ,
138135 isManagedCloud,
139136 unlockLockedFlags : parsed . data . unlockLockedFlags ?? false ,
137+ graceMs : env . RUN_OPS_MINT_FLIP_GRACE_MS ,
140138 } ) ;
141139
142140 return json ( { success : true } ) ;
@@ -401,6 +399,7 @@ export default function AdminFeatureFlagsRoute() {
401399 open = { confirmOpen }
402400 onOpenChange = { setConfirmOpen }
403401 initialValues = { initialValues }
402+ storedValues = { allFlags }
404403 newValues = { values }
405404 controlTypes = { typedControlTypes }
406405 lockedKeys = { unlocked ? [ ] : GLOBAL_LOCKED_FLAGS }
@@ -467,6 +466,7 @@ function ConfirmDialog({
467466 open,
468467 onOpenChange,
469468 initialValues,
469+ storedValues,
470470 newValues,
471471 controlTypes,
472472 lockedKeys,
@@ -477,6 +477,7 @@ function ConfirmDialog({
477477 open : boolean ;
478478 onOpenChange : ( open : boolean ) => void ;
479479 initialValues : Record < string , unknown > ;
480+ storedValues : Record < string , unknown > ;
480481 newValues : Record < string , unknown > ;
481482 controlTypes : Record < string , FlagControlType > ;
482483 lockedKeys : readonly string [ ] ;
@@ -488,34 +489,12 @@ function ConfirmDialog({
488489 . filter ( ( key ) => ! lockedKeys . includes ( key ) )
489490 . sort ( ) ;
490491
491- type Change =
492- | { key : string ; type : "added" ; newVal : string }
493- | { key : string ; type : "removed" ; oldVal : string }
494- | { key : string ; type : "changed" ; oldVal : string ; newVal : string } ;
495-
496- const changes = editableKeys . flatMap < Change > ( ( key ) => {
497- const wasSet = key in initialValues ;
498- const isSet = key in newValues ;
499- const oldVal = initialValues [ key ] ;
500- const newVal = newValues [ key ] ;
501-
502- if ( ! wasSet && ! isSet ) return [ ] ;
503- if ( wasSet && isSet && stableStringify ( oldVal ) === stableStringify ( newVal ) ) return [ ] ;
504-
505- if ( ! wasSet && isSet ) {
506- return [ { key, type : "added" as const , newVal : String ( newVal ) } ] ;
507- }
508- if ( wasSet && ! isSet ) {
509- return [ { key, type : "removed" as const , oldVal : String ( oldVal ) } ] ;
510- }
511- return [
512- {
513- key,
514- type : "changed" as const ,
515- oldVal : String ( oldVal ) ,
516- newVal : String ( newVal ) ,
517- } ,
518- ] ;
492+ const changes = buildFlagChangeList ( {
493+ editableKeys,
494+ lockedKeys,
495+ initialValues,
496+ storedValues,
497+ newValues,
519498 } ) ;
520499
521500 return (
0 commit comments