@@ -10,17 +10,27 @@ import {
1010} from "~/components/primitives/Dialog" ;
1111import { Button } from "~/components/primitives/Buttons" ;
1212import { Callout } from "~/components/primitives/Callout" ;
13+ import { LockClosedIcon } from "@heroicons/react/20/solid" ;
14+ import { CheckboxWithLabel } from "~/components/primitives/Checkbox" ;
1315import { cn } from "~/utils/cn" ;
14- import { FEATURE_FLAG , type FlagControlType } from "~/v3/featureFlags" ;
15- import { UNSET_VALUE , BooleanControl , EnumControl , StringControl } from "./FlagControls" ;
16-
17- const HIDDEN_FLAGS = [ FEATURE_FLAG . defaultWorkerInstanceGroupId ] ;
16+ import { FEATURE_FLAG , ORG_LOCKED_FLAGS , type FlagControlType } from "~/v3/featureFlags" ;
17+ import {
18+ UNSET_VALUE ,
19+ BooleanControl ,
20+ EnumControl ,
21+ StringControl ,
22+ WorkerGroupControl ,
23+ type WorkerGroup ,
24+ } from "./FlagControls" ;
1825
1926type LoaderData = {
2027 org : { id : string ; title : string ; slug : string } ;
2128 orgFlags : Record < string , unknown > ;
2229 globalFlags : Record < string , unknown > ;
2330 controlTypes : Record < string , FlagControlType > ;
31+ workerGroupName ?: string ;
32+ workerGroups ?: WorkerGroup [ ] ;
33+ isManagedCloud ?: boolean ;
2434} ;
2535
2636type ActionData = {
@@ -47,6 +57,9 @@ export function FeatureFlagsDialog({
4757 const [ overrides , setOverrides ] = useState < Record < string , unknown > > ( { } ) ;
4858 const [ initialOverrides , setInitialOverrides ] = useState < Record < string , unknown > > ( { } ) ;
4959 const [ saveError , setSaveError ] = useState < string | null > ( null ) ;
60+ const [ unlocked , setUnlocked ] = useState ( false ) ;
61+
62+ const isLocked = ( key : string ) => ! unlocked && ORG_LOCKED_FLAGS . includes ( key ) ;
5063
5164 useEffect ( ( ) => {
5265 if ( open && orgId ) {
@@ -104,11 +117,7 @@ export function FeatureFlagsDialog({
104117 const jsonPreview =
105118 Object . keys ( overrides ) . length === 0 ? "null" : JSON . stringify ( overrides , null , 2 ) ;
106119
107- const sortedFlagKeys = data
108- ? Object . keys ( data . controlTypes )
109- . filter ( ( key ) => ! HIDDEN_FLAGS . includes ( key ) )
110- . sort ( )
111- : [ ] ;
120+ const sortedFlagKeys = data ? Object . keys ( data . controlTypes ) . sort ( ) : [ ] ;
112121
113122 return (
114123 < Dialog open = { open } onOpenChange = { onOpenChange } >
@@ -118,16 +127,57 @@ export function FeatureFlagsDialog({
118127 Org-level overrides. Unset flags inherit from global defaults.
119128 </ DialogDescription >
120129
130+ { data && (
131+ < div className = { data . isManagedCloud ? "cursor-not-allowed" : undefined } >
132+ < CheckboxWithLabel
133+ variant = "simple/small"
134+ label = {
135+ data . isManagedCloud
136+ ? "Unlock read-only flags (only in unmanaged cloud)"
137+ : "Unlock read-only flags"
138+ }
139+ defaultChecked = { unlocked }
140+ onChange = { setUnlocked }
141+ disabled = { data . isManagedCloud }
142+ className = { data . isManagedCloud ? "pointer-events-none" : undefined }
143+ />
144+ </ div >
145+ ) }
146+
121147 < div className = "max-h-[60vh] overflow-y-auto" >
122148 { isLoading ? (
123149 < div className = "py-8 text-center text-sm text-text-dimmed" > Loading flags...</ div >
124150 ) : data ? (
125151 < div className = "flex flex-col gap-1.5" >
126152 { sortedFlagKeys . map ( ( key ) => {
127153 const control = data . controlTypes [ key ] ;
128- const isOverridden = key in overrides ;
154+ const locked = isLocked ( key ) ;
129155 const globalValue = data . globalFlags [ key as keyof typeof data . globalFlags ] ;
130- const globalDisplay = globalValue !== undefined ? String ( globalValue ) : "unset" ;
156+ const isWorkerGroup = key === FEATURE_FLAG . defaultWorkerInstanceGroupId ;
157+ const globalDisplay =
158+ isWorkerGroup && data . workerGroupName && globalValue !== undefined
159+ ? `${ data . workerGroupName } (${ String ( globalValue ) . slice ( 0 , 8 ) } ...)`
160+ : globalValue !== undefined
161+ ? String ( globalValue )
162+ : "unset" ;
163+
164+ if ( locked ) {
165+ return (
166+ < div
167+ key = { key }
168+ className = "flex items-center justify-between rounded-md border border-transparent bg-charcoal-750 px-3 py-2.5"
169+ title = "Global-level setting - not editable per org"
170+ >
171+ < div className = "min-w-0 flex-1" >
172+ < div className = "truncate text-sm text-text-dimmed" > { key } </ div >
173+ < div className = "text-xs text-charcoal-400" > global: { globalDisplay } </ div >
174+ </ div >
175+ < LockClosedIcon className = "size-4 text-charcoal-500" />
176+ </ div >
177+ ) ;
178+ }
179+
180+ const isOverridden = key in overrides ;
131181
132182 return (
133183 < div
@@ -160,15 +210,26 @@ export function FeatureFlagsDialog({
160210 unset
161211 </ Button >
162212
163- { control . type === "boolean" && (
213+ { isWorkerGroup && data . workerGroups ? (
214+ < WorkerGroupControl
215+ value = { isOverridden ? ( overrides [ key ] as string ) : undefined }
216+ workerGroups = { data . workerGroups as WorkerGroup [ ] }
217+ onChange = { ( val ) => {
218+ if ( val === UNSET_VALUE ) {
219+ unsetFlag ( key ) ;
220+ } else {
221+ setFlagValue ( key , val ) ;
222+ }
223+ } }
224+ dimmed = { ! isOverridden }
225+ />
226+ ) : control . type === "boolean" ? (
164227 < BooleanControl
165228 value = { isOverridden ? ( overrides [ key ] as boolean ) : undefined }
166229 onChange = { ( val ) => setFlagValue ( key , val ) }
167230 dimmed = { ! isOverridden }
168231 />
169- ) }
170-
171- { control . type === "enum" && (
232+ ) : control . type === "enum" ? (
172233 < EnumControl
173234 value = { isOverridden ? ( overrides [ key ] as string ) : undefined }
174235 options = { control . options }
@@ -181,9 +242,7 @@ export function FeatureFlagsDialog({
181242 } }
182243 dimmed = { ! isOverridden }
183244 />
184- ) }
185-
186- { control . type === "string" && (
245+ ) : control . type === "string" ? (
187246 < StringControl
188247 value = { isOverridden ? ( overrides [ key ] as string ) : "" }
189248 onChange = { ( val ) => {
@@ -195,7 +254,7 @@ export function FeatureFlagsDialog({
195254 } }
196255 dimmed = { ! isOverridden }
197256 />
198- ) }
257+ ) : null }
199258 </ div >
200259 </ div >
201260 ) ;
0 commit comments