@@ -39,7 +39,8 @@ import {
3939 dependentKey ,
4040 effectiveCopyDependentValue ,
4141 effectiveDependentValue ,
42- getActionableDependentFields ,
42+ getDisplayedDependentFields ,
43+ isDependentConfigurationActionable ,
4344} from '@/ee/workspace-forking/components/fork-sync/dependent-value'
4445import type {
4546 ForkKindSummary ,
@@ -109,7 +110,8 @@ function groupDependentsByWorkflow(
109110 workflows : ForkResourceUsage [ 'workflows' ] ,
110111 dependents : ForkDependentReconfig [ ] ,
111112 reconfig : Record < string , string > ,
112- state : DependentConfigurationState
113+ state : DependentConfigurationState ,
114+ showConfigured : boolean
113115) : WorkflowDependents [ ] {
114116 const byWorkflow = new Map < string , ForkDependentReconfig [ ] > ( )
115117 for ( const dependent of dependents ) {
@@ -138,7 +140,12 @@ function groupDependentsByWorkflow(
138140 blocks : Array . from ( byBlock . values ( ) )
139141 . map ( ( block ) => ( {
140142 ...block ,
141- configurableFields : getActionableDependentFields ( block . fields , reconfig , state ) ,
143+ configurableFields : getDisplayedDependentFields (
144+ block . fields ,
145+ reconfig ,
146+ state ,
147+ showConfigured
148+ ) ,
142149 } ) )
143150 . filter ( ( block ) => block . configurableFields . length > 0 )
144151 . sort ( ( a , b ) => a . blockName . localeCompare ( b . blockName ) ) ,
@@ -149,11 +156,13 @@ function groupDependentsByWorkflow(
149156/** Chain state for one block: the SelectorContext values its parent fields provide. */
150157function blockChainState (
151158 block : DependentBlock ,
159+ activeField : ForkDependentReconfig ,
152160 effectiveValue : ( field : ForkDependentReconfig ) => string
153161) {
154162 const providedValues : Record < string , string > = { }
155163 const providedContextKeys = new Set < string > ( )
156164 for ( const field of block . fields ) {
165+ if ( field . dependencyScope !== activeField . dependencyScope ) continue
157166 if ( field . providesContextKey ) {
158167 providedContextKeys . add ( field . providesContextKey )
159168 const value = effectiveValue ( field )
@@ -199,7 +208,7 @@ function DependentSelector({
199208 copying
200209 ? effectiveCopyDependentValue ( f , reconfig )
201210 : effectiveDependentValue ( f , reconfig , parentChanged )
202- const { providedValues, providedContextKeys } = blockChainState ( block , effectiveValue )
211+ const { providedValues, providedContextKeys } = blockChainState ( block , field , effectiveValue )
203212 // Disabled until every in-block parent it depends on has a value, so a child never queries
204213 // a stale upstream value.
205214 const ready = field . consumesContextKeys . every (
@@ -230,6 +239,7 @@ function DependentSelector({
230239
231240interface DependentWorkflowCardProps {
232241 workflow : WorkflowDependents
242+ initiallyExpanded : boolean
233243 target : string
234244 parentChanged : boolean
235245 /** True when the parent is resolved by COPY - the selectors browse the SOURCE parent. */
@@ -244,10 +254,12 @@ interface DependentWorkflowCardProps {
244254 * One workflow's dependent fields as a collapsible card (the same `CollapsibleCard` the table
245255 * workflow sidebar's input mapping and the enrichment config use): the header names the
246256 * workflow; the body groups fields under block → optional tool → plain field label.
247- * Cards holding a required field start expanded - a required field is what gates Sync.
257+ * Cards holding a required field start expanded because that field gates Sync. Cards first
258+ * revealed by explicit edit mode also start expanded so the edit action exposes its controls.
248259 */
249260function DependentWorkflowCard ( {
250261 workflow,
262+ initiallyExpanded,
251263 target,
252264 parentChanged,
253265 copying,
@@ -257,7 +269,9 @@ function DependentWorkflowCard({
257269 setReconfig,
258270} : DependentWorkflowCardProps ) {
259271 const [ collapsed , setCollapsed ] = useState (
260- ( ) => ! workflow . blocks . some ( ( block ) => block . configurableFields . some ( ( field ) => field . required ) )
272+ ( ) =>
273+ ! initiallyExpanded &&
274+ ! workflow . blocks . some ( ( block ) => block . configurableFields . some ( ( field ) => field . required ) )
261275 )
262276 return (
263277 < CollapsibleCard
@@ -268,14 +282,17 @@ function DependentWorkflowCard({
268282 < div className = 'flex flex-col gap-3' >
269283 { workflow . blocks . map ( ( block ) => {
270284 const topLevel = block . configurableFields . filter ( ( field ) => ! field . toolName )
271- const byTool = new Map < string , ForkDependentReconfig [ ] > ( )
285+ const byTool = new Map < string , { name : string ; fields : ForkDependentReconfig [ ] } > ( )
272286 for ( const field of block . configurableFields ) {
273287 if ( ! field . toolName ) continue
274- const list = byTool . get ( field . toolName )
275- if ( list ) list . push ( field )
276- else byTool . set ( field . toolName , [ field ] )
288+ const scope = field . dependencyScope ?? field . toolName
289+ const group = byTool . get ( scope )
290+ if ( group ) group . fields . push ( field )
291+ else byTool . set ( scope , { name : field . toolName , fields : [ field ] } )
277292 }
278- const toolGroups = Array . from ( byTool . entries ( ) ) . sort ( ( [ a ] , [ b ] ) => a . localeCompare ( b ) )
293+ const toolGroups = Array . from ( byTool . entries ( ) ) . sort ( ( [ , a ] , [ , b ] ) =>
294+ a . name . localeCompare ( b . name )
295+ )
279296
280297 return (
281298 < div key = { block . targetBlockId } className = 'flex flex-col gap-2' >
@@ -299,10 +316,10 @@ function DependentWorkflowCard({
299316 />
300317 </ div >
301318 ) ) }
302- { toolGroups . map ( ( [ toolName , fields ] ) => (
303- < div key = { toolName } className = 'flex flex-col gap-1.5 pl-2' >
304- < span className = 'text-[var(--text-muted)] text-small' > { toolName } </ span >
305- { fields . map ( ( field ) => (
319+ { toolGroups . map ( ( [ scope , tool ] ) => (
320+ < div key = { scope } className = 'flex flex-col gap-1.5 pl-2' >
321+ < span className = 'text-[var(--text-muted)] text-small' > { tool . name } </ span >
322+ { tool . fields . map ( ( field ) => (
306323 < div key = { dependentKey ( field ) } className = 'flex flex-col gap-1' >
307324 < Label className = 'text-[var(--text-muted)] text-caption' >
308325 { field . title }
@@ -346,6 +363,7 @@ interface MappingEntryProps {
346363 * Workflows with nothing to configure are named in a muted note so the usage stays visible.
347364 */
348365function MappingEntry ( { controller, group, entry } : MappingEntryProps ) {
366+ const [ showConfigured , setShowConfigured ] = useState ( false )
349367 const target = controller . targetFor ( entry )
350368 const takenOwners = controller . takenOwnersFor ( entry , group . items )
351369 const parentChanged = controller . parentChangedFor ( entry )
@@ -354,17 +372,34 @@ function MappingEntry({ controller, group, entry }: MappingEntryProps) {
354372
355373 const usages = controller . usagesForEntry ( entry )
356374 const dependents = controller . dependentsForEntry ( entry )
375+ const parentResolved = target !== '' || copying
357376 const workflows = useMemo (
358377 ( ) =>
359- groupDependentsByWorkflow ( usages , dependents , controller . reconfig , {
360- parentResolved : target !== '' || copying ,
361- parentChanged,
362- copying,
363- } ) ,
364- [ usages , dependents , controller . reconfig , target , parentChanged , copying ]
378+ groupDependentsByWorkflow (
379+ usages ,
380+ dependents ,
381+ controller . reconfig ,
382+ { parentResolved, parentChanged, copying } ,
383+ showConfigured
384+ ) ,
385+ [
386+ usages ,
387+ dependents ,
388+ controller . reconfig ,
389+ parentResolved ,
390+ parentChanged ,
391+ copying ,
392+ showConfigured ,
393+ ]
365394 )
366395 const configurable = workflows . filter ( ( workflow ) => workflow . blocks . length > 0 )
367396 const usedOnly = workflows . filter ( ( workflow ) => workflow . blocks . length === 0 )
397+ const configurationState = { parentResolved, parentChanged, copying }
398+ const hasHiddenConfigured = dependents . some (
399+ ( field ) => ! isDependentConfigurationActionable ( field , controller . reconfig , configurationState )
400+ )
401+ const canEditConfigured =
402+ parentResolved && ! parentChanged && ! copying && ( showConfigured || hasHiddenConfigured )
368403
369404 return (
370405 < div className = 'flex flex-col gap-2' >
@@ -422,10 +457,18 @@ function MappingEntry({ controller, group, entry }: MappingEntryProps) {
422457 </ p >
423458 ) : null }
424459 </ div >
460+ { canEditConfigured ? (
461+ < div className = 'flex justify-end' >
462+ < Chip active = { showConfigured } onClick = { ( ) => setShowConfigured ( ( value ) => ! value ) } >
463+ { showConfigured ? 'Done editing' : 'Edit configuration' }
464+ </ Chip >
465+ </ div >
466+ ) : null }
425467 { configurable . map ( ( workflow ) => (
426468 < DependentWorkflowCard
427469 key = { workflow . workflowId }
428470 workflow = { workflow }
471+ initiallyExpanded = { showConfigured }
429472 target = { target }
430473 parentChanged = { parentChanged }
431474 copying = { copying }
@@ -437,8 +480,8 @@ function MappingEntry({ controller, group, entry }: MappingEntryProps) {
437480 ) ) }
438481 { usedOnly . length > 0 ? (
439482 < p className = 'text-[var(--text-tertiary)] text-caption' >
440- Also used in { usedOnly . map ( ( workflow ) => workflow . workflowName ) . join ( ', ' ) } — nothing to
441- configure there .
483+ Also used in { usedOnly . map ( ( workflow ) => workflow . workflowName ) . join ( ', ' ) } — no changes
484+ required .
442485 </ p >
443486 ) : null }
444487 </ div >
0 commit comments