@@ -7,7 +7,9 @@ import { FieldSelectionDropdown } from './FieldSelectionDropdown';
77export interface AddEntryFormState {
88 contentTypeId : string ;
99 isReference : boolean | null ;
10+ /** Parent entry tempId when linking as a child reference. */
1011 referenceEntryId : string ;
12+ /** Reference field on the parent content type. */
1113 referenceFieldId : string ;
1214 selectedFieldIds : string [ ] ;
1315}
@@ -23,6 +25,7 @@ export const INITIAL_ADD_ENTRY_FORM_STATE: AddEntryFormState = {
2325export interface ExistingEntryOption {
2426 tempId : string ;
2527 label : string ;
28+ contentTypeId : string ;
2629}
2730
2831const getReferenceFieldOptions = (
@@ -38,17 +41,43 @@ const getReferenceFieldOptions = (
3841 } ) ;
3942} ;
4043
44+ const getParentContentTypeId = (
45+ state : AddEntryFormState ,
46+ existingEntries : ExistingEntryOption [ ]
47+ ) : string =>
48+ existingEntries . find ( ( entry ) => entry . tempId === state . referenceEntryId ) ?. contentTypeId ?? '' ;
49+
50+ /** Existing entries whose content type has at least one Entry reference field. */
51+ export const getParentEntriesThatAcceptChildren = (
52+ contentTypes : WorkflowContentType [ ] ,
53+ existingEntries : ExistingEntryOption [ ]
54+ ) : ExistingEntryOption [ ] =>
55+ existingEntries . filter (
56+ ( entry ) => getReferenceFieldOptions ( contentTypes , entry . contentTypeId ) . length > 0
57+ ) ;
58+
59+ /** True when at least one existing entry's content type can accept a child reference. */
60+ export const canLinkAsChildReference = (
61+ contentTypes : WorkflowContentType [ ] ,
62+ existingEntries : ExistingEntryOption [ ]
63+ ) : boolean => getParentEntriesThatAcceptChildren ( contentTypes , existingEntries ) . length > 0 ;
64+
4165/** Returns true when the form lacks enough input to save. */
4266export const isAddEntrySaveDisabled = (
4367 state : AddEntryFormState ,
44- contentTypes : WorkflowContentType [ ]
68+ contentTypes : WorkflowContentType [ ] ,
69+ existingEntries : ExistingEntryOption [ ] = [ ]
4570) : boolean => {
4671 if ( ! state . contentTypeId ) return true ;
47- if ( state . isReference === null ) return true ;
72+ const canBeReference = canLinkAsChildReference ( contentTypes , existingEntries ) ;
73+ // Reference Yes/No is hidden when no parent can accept a child — treat as No.
74+ if ( canBeReference && state . isReference === null ) return true ;
4875 if ( state . isReference ) {
49- const referenceFieldOptions = getReferenceFieldOptions ( contentTypes , state . contentTypeId ) ;
50- if ( referenceFieldOptions . length === 0 ) return true ;
76+ // Child link lives on the parent — require a parent, then its reference field when ambiguous.
5177 if ( ! state . referenceEntryId ) return true ;
78+ const parentContentTypeId = getParentContentTypeId ( state , existingEntries ) ;
79+ const referenceFieldOptions = getReferenceFieldOptions ( contentTypes , parentContentTypeId ) ;
80+ if ( referenceFieldOptions . length === 0 ) return true ;
5281 if ( referenceFieldOptions . length > 1 && ! state . referenceFieldId ) return true ;
5382 }
5483 return state . selectedFieldIds . length === 0 ;
@@ -57,9 +86,11 @@ export const isAddEntrySaveDisabled = (
5786/** Maps form state to the payload expected by onAddEntry. */
5887export const toAddEntryFormParams = (
5988 state : AddEntryFormState ,
60- contentTypes : WorkflowContentType [ ]
89+ contentTypes : WorkflowContentType [ ] ,
90+ existingEntries : ExistingEntryOption [ ] = [ ]
6191) : AddEntryFormParams => {
62- const referenceFieldOptions = getReferenceFieldOptions ( contentTypes , state . contentTypeId ) ;
92+ const parentContentTypeId = getParentContentTypeId ( state , existingEntries ) ;
93+ const referenceFieldOptions = getReferenceFieldOptions ( contentTypes , parentContentTypeId ) ;
6394 return {
6495 contentTypeId : state . contentTypeId ,
6596 isReference : state . isReference ?? false ,
@@ -96,12 +127,22 @@ export const AddEntryForm = ({
96127 ( ) => buildFieldOptionsForContentType ( selectedContentType ) ,
97128 [ selectedContentType ]
98129 ) ;
130+ const parentContentTypeId = useMemo (
131+ ( ) =>
132+ existingEntries . find ( ( entry ) => entry . tempId === state . referenceEntryId ) ?. contentTypeId ?? '' ,
133+ [ existingEntries , state . referenceEntryId ]
134+ ) ;
99135 const referenceFieldOptions = useMemo (
100- ( ) => getReferenceFieldOptions ( contentTypes , state . contentTypeId ) ,
101- [ contentTypes , state . contentTypeId ]
136+ ( ) => getReferenceFieldOptions ( contentTypes , parentContentTypeId ) ,
137+ [ contentTypes , parentContentTypeId ]
138+ ) ;
139+ const showReferenceFieldSelect =
140+ Boolean ( state . referenceEntryId ) && referenceFieldOptions . length > 1 ;
141+ const parentEntryOptions = useMemo (
142+ ( ) => getParentEntriesThatAcceptChildren ( contentTypes , existingEntries ) ,
143+ [ contentTypes , existingEntries ]
102144 ) ;
103- const showReferenceFieldSelect = referenceFieldOptions . length > 1 ;
104- const canBeReference = referenceFieldOptions . length > 0 ;
145+ const canBeReference = parentEntryOptions . length > 0 ;
105146 const hasContentType = Boolean ( state . contentTypeId ) ;
106147
107148 const handleContentTypeChange = ( contentTypeId : string ) => {
@@ -126,6 +167,13 @@ export const AddEntryForm = ({
126167 } ) ;
127168 } ;
128169
170+ const handleParentEntryChange = ( referenceEntryId : string ) => {
171+ onChange ( {
172+ referenceEntryId,
173+ referenceFieldId : '' ,
174+ } ) ;
175+ } ;
176+
129177 return (
130178 < Flex flexDirection = "column" gap = "spacingS" >
131179 < Text as = "p" fontWeight = "fontWeightDemiBold" >
@@ -150,39 +198,44 @@ export const AddEntryForm = ({
150198
151199 { hasContentType && (
152200 < >
153- < FormControl marginBottom = "none" >
154- < FormControl . Label > Should this entry be a reference entry?</ FormControl . Label >
155- < Flex flexDirection = "column" gap = "spacingXs" >
156- < Radio
157- id = "ref-yes"
158- name = "is-reference"
159- value = "yes"
160- isChecked = { state . isReference === true }
161- isDisabled = { ! canBeReference }
162- onChange = { ( ) => handleReferenceChange ( true ) } >
163- Yes
164- </ Radio >
165- < Radio
166- id = "ref-no"
167- name = "is-reference"
168- value = "no"
169- isChecked = { state . isReference === false }
170- onChange = { ( ) => handleReferenceChange ( false ) } >
171- No
172- </ Radio >
173- </ Flex >
174- </ FormControl >
201+ { canBeReference && (
202+ < FormControl marginBottom = "none" >
203+ < FormControl . Label >
204+ Should this new entry be a reference of an existing entry?
205+ </ FormControl . Label >
206+ < Flex flexDirection = "column" gap = "spacingXs" >
207+ < Radio
208+ id = "ref-yes"
209+ name = "is-reference"
210+ value = "yes"
211+ isChecked = { state . isReference === true }
212+ onChange = { ( ) => handleReferenceChange ( true ) } >
213+ Yes
214+ </ Radio >
215+ < Radio
216+ id = "ref-no"
217+ name = "is-reference"
218+ value = "no"
219+ isChecked = { state . isReference === false }
220+ onChange = { ( ) => handleReferenceChange ( false ) } >
221+ No
222+ </ Radio >
223+ </ Flex >
224+ </ FormControl >
225+ ) }
175226
176- { state . isReference === true && (
227+ { canBeReference && state . isReference === true && (
177228 < FormControl marginBottom = "none" >
178- < FormControl . Label > Select the entry this should reference</ FormControl . Label >
229+ < FormControl . Label >
230+ Which existing entry should this new entry be a reference to?
231+ </ FormControl . Label >
179232 < Select
180233 value = { state . referenceEntryId }
181- onChange = { ( e ) => onChange ( { referenceEntryId : e . target . value } ) } >
234+ onChange = { ( e ) => handleParentEntryChange ( e . target . value ) } >
182235 < Select . Option value = "" isDisabled >
183236 Select an entry
184237 </ Select . Option >
185- { existingEntries . map ( ( entry ) => (
238+ { parentEntryOptions . map ( ( entry ) => (
186239 < Select . Option key = { entry . tempId } value = { entry . tempId } >
187240 { entry . label }
188241 </ Select . Option >
@@ -191,9 +244,11 @@ export const AddEntryForm = ({
191244 </ FormControl >
192245 ) }
193246
194- { state . isReference === true && showReferenceFieldSelect && (
247+ { canBeReference && state . isReference === true && showReferenceFieldSelect && (
195248 < FormControl marginBottom = "none" >
196- < FormControl . Label > Which field should connect to this reference?</ FormControl . Label >
249+ < FormControl . Label >
250+ Which field on the parent should link to this entry?
251+ </ FormControl . Label >
197252 < Select
198253 value = { state . referenceFieldId }
199254 onChange = { ( e ) => onChange ( { referenceFieldId : e . target . value } ) } >
0 commit comments