Repository: strapi/strapi
Pull request #23382: feat: OTF relations create modal add save and publish
Author: login: simotae14
Labels: name: source: core:content-manager
description: Source is core/content-manager package
name: pr: feature
description: This PR adds a new feature
Created: 2025-04-16T14:42:39Z | Merged: 2025-04-24T15:05:18Z

<!--
Hello 👋 Thank you for submitting a pull request.

To help us merge your PR, make sure to follow the instructions below:

- Create or update the tests
- Create or update the documentation at https://github.com/strapi/documentation
- Refer to the issue you are closing in the PR description: Fix #issue
- Specify if the PR is ready to be merged or work in progress (by opening a draft PR)

Please ensure you read the Contributing Guide: https://github.com/strapi/strapi/blob/main/CONTRIBUTING.md
-->

### What does it do?

Add the logic in the create relation modal to save and publish a new entry, add the new relation to the parent relation field, track the didCreateEntry with fromRelationModal and fromPreview and handle the back button history in the case you try to open or create a new modal from the create relation modal without saving.
Also the fullpage click is handled in this PR

### Why is it needed?

We need to implement the logic to save and publish a new relation in the create relation modal

### How to test it?

Just try to save and publish relations in the create relation modal starting from collection types, single types document, DZ, components ...

### Related issue(s)/PR(s)

CS-1338
Fixes CS-1407



Diff:
diff --git a/packages/core/admin/admin/src/features/Tracking.tsx b/packages/core/admin/admin/src/features/Tracking.tsx
index 1405f32df418..9b0a3a43f8a8 100644
--- a/packages/core/admin/admin/src/features/Tracking.tsx
+++ b/packages/core/admin/admin/src/features/Tracking.tsx
@@ -318,6 +318,8 @@ interface CreateEntryEvents {
     documentId?: string;
     status?: string;
     error?: unknown;
+    fromPreview?: boolean;
+    fromRelationModal?: boolean;
   };
 }
 
diff --git a/packages/core/content-manager/admin/src/hooks/useDocumentActions.ts b/packages/core/content-manager/admin/src/hooks/useDocumentActions.ts
index dd639a0ffabf..68d0a34f4233 100644
--- a/packages/core/content-manager/admin/src/hooks/useDocumentActions.ts
+++ b/packages/core/content-manager/admin/src/hooks/useDocumentActions.ts
@@ -602,8 +602,12 @@ const useDocumentActions: UseDocumentActions = () => {
 
           return { error: res.error };
         }
-
-        trackUsage('didCreateEntry', { ...trackerProperty, documentId: res.data.data.documentId });
+        trackUsage('didCreateEntry', {
+          ...trackerProperty,
+          documentId: res.data.data.documentId,
+          fromPreview,
+          fromRelationModal,
+        });
 
         toggleNotification({
           type: 'success',
@@ -627,7 +631,16 @@ const useDocumentActions: UseDocumentActions = () => {
         throw err;
       }
     },
-    [createDocument, formatAPIError, formatMessage, setCurrentStep, toggleNotification, trackUsage]
+    [
+      createDocument,
+      formatAPIError,
+      formatMessage,
+      fromPreview,
+      fromRelationModal,
+      setCurrentStep,
+      toggleNotification,
+      trackUsage,
+    ]
   );
 
   const [autoCloneDocument] = useAutoCloneDocumentMutation();
diff --git a/packages/core/content-manager/admin/src/hooks/useDocumentContext.ts b/packages/core/content-manager/admin/src/hooks/useDocumentContext.ts
index 141adb917636..4b3c956ffad8 100644
--- a/packages/core/content-manager/admin/src/hooks/useDocumentContext.ts
+++ b/packages/core/content-manager/admin/src/hooks/useDocumentContext.ts
@@ -11,7 +11,7 @@ interface DocumentMeta {
    * The equivalent of the ":id" url param value
    * i.e. gus5a67jcboa3o2zjnz39mb1
    */
-  documentId: string;
+  documentId?: string;
   /**
    * The equivalent of the url ":slug" param value
    * i.e. api::articles.article
diff --git a/packages/core/content-manager/admin/src/pages/EditView/components/DocumentActions.tsx b/packages/core/content-manager/admin/src/pages/EditView/components/DocumentActions.tsx
index 9c15955b351b..009860ed6bdb 100644
--- a/packages/core/content-manager/admin/src/pages/EditView/components/DocumentActions.tsx
+++ b/packages/core/content-manager/admin/src/pages/EditView/components/DocumentActions.tsx
@@ -20,25 +20,34 @@ import {
 } from '@strapi/design-system';
 import { Cross, More, WarningCircle } from '@strapi/icons';
 import mapValues from 'lodash/fp/mapValues';
+import get from 'lodash/get';
+import merge from 'lodash/merge';
+import set from 'lodash/set';
 import { useIntl } from 'react-intl';
 import { useMatch, useNavigate, useParams } from 'react-router-dom';
 import { DefaultTheme, styled } from 'styled-components';
 
+import { Create, Publish } from '../../../../../shared/contracts/collection-types';
 import { PUBLISHED_AT_ATTRIBUTE_NAME } from '../../../constants/attributes';
 import { SINGLE_TYPES } from '../../../constants/collections';
 import { useDocumentRBAC } from '../../../features/DocumentRBAC';
-import { useDoc } from '../../../hooks/useDocument';
+import { useDoc, useDocument } from '../../../hooks/useDocument';
 import { useDocumentActions } from '../../../hooks/useDocumentActions';
 import { useDocumentContext } from '../../../hooks/useDocumentContext';
 import { usePreviewContext } from '../../../preview/pages/Preview';
 import { CLONE_PATH, LIST_PATH } from '../../../router';
-import { useGetDraftRelationCountQuery } from '../../../services/documents';
+import {
+  useGetDraftRelationCountQuery,
+  useUpdateDocumentMutation,
+} from '../../../services/documents';
 import { isBaseQueryError, buildValidParams } from '../../../utils/api';
 import { getTranslation } from '../../../utils/translations';
+import { AnyData } from '../utils/data';
+
+import { useRelationModal } from './FormInputs/Relations/RelationModal';
 
 import type { RelationsFormValue } from './FormInputs/Relations/Relations';
 import type { DocumentActionComponent } from '../../../content-manager';
-
 /* -------------------------------------------------------------------------------------------------
  * Types
  * -----------------------------------------------------------------------------------------------*/
@@ -108,6 +117,50 @@ interface DocumentActionsProps {
   actions: Action[];
 }
 
+const connectRelationToParent = (
+  parentDataToUpdate: AnyData | undefined,
+  fieldToConnect: string,
+  data: Create.Response['data'] | Publish.Response['data'],
+  fieldToConnectUID?: string
+) => {
+  /*
+   * Check if the fieldToConnect is already present in the parentDataToUpdate.
+   * This happens in particular when in the parentDocument you have created
+   * a new component without saving.
+   */
+  const isFieldPresent = !!get(parentDataToUpdate, fieldToConnect);
+  const fieldToConnectPath = isFieldPresent
+    ? fieldToConnect
+    : // Compute the path to the parent object
+      fieldToConnect.split('.').slice(0, -1).join('.');
+  const fieldToConnectValue = isFieldPresent
+    ? {
+        connect: [
+          {
+            id: data.documentId,
+            documentId: data.documentId,
+            locale: data.locale,
+          },
+        ],
+      }
+    : {
+        [fieldToConnect.split('.').pop()!]: {
+          connect: [
+            {
+              id: data.documentId,
+              documentId: data.documentId,
+              locale: data.locale,
+            },
+          ],
+          disconnect: [],
+        },
+        // In case the object was not present you need to pass the componentUID of the parent document
+        __component: fieldToConnectUID,
+      };
+  const objectToConnect = set({}, fieldToConnectPath, fieldToConnectValue);
+  return merge(parentDataToUpdate, objectToConnect);
+};
+
 const DocumentActions = ({ actions }: DocumentActionsProps) => {
   const { formatMessage } = useIntl();
   const [primaryAction, secondaryAction, ...restActions] = actions.filter((action) => {
@@ -550,7 +603,31 @@ const PublishAction: DocumentActionComponent = ({
   const formValues = useForm('PublishAction', ({ values }) => values);
   const resetForm = useForm('PublishAction', ({ resetForm }) => resetForm);
 
+  // need to discriminate if the publish is coming from a relation modal or in the edit view
+  const relationContext = useRelationModal('PublishAction', () => true, false);
+  const fromRelationModal = relationContext != undefined;
+
+  const dispatch = useRelationModal('PublishAction', (state) => state.dispatch);
+  const fieldToConnect = useRelationModal(
+    'PublishAction',
+    (state) => state.state.fieldToConnect,
+    false
+  );
+  const fieldToConnectUID = useRelationModal(
+    'PublishAction',
+    (state) => state.state.fieldToConnectUID,
+    false
+  );
+  const documentHistory = useRelationModal(
+    'PublishAction',
+    (state) => state.state.documentHistory,
+    false
+  );
+  const rootDocumentMeta = useRelationModal('PublishAction', (state) => state.rootDocumentMeta);
+
   const { currentDocumentMeta } = useDocumentContext('PublishAction');
+  const [updateDocumentMutation] = useUpdateDocumentMutation();
+  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
 
   const idToPublish = currentDocumentMeta.documentId || id;
 
@@ -636,6 +713,17 @@ const PublishAction: DocumentActionComponent = ({
     model,
     currentDocumentMeta.params,
   ]);
+  const parentDocumentMetaToUpdate = documentHistory?.at(-2) ?? rootDocumentMeta;
+  const parentDocumentData = useDocument(
+    {
+      documentId: parentDocumentMetaToUpdate?.documentId,
+      model: parentDocumentMetaToUpdate?.model,
+      collectionType: parentDocumentMetaToUpdate?.collectionType,
+      params: parentDocumentMetaToUpdate?.params,
+    },
+    { skip: !parentDocumentMetaToUpdate }
+  );
+  const { getInitialFormValues } = useDoc();
 
   const isDocumentPublished =
     (document?.[PUBLISHED_AT_ATTRIBUTE_NAME] ||
@@ -683,11 +771,76 @@ const PublishAction: DocumentActionComponent = ({
         /**
          * TODO: refactor the router so we can just do `../${res.data.documentId}` instead of this.
          */
-        if (idToPublish === 'create') {
+        if (idToPublish === 'create' && !fromRelationModal) {
           navigate({
             pathname: `../${collectionType}/${model}/${res.data.documentId}`,
             search: rawQuery,
           });
+        } else if (fromRelationModal) {
+          const newRelation = {
+            documentId: res.data.documentId,
+            collectionType,
+            model,
+            params: currentDocumentMeta.params,
+          };
+
+          /*
+           * Update, if needed, the parent relation with the newly published document.
+           * Check if in history we have the parent relation otherwise use the
+           * rootDocument
+           */
+          if (
+            fieldToConnect &&
+            documentHistory &&
+            (parentDocumentMetaToUpdate.documentId ||
+              parentDocumentMetaToUpdate.collectionType === SINGLE_TYPES)
+          ) {
+            const parentDataToUpdate =
+              parentDocumentMetaToUpdate.collectionType === SINGLE_TYPES
+                ? getInitialFormValues()
+                : parentDocumentData.getInitialFormValues();
+            const metaDocumentToUpdate = documentHistory.at(-2) ?? rootDocumentMeta;
+
+            const dataToUpdate = connectRelationToParent(
+              parentDataToUpdate,
+              fieldToConnect,
+              res.data,
+              fieldToConnectUID
+            );
+
+            try {
+              const updateRes = await updateDocumentMutation({
+                collectionType: metaDocumentToUpdate.collectionType,
+                model: metaDocumentToUpdate.model,
+                documentId:
+                  metaDocumentToUpdate.collectionType !== SINGLE_TYPES
+                    ? metaDocumentToUpdate.documentId
+                    : undefined,
+                params: metaDocumentToUpdate.params,
+                data: dataToUpdate,
+              });
+
+              if ('error' in updateRes) {
+                toggleNotification({ type: 'danger', message: formatAPIError(updateRes.error) });
+                return;
+              }
+            } catch (err) {
+              toggleNotification({
+                type: 'danger',
+                message: formatMessage({
+                  id: 'notification.error',
+                  defaultMessage: 'An error occurred',
+                }),
+              });
+
+              throw err;
+            }
+          }
+
+          dispatch({
+            type: 'GO_TO_CREATED_RELATION',
+            payload: { document: newRelation, shouldBypassConfirmation: true },
+          });
         }
       } else if (
         'error' in res &&
@@ -788,6 +941,7 @@ const UpdateAction: DocumentActionComponent = ({
   const { create, update, clone, isLoading } = useDocumentActions();
   const [{ rawQuery }] = useQueryParams();
   const onPreview = usePreviewContext('UpdateAction', (state) => state.onPreview, false);
+  const { getInitialFormValues } = useDoc();
 
   const isSubmitting = useForm('UpdateAction', ({ isSubmitting }) => isSubmitting);
   const modified = useForm('UpdateAction', ({ modified }) => modified);
@@ -795,9 +949,43 @@ const UpdateAction: DocumentActionComponent = ({
   const document = useForm('UpdateAction', ({ values }) => values);
   const validate = useForm('UpdateAction', (state) => state.validate);
   const setErrors = useForm('UpdateAction', (state) => state.setErrors);
-  const resetForm = useForm('PublishAction', ({ resetForm }) => resetForm);
+  const resetForm = useForm('UpdateAction', ({ resetForm }) => resetForm);
+
+  const dispatch = useRelationModal('UpdateAction', (state) => state.dispatch);
+
+  // need to discriminate if the update is coming from a relation modal or in the edit view
+  const relationContext = useRelationModal('UpdateAction', () => true, false);
+  const fieldToConnect = useRelationModal(
+    'UpdateAction',
+    (state) => state.state.fieldToConnect,
+    false
+  );
+  const fieldToConnectUID = useRelationModal(
+    'PublishAction',
+    (state) => state.state.fieldToConnectUID,
+    false
+  );
+  const documentHistory = useRelationModal(
+    'UpdateAction',
+    (state) => state.state.documentHistory,
+    false
+  );
+  const rootDocumentMeta = useRelationModal('UpdateAction', (state) => state.rootDocumentMeta);
+  const fromRelationModal = relationContext != undefined;
 
   const { currentDocumentMeta } = useDocumentContext('UpdateAction');
+  const [updateDocumentMutation] = useUpdateDocumentMutation();
+  const { _unstableFormatAPIError: formatAPIError } = useAPIErrorHandler();
+  const parentDocumentMetaToUpdate = documentHistory?.at(-2) ?? rootDocumentMeta;
+  const parentDocumentData = useDocument(
+    {
+      documentId: parentDocumentMetaToUpdate?.documentId,
+      model: parentDocumentMetaToUpdate?.model,
+      collectionType: parentDocumentMetaToUpdate?.collectionType,
+      params: parentDocumentMetaToUpdate?.params,
+    },
+    { skip: !parentDocumentMetaToUpdate }
+  );
 
   const handleUpdate = React.useCallback(async () => {
     setSubmitting(true);
@@ -875,13 +1063,79 @@ const UpdateAction: DocumentActionComponent = ({
         );
 
         if ('data' in res && collectionType !== SINGLE_TYPES) {
-          navigate(
-            {
-              pathname: `../${res.data.documentId}`,
-              search: rawQuery,
-            },
-            { replace: true, relative: 'path' }
-          );
+          if (fromRelationModal) {
+            const createdRelation = {
+              documentId: res.data.documentId,
+              collectionType,
+              model,
+              params: currentDocumentMeta.params,
+            };
+            /*
+             * Update, if needed, the parent relation with the newly published document.
+             * Check if in history we have the parent relation otherwise use the
+             * rootDocument
+             */
+            if (
+              fieldToConnect &&
+              documentHistory &&
+              (parentDocumentMetaToUpdate.documentId ||
+                parentDocumentMetaToUpdate.collectionType === SINGLE_TYPES)
+            ) {
+              const parentDataToUpdate =
+                parentDocumentMetaToUpdate.collectionType === SINGLE_TYPES
+                  ? getInitialFormValues()
+                  : parentDocumentData.getInitialFormValues();
+
+              const dataToUpdate = connectRelationToParent(
+                parentDataToUpdate,
+                fieldToConnect,
+                res.data,
+                fieldToConnectUID
+              );
+
+              try {
+                const updateRes = await updateDocumentMutation({
+                  collectionType: parentDocumentMetaToUpdate.collectionType,
+                  model: parentDocumentMetaToUpdate.model,
+                  documentId:
+                    parentDocumentMetaToUpdate.collectionType !== SINGLE_TYPES
+                      ? parentDocumentMetaToUpdate.documentId
+                      : undefined,
+                  params: parentDocumentMetaToUpdate.params,
+                  data: {
+                    ...dataToUpdate,
+                  },
+                });
+                if ('error' in updateRes) {
+                  toggleNotification({ type: 'danger', message: formatAPIError(updateRes.error) });
+                  return;
+                }
+              } catch (err) {
+                toggleNotification({
+                  type: 'danger',
+                  message: formatMessage({
+                    id: 'notification.error',
+                    defaultMessage: 'An error occurred',
+                  }),
+                });
+
+                throw err;
+              }
+            }
+
+            dispatch({
+              type: 'GO_TO_CREATED_RELATION',
+              payload: { document: createdRelation, shouldBypassConfirmation: true },
+            });
+          } else {
+            navigate(
+              {
+                pathname: `../${res.data.documentId}`,
+                search: rawQuery,
+              },
+              { replace: true, relative: 'path' }
+            );
+          }
         } else if (
           'error' in res &&
           isBaseQueryError(res.error) &&
@@ -897,26 +1151,36 @@ const UpdateAction: DocumentActionComponent = ({
       }
     }
   }, [
+    setSubmitting,
+    modified,
+    validate,
+    isCloning,
+    documentId,
+    collectionType,
+    toggleNotification,
+    formatMessage,
     clone,
+    model,
     cloneMatch?.params.origin,
-    collectionType,
-    create,
     currentDocumentMeta.params,
     document,
-    documentId,
-    formatMessage,
-    formatValidationErrors,
-    isCloning,
-    model,
-    modified,
     navigate,
     rawQuery,
-    resetForm,
     setErrors,
-    setSubmitting,
-    toggleNotification,
+    formatValidationErrors,
     update,
-    validate,
+    resetForm,
+    create,
+    fromRelationModal,
+    fieldToConnect,
+    documentHistory,
+    parentDocumentMetaToUpdate,
+    dispatch,
+    getInitialFormValues,
+    parentDocumentData,
+    fieldToConnectUID,
+    updateDocumentMutation,
+    formatAPIError,
     onPreview,
   ]);
 
diff --git a/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/Relations/RelationModal.tsx b/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/Relations/RelationModal.tsx
index d276ef743c2c..3d0e023e0de1 100644
--- a/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/Relations/RelationModal.tsx
+++ b/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/Relations/RelationModal.tsx
@@ -77,6 +77,8 @@ interface State {
     | DocumentMeta; // Open a specific document in the modal
   isModalOpen: boolean;
   hasUnsavedChanges: boolean;
+  fieldToConnect?: string;
+  fieldToConnectUID?: string;
 }
 
 type Action =
@@ -85,6 +87,8 @@ type Action =
       payload: {
         document: DocumentMeta;
         shouldBypassConfirmation: boolean;
+        fieldToConnect?: string;
+        fieldToConnectUID?: string;
       };
     }
   | {
@@ -94,6 +98,15 @@ type Action =
   | {
       type: 'GO_FULL_PAGE';
     }
+  | {
+      type: 'GO_TO_CREATED_RELATION';
+      payload: {
+        document: DocumentMeta;
+        shouldBypassConfirmation: boolean;
+        fieldToConnect?: string;
+        fieldToConnectUID?: string;
+      };
+    }
   | {
       type: 'CANCEL_CONFIRM_DIALOG';
     }
@@ -110,14 +123,27 @@ function reducer(state: State, action: Action): State {
   switch (action.type) {
     case 'GO_TO_RELATION':
       if (state.hasUnsavedChanges && !action.payload.shouldBypassConfirmation) {
-        return { ...state, confirmDialogIntent: action.payload.document };
+        return {
+          ...state,
+          confirmDialogIntent: action.payload.document,
+          fieldToConnect: action.payload.fieldToConnect,
+          fieldToConnectUID: action.payload.fieldToConnectUID,
+        };
       }
 
+      const lastItemDocumentHistory = state.documentHistory.at(-1);
+      const hasToResetDocumentHistory =
+        lastItemDocumentHistory && !lastItemDocumentHistory.documentId;
       return {
         ...state,
-        documentHistory: [...state.documentHistory, action.payload.document],
+        // Reset document history if the last item has documentId undefined
+        documentHistory: hasToResetDocumentHistory
+          ? [action.payload.document]
+          : [...state.documentHistory, action.payload.document],
         confirmDialogIntent: null,
         isModalOpen: true,
+        fieldToConnect: hasToResetDocumentHistory ? undefined : action.payload.fieldToConnect,
+        fieldToConnectUID: hasToResetDocumentHistory ? undefined : action.payload.fieldToConnectUID,
       };
     case 'GO_BACK':
       if (state.hasUnsavedChanges && !action.payload.shouldBypassConfirmation) {
@@ -141,6 +167,18 @@ function reducer(state: State, action: Action): State {
         isModalOpen: false,
         confirmDialogIntent: null,
       };
+    case 'GO_TO_CREATED_RELATION':
+      return {
+        ...state,
+        // Reset document history if the last item has documentId undefined
+        documentHistory: state.documentHistory
+          ? [...state.documentHistory.slice(0, -1), action.payload.document]
+          : [action.payload.document],
+        confirmDialogIntent: null,
+        isModalOpen: true,
+        fieldToConnect: undefined,
+        fieldToConnectUID: undefined,
+      };
     case 'CANCEL_CONFIRM_DIALOG':
       return {
         ...state,
@@ -207,6 +245,7 @@ const RootRelationRenderer = (props: RelationModalRendererProps) => {
     confirmDialogIntent: null,
     isModalOpen: false,
     hasUnsavedChanges: false,
+    fieldToConnect: undefined,
   });
 
   const rootDocument = useDoc();
@@ -225,7 +264,6 @@ const RootRelationRenderer = (props: RelationModalRendererProps) => {
   // TODO: check if we can remove the single type check
   const isSingleType = currentDocumentMeta.collectionType === SINGLE_TYPES;
   const isCreating = !currentDocumentMeta.documentId && !isSingleType;
-
   /**
    * There is no parent relation, so the relation modal doesn't exist. Create it and set up all the
    * pieces that will be used by potential child relations: the context, header, form, and footer.
@@ -274,6 +312,13 @@ const RelationModalRenderer = (props: RelationModalRendererProps) => {
 /* -------------------------------------------------------------------------------------------------
  * RelationModal
  * -----------------------------------------------------------------------------------------------*/
+const generateCreateUrl = (currentDocumentMeta: DocumentMeta) => {
+  return `/content-manager/${currentDocumentMeta.collectionType}/${currentDocumentMeta.model}/create${
+    currentDocumentMeta.params?.locale
+      ? `?plugins[i18n][locale]=${currentDocumentMeta.params.locale}`
+      : ''
+  }`;
+};
 
 const RelationModal = ({ children }: { children: React.ReactNode }) => {
   const { formatMessage } = useIntl();
@@ -337,7 +382,11 @@ const RelationModal = ({ children }: { children: React.ReactNode }) => {
                   type: 'GO_FULL_PAGE',
                 });
                 if (!state.hasUnsavedChanges) {
-                  navigate(getFullPageUrl(currentDocumentMeta));
+                  if (isCreating) {
+                    navigate(generateCreateUrl(currentDocumentMeta));
+                  } else {
+                    navigate(getFullPageUrl(currentDocumentMeta));
+                  }
                 }
               }}
               variant="tertiary"
@@ -352,8 +401,8 @@ const RelationModal = ({ children }: { children: React.ReactNode }) => {
         </Modal.Header>
         <Modal.Body>
           <FormContext
-            method="PUT"
-            initialValues={currentDocument.getInitialFormValues()}
+            method={isCreating ? 'POST' : 'PUT'}
+            initialValues={currentDocument.getInitialFormValues(isCreating)}
             validate={(values: Record<string, unknown>, options: Record<string, string>) => {
               const yupSchema = createYupSchema(
                 currentDocument.schema?.attributes,
@@ -393,6 +442,7 @@ const RelationModalBody = () => {
     'RelationModalForm',
     (state) => state.currentDocumentMeta
   );
+  const isCreating = useRelationModal('RelationModalForm', (state) => state.isCreating);
 
   /**
    * One-way sync the modified state from the form to the modal state.
@@ -430,7 +480,11 @@ const RelationModalBody = () => {
     if (isRootDocumentUrl) {
       handleCloseModal(true);
     } else {
-      navigate(fullPageUrl);
+      if (isCreating) {
+        navigate(generateCreateUrl(currentDocumentMeta));
+      } else {
+        navigate(fullPageUrl);
+      }
     }
   };
 
@@ -522,7 +576,9 @@ const RelationModalForm = () => {
   const documentLayoutResponse = useDocumentLayout(currentDocumentMeta.model);
   const plugins = useStrapiApp('RelationModalForm', (state) => state.plugins);
 
-  const initialValues = currentDocument.getInitialFormValues(isCreating);
+  const initialValues = isCreating
+    ? currentDocument.getInitialFormValues(isCreating)
+    : currentDocument.getInitialFormValues();
 
   const {
     permissions = [],
diff --git a/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/Relations/Relations.tsx b/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/Relations/Relations.tsx
index 00e8b1e4ec18..f07f2b625541 100644
--- a/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/Relations/Relations.tsx
+++ b/packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/Relations/Relations.tsx
@@ -22,7 +22,7 @@ import {
   FlexComponent,
   BoxComponent,
 } from '@strapi/design-system';
-import { Cross, Drag, ArrowClockwise, Link as LinkIcon } from '@strapi/icons';
+import { Cross, Drag, ArrowClockwise, Link as LinkIcon, Plus } from '@strapi/icons';
 import { generateNKeysBetween } from 'fractional-indexing';
 import pipe from 'lodash/fp/pipe';
 import { getEmptyImage } from 'react-dnd-html5-backend';
@@ -598,7 +598,12 @@ const RelationsInput = ({
     // @ts-expect-error – targetModel does exist on the attribute. But it's not typed.
     model: props.attribute.targetModel,
     documentId: '',
-  };
+    params: currentDocumentMeta.params,
+  } as DocumentMeta;
+
+  const { componentUID } = useComponent('RelationsField', ({ uid }) => ({
+    componentUID: uid,
+  }));
 
   return (
     <Field.Root error={field.error} hint={hint} name={name} required={required}>
@@ -620,9 +625,12 @@ const RelationsInput = ({
                 payload: {
                   document: relation,
                   shouldBypassConfirmation: false,
+                  fieldToConnect: name,
+                  fieldToConnectUID: componentUID,
                 },
               });
             }}
+            creatableStartIcon={<Plus fill="neutral500" />}
             name={name}
             autocomplete="list"
             placeholder={
diff --git a/tests/e2e/tests/content-manager/relations-on-the-fly/create-relation.spec.ts b/tests/e2e/tests/content-manager/relations-on-the-fly/create-relation.spec.ts
new file mode 100644
index 000000000000..fce3e22b0c33
--- /dev/null
+++ b/tests/e2e/tests/content-manager/relations-on-the-fly/create-relation.spec.ts
@@ -0,0 +1,281 @@
+import { test, expect } from '@playwright/test';
+import { login } from '../../../utils/login';
+import { resetDatabaseAndImportDataFromPath } from '../../../utils/dts-import';
+import { clickAndWait } from '../../../utils/shared';
+
+const CREATE_URL = /\/admin\/content-manager\/collection-types\/api::author.author\/create(\?.*)?/;
+
+test.describe('Relations on the fly - Create a Relation', () => {
+  test.beforeEach(async ({ page }) => {
+    await resetDatabaseAndImportDataFromPath('with-admin.tar');
+    await page.goto('/admin');
+    await login({ page });
+  });
+
+  test('I want to create a new relation, save the related document and check if the new relation is added to the parent document', async ({
+    page,
+  }) => {
+    // Step 1. Got to Article collection-type and open one article
+    await clickAndWait(page, page.getByRole('link', { name: 'Content Manager' }));
+    await clickAndWait(page, page.getByRole('link', { name: 'Article' }));
+    await clickAndWait(page, page.getByRole('gridcell', { name: 'West Ham post match analysis' }));
+
+    // Step 2. Open the relation modal
+    await page.getByRole('combobox', { name: 'authors' }).click();
+    await page.getByRole('option', { name: 'Create a relation' }).click();
+
+    // Step 3. Edit the form
+    await expect(page.getByRole('banner').getByText('Create a relation')).toBeVisible();
+    await expect(page.getByRole('heading', { name: 'Untitled' })).toBeVisible();
+    const name = page.getByRole('textbox', { name: 'name' });
+    await expect(name).toHaveValue('');
+    await name.fill('Mr. Plop');
+    await expect(name).toHaveValue('Mr. Plop');
+
+    // Step 4. Save the related document as draft
+    await clickAndWait(page, page.getByRole('button', { name: 'Save' }));
+    await expect(name).toHaveValue('Mr. Plop');
+    await expect(page.getByRole('status', { name: 'Draft' }).first()).toBeVisible();
+
+    // Step 5. Close the relation modal to see the updated relation on the root document
+    await page.getByRole('button', { name: 'Close modal' }).click();
+    await expect(page.getByRole('button', { name: 'Mr. Plop' })).toBeVisible();
+  });
+
+  test('I want to create a new relation, publish the related document and check if the new relation is added to the parent document', async ({
+    page,
+  }) => {
+    // Step 1. Got to Article collection-type and open one article
+    await clickAndWait(page, page.getByRole('link', { name: 'Content Manager' }));
+    await clickAndWait(page, page.getByRole('link', { name: 'Article' }));
+    await clickAndWait(page, page.getByRole('gridcell', { name: 'West Ham post match analysis' }));
+
+    // Step 2. Open the relation modal
+    await page.getByRole('combobox', { name: 'authors' }).click();
+    await page.getByRole('option', { name: 'Create a relation' }).click();
+
+    // Step 3. Edit the form
+    const name = page.getByRole('textbox', { name: 'name' });
+    await expect(name).toHaveValue('');
+    await name.fill('Mr. Fred Passo');
+    await expect(name).toHaveValue('Mr. Fred Passo');
+
+    // Step 4. Publish the related document
+    await clickAndWait(page, page.getByRole('button', { name: 'Publish' }));
+    await expect(name).toHaveValue('Mr. Fred Passo');
+    await expect(page.getByRole('status', { name: 'Published' }).first()).toBeVisible();
+
+    // Step 5. Close the relation modal to see the updated relation on the root document
+    await page.getByRole('button', { name: 'Close modal' }).click();
+    await expect(page.getByRole('button', { name: 'Mr. Fred Passo' })).toBeVisible();
+  });
+
+  test('I want to create a new relation in a modal and open it in full page', async ({ page }) => {
+    await clickAndWait(page, page.getByRole('link', { name: 'Content Manager' }));
+    await clickAndWait(page, page.getByRole('link', { name: 'Article' }));
+    await clickAndWait(page, page.getByRole('gridcell', { name: 'West Ham post match analysis' }));
+
+    await expect(page.getByRole('heading', { name: 'West Ham post match analysis' })).toBeVisible();
+
+    // Open the relation modal
+    await page.getByRole('combobox', { name: 'authors' }).click();
+    await page.getByRole('option', { name: 'Create a relation' }).click();
+    await expect(page.getByText('Create a relation')).toBeVisible();
+
+    // click on the full page icon
+    await clickAndWait(page, page.getByRole('button', { name: 'Go to entry' }));
+    await page.waitForURL(CREATE_URL);
+    await expect(page.getByRole('heading', { name: 'Create an entry' })).toBeVisible();
+  });
+
+  test('I want to click on a new relation in the create relation modal without saving the data in the form', async ({
+    page,
+  }) => {
+    await clickAndWait(page, page.getByRole('link', { name: 'Content Manager' }));
+    await clickAndWait(page, page.getByRole('link', { name: 'Article' }));
+    await clickAndWait(page, page.getByRole('gridcell', { name: 'West Ham post match analysis' }));
+
+    // Open the relation modal
+    await page.getByRole('combobox', { name: 'authors' }).click();
+    await page.getByRole('option', { name: 'Create a relation' }).click();
+
+    const name = page.getByRole('textbox', { name: 'name' });
+    await name.fill('Mr. Coach Hair');
+
+    // Open a new relation in the articles field
+    await clickAndWait(page, page.getByRole('combobox', { name: 'articles' }));
+    await page.getByRole('option', { name: 'Create a relation' }).click();
+
+    // Check the confirmation modal is shown and click confirm
+    await clickAndWait(page, page.getByRole('button', { name: 'Confirm' }));
+
+    // Check the nested relation modal is shown
+    await expect(page.getByRole('banner').getByText('Create a relation')).toBeVisible();
+    await expect(page.getByRole('heading', { name: 'Untitled' })).toBeVisible();
+    // and the back button is disabled
+    await expect(page.getByRole('button', { name: 'Back' })).toBeDisabled();
+  });
+
+  test('I want to add a new relation and edit it in the create relation modal without saving the data in the form', async ({
+    page,
+  }) => {
+    await clickAndWait(page, page.getByRole('link', { name: 'Content Manager' }));
+    await clickAndWait(page, page.getByRole('link', { name: 'Article' }));
+    await clickAndWait(page, page.getByRole('gridcell', { name: 'West Ham post match analysis' }));
+
+    // Open the relation modal
+    await page.getByRole('combobox', { name: 'authors' }).click();
+    await page.getByRole('option', { name: 'Create a relation' }).click();
+
+    const name = page.getByRole('textbox', { name: 'name' });
+    await name.fill('Mr. Coach Hair');
+
+    // Open a new relation in the articles field
+    await clickAndWait(page, page.getByRole('combobox', { name: 'articles' }));
+    await page.getByRole('option', { name: 'West Ham post match analysis' }).click();
+    await clickAndWait(page, page.getByRole('button', { name: 'West Ham post match analysis' }));
+
+    // Check the confirmation modal is shown and click confirm
+    await clickAndWait(page, page.getByRole('button', { name: 'Confirm' }));
+
+    // Check the nested relation modal is shown
+    await expect(page.getByRole('banner').getByText('Edit a relation')).toBeVisible();
+    await expect(page.getByRole('heading', { name: 'West Ham post match analysis' })).toBeVisible();
+    // and the back button is disabled
+    await expect(page.getByRole('button', { name: 'Back' })).toBeDisabled();
+  });
+
+  test('I want to click to close the relation modal without saving the data in the form', async ({
+    page,
+  }) => {
+    await clickAndWait(page, page.getByRole('link', { name: 'Content Manager' }));
+    await clickAndWait(page, page.getByRole('link', { name: 'Article' }));
+    await clickAndWait(page, page.getByRole('gridcell', { name: 'West Ham post match analysis' }));
+
+    // Open the relation modal
+    await page.getByRole('combobox', { name: 'authors' }).click();
+    await page.getByRole('option', { name: 'Create a relation' }).click();
+
+    const name = page.getByRole('textbox', { name: 'name' });
+    await name.fill('Mr. Coach Hair');
+
+    // Click the Close button
+    await clickAndWait(page, page.getByRole('button', { name: 'Close modal' }));
+
+    // Check the confirmation modal is shown and click confirm
+    await clickAndWait(page, page.getByRole('button', { name: 'Confirm' }));
+
+    await expect(page.getByRole('heading', { name: 'West Ham post match analysis' })).toBeVisible();
+  });
+
+  test('I want to click the button to open the full page without saving the data in the form', async ({
+    page,
+  }) => {
+    await clickAndWait(page, page.getByRole('link', { name: 'Content Manager' }));
+    await clickAndWait(page, page.getByRole('link', { name: 'Article' }));
+    await clickAndWait(page, page.getByRole('gridcell', { name: 'West Ham post match analysis' }));
+
+    // Open the relation modal
+    await page.getByRole('combobox', { name: 'authors' }).click();
+    await page.getByRole('option', { name: 'Create a relation' }).click();
+
+    const name = page.getByRole('textbox', { name: 'name' });
+    await name.fill('Mr. Coach Hair');
+
+    // click on the full page icon
+    await clickAndWait(page, page.getByRole('button', { name: 'Go to entry' }));
+
+    // Check the confirmation modal is shown and click confirm
+    await clickAndWait(page, page.getByRole('button', { name: 'Confirm' }));
+
+    await page.waitForURL(CREATE_URL);
+    await expect(page.getByRole('heading', { name: 'Create an entry' })).toBeVisible();
+  });
+
+  test('I want to click the back button to open the previous relation without saving the data in the form', async ({
+    page,
+  }) => {
+    await clickAndWait(page, page.getByRole('link', { name: 'Content Manager' }));
+    await clickAndWait(page, page.getByRole('link', { name: 'Article' }));
+    await clickAndWait(page, page.getByRole('gridcell', { name: 'West Ham post match analysis' }));
+
+    // Open the relation modal
+    await clickAndWait(page, page.getByRole('button', { name: 'Coach Beard' }));
+
+    // Open a nested relation
+    await clickAndWait(page, page.getByRole('button', { name: 'West Ham post match analysis' }));
+    // Open the relation modal
+    await page.getByRole('combobox', { name: 'authors' }).click();
+    await page.getByRole('option', { name: 'Create a relation' }).click();
+
+    // Change the name of the author
+    const name = page.getByRole('textbox', { name: 'name' });
+    await name.fill('Mr. Coach Hair');
+
+    // click on the Back button
+    await clickAndWait(page, page.getByRole('button', { name: 'Back' }));
+
+    // Check the confirmation modal is shown and click confirm
+    await clickAndWait(page, page.getByRole('button', { name: 'Confirm' }));
+
+    await expect(page.getByRole('heading', { name: 'West Ham post match analysis' })).toBeVisible();
+  });
+
+  test('I want to create a relation inside a component, and save', async ({ page }) => {
+    await clickAndWait(page, page.getByRole('link', { name: 'Content Manager' }));
+    // Step 1. Got to Shop single-type
+    await clickAndWait(page, page.getByRole('link', { name: 'Shop' }));
+    // Step 2. Choose the product carousel component and open its toggle
+    await page.getByRole('button', { name: 'Product carousel' }).click();
+    // Step 3. Select a product
+    await page.getByRole('combobox', { name: 'products' }).click();
+    // Step 4. Open the relation modal
+    await page.getByRole('option', { name: 'Create a relation' }).click();
+    await expect(page.getByText('Create a relation')).toBeVisible();
+    await expect(page.getByRole('heading', { name: 'Untitled' })).toBeVisible();
+
+    // Change the name of the article
+    const name = page.getByRole('textbox', { name: 'name' });
+    await name.fill('Nike Zoom Kd Iv Gold C800');
+
+    // Step 5. Save the related document as draft
+    await clickAndWait(page, page.getByRole('button', { name: 'Save' }));
+    await expect(name).toHaveValue('Nike Zoom Kd Iv Gold C800');
+    await expect(page.getByRole('status', { name: 'Draft' }).first()).toBeVisible();
+
+    // Step 6. Close the relation modal to see the updated relation on the root document
+    await page.getByRole('button', { name: 'Close modal' }).click();
+    await expect(page.getByRole('button', { name: 'Nike Zoom Kd Iv Gold C800' })).toBeVisible();
+  });
+
+  test('I want to create a relation inside a new component, and save', async ({ page }) => {
+    await clickAndWait(page, page.getByRole('link', { name: 'Content Manager' }));
+    // Step 1. Got to Shop single-type
+    await clickAndWait(page, page.getByRole('link', { name: 'Shop' }));
+    // Step 2. Add a new component
+    await clickAndWait(page, page.getByRole('button', { name: 'Add a component to content' }));
+    await clickAndWait(page, page.getByRole('button', { name: 'Product carousel', exact: true }));
+
+    // Step 3. Choose the new product carousel component and open its toggle
+    await page.getByRole('button', { name: 'Product carousel', exact: true }).click();
+    // Step 4. Select a product
+    await page.getByRole('combobox', { name: 'products' }).click();
+    // Step 5. Open the relation modal
+    await page.getByRole('option', { name: 'Create a relation' }).click();
+    await expect(page.getByText('Create a relation')).toBeVisible();
+    await expect(page.getByRole('heading', { name: 'Untitled' })).toBeVisible();
+
+    // Change the name of the article
+    const name = page.getByRole('textbox', { name: 'name' });
+    await name.fill('Nike Zoom Kd Iv Gold C800');
+
+    // Step 6. Save the related document as draft
+    await clickAndWait(page, page.getByRole('button', { name: 'Save' }));
+    await expect(name).toHaveValue('Nike Zoom Kd Iv Gold C800');
+    await expect(page.getByRole('status', { name: 'Draft' }).first()).toBeVisible();
+
+    // Step 7. Close the relation modal to see the updated relation on the root document
+    await page.getByRole('button', { name: 'Close modal' }).click();
+    await expect(page.getByRole('button', { name: 'Nike Zoom Kd Iv Gold C800' })).toBeVisible();
+  });
+});
diff --git a/tests/e2e/tests/content-manager/relations.spec.ts b/tests/e2e/tests/content-manager/relations-on-the-fly/edit-relation.spec.ts
similarity index 91%
rename from tests/e2e/tests/content-manager/relations.spec.ts
rename to tests/e2e/tests/content-manager/relations-on-the-fly/edit-relation.spec.ts
index 6447e99c9e43..fda18ace32b2 100644
--- a/tests/e2e/tests/content-manager/relations.spec.ts
+++ b/tests/e2e/tests/content-manager/relations-on-the-fly/edit-relation.spec.ts
@@ -1,12 +1,12 @@
 import { test, expect } from '@playwright/test';
-import { login } from '../../utils/login';
-import { resetDatabaseAndImportDataFromPath } from '../../utils/dts-import';
-import { clickAndWait } from '../../utils/shared';
+import { login } from '../../../utils/login';
+import { resetDatabaseAndImportDataFromPath } from '../../../utils/dts-import';
+import { clickAndWait } from '../../../utils/shared';
 
 const AUTHOR_EDIT_URL =
   /\/admin\/content-manager\/collection-types\/api::author.author\/(?!create)[^/]/;
 
-test.describe('Relations on the fly', () => {
+test.describe('Relations on the fly - Edit a Relation', () => {
   test.beforeEach(async ({ page }) => {
     await resetDatabaseAndImportDataFromPath('with-admin.tar');
     await page.goto('/admin');
@@ -253,25 +253,4 @@ test.describe('Relations on the fly', () => {
     await expect(page.getByRole('button', { name: 'Mr. Led Tasso' })).toBeVisible();
     await expect(page.getByText('Mr. Led TassoPublished')).toBeVisible();
   });
-
-  test('I want to crete a new relation', async ({ page }) => {
-    // Step 1. Got to Article collection-type and open one article
-    await clickAndWait(page, page.getByRole('link', { name: 'Content Manager' }));
-    await clickAndWait(page, page.getByRole('link', { name: 'Article' }));
-    await clickAndWait(page, page.getByRole('gridcell', { name: 'West Ham post match analysis' }));
-
-    // Step 2. Open the relation modal
-    await page.getByRole('combobox', { name: 'authors' }).click();
-    await page.getByRole('option', { name: 'Create a relation' }).click();
-
-    // Step 3. Edit the form
-    await expect(page.getByRole('banner').getByText('Create a relation')).toBeVisible();
-    await expect(page.getByRole('heading', { name: 'Untitled' })).toBeVisible();
-    const name = page.getByRole('textbox', { name: 'name' });
-    await expect(name).toHaveValue('');
-    await name.fill('Mr. Plop');
-    await expect(name).toHaveValue('Mr. Plop');
-
-    // TODO: save/publish
-  });
 });


Reviews:
user: login: remidej
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T08:19:32Z
user: login: simotae14
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T08:20:16Z
user: login: simotae14
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T08:20:51Z
user: login: markkaylor
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T09:00:26Z
user: login: simotae14
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T09:03:08Z
user: login: markkaylor
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T09:10:22Z
user: login: simotae14
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T09:19:29Z
user: login: remidej
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T09:24:19Z
user: login: remidej
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T09:25:01Z
user: login: simotae14
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T09:30:44Z
user: login: simotae14
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T09:30:54Z
user: login: markkaylor
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T09:50:50Z
user: login: remidej
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T10:18:12Z
user: login: markkaylor
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T13:14:39Z
user: login: markkaylor
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T13:15:20Z
user: login: markkaylor
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T13:22:05Z
user: login: markkaylor
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T13:25:32Z
user: login: markkaylor
body: Looks good to me, tested locally. I think you just have an e2e test failing
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T14:33:25Z

Review comments:
diff_hunk: @@ -734,7 +735,10 @@ const PublishAction: DocumentActionComponent = ({
           // Update, if needed, the parent relation with the newly published document
           // check if in history we have the parent relation otherwise use the rootDocument
           if (fieldToConnect && documentHistory && parentDocumentMetaToUpdate.documentId) {
-            const parentDataToUpdate = parentDocumentData.getInitialFormValues();
+            const parentDataToUpdate =
+              parentDocumentMetaToUpdate.collectionType === SINGLE_TYPES
+                ? getInitialFormValues()
+                : parentDocumentData.getInitialFormValues();
path: packages/core/content-manager/admin/src/pages/EditView/components/DocumentActions.tsx
user: login: remidej
body: I wonder why `parentDocumentData.getInitialFormValues` doesn't work for single types? Asking because ideally I'd prefer to avoid needing useDoc since it's not flexible
created_at: 2025-04-24T08:17:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -734,7 +735,10 @@ const PublishAction: DocumentActionComponent = ({
           // Update, if needed, the parent relation with the newly published document
           // check if in history we have the parent relation otherwise use the rootDocument
           if (fieldToConnect && documentHistory && parentDocumentMetaToUpdate.documentId) {
-            const parentDataToUpdate = parentDocumentData.getInitialFormValues();
+            const parentDataToUpdate =
+              parentDocumentMetaToUpdate.collectionType === SINGLE_TYPES
+                ? getInitialFormValues()
+                : parentDocumentData.getInitialFormValues();
path: packages/core/content-manager/admin/src/pages/EditView/components/DocumentActions.tsx
user: login: simotae14
body: I receive an empty object with single types
created_at: 2025-04-24T08:20:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -734,7 +735,10 @@ const PublishAction: DocumentActionComponent = ({
           // Update, if needed, the parent relation with the newly published document
           // check if in history we have the parent relation otherwise use the rootDocument
           if (fieldToConnect && documentHistory && parentDocumentMetaToUpdate.documentId) {
-            const parentDataToUpdate = parentDocumentData.getInitialFormValues();
+            const parentDataToUpdate =
+              parentDocumentMetaToUpdate.collectionType === SINGLE_TYPES
+                ? getInitialFormValues()
+                : parentDocumentData.getInitialFormValues();
path: packages/core/content-manager/admin/src/pages/EditView/components/DocumentActions.tsx
user: login: simotae14
body: and then I realized that I can't have a single type document in the modal but only in the Edit view page so I can use useDoc
created_at: 2025-04-24T08:20:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -11,7 +11,7 @@ interface DocumentMeta {
    * The equivalent of the ":id" url param value
    * i.e. gus5a67jcboa3o2zjnz39mb1
    */
-  documentId: string;
+  documentId?: string;
path: packages/core/content-manager/admin/src/hooks/useDocumentContext.ts
user: login: remidej
body: It's only optional when it's a single type right? There's an union opportunity to use a union to let TS know about that nuance. But we can move on without it
created_at: 2025-04-24T08:54:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -788,16 +892,46 @@ const UpdateAction: DocumentActionComponent = ({
   const { create, update, clone, isLoading } = useDocumentActions();
   const [{ rawQuery }] = useQueryParams();
   const onPreview = usePreviewContext('UpdateAction', (state) => state.onPreview, false);
+  const { getInitialFormValues } = useDoc();
 
   const isSubmitting = useForm('UpdateAction', ({ isSubmitting }) => isSubmitting);
   const modified = useForm('UpdateAction', ({ modified }) => modified);
   const setSubmitting = useForm('UpdateAction', ({ setSubmitting }) => setSubmitting);
   const document = useForm('UpdateAction', ({ values }) => values);
   const validate = useForm('UpdateAction', (state) => state.validate);
   const setErrors = useForm('UpdateAction', (state) => state.setErrors);
-  const resetForm = useForm('PublishAction', ({ resetForm }) => resetForm);
+  const resetForm = useForm('UpdateAction', ({ resetForm }) => resetForm);
+
+  const dispatch = useRelationModal('UpdateAction', (state) => state.dispatch);
+
+  // need to discriminate if the update is coming from a relation modal or in the edit view
+  const relationContext = useRelationModal('UpdateAction', () => true, false);
+  const fieldToConnect = useRelationModal(
+    'UpdateAction',
+    (state) => state.state.fieldToConnect,
+    false
+  );
+  const documentHistory = useRelationModal(
+    'UpdateAction',
+    (state) => state.state.documentHistory,
+    false
+  );
+  const rootDocumentMeta = useRelationModal('UpdateAction', (state) => state.rootDocumentMeta);
+  const fromRelationModal = relationContext != undefined;
 
   const { currentDocumentMeta } = useDocumentContext('UpdateAction');
+  const [updateDocumentMutation] = useUpdateDocumentMutation();
path: packages/core/content-manager/admin/src/pages/EditView/components/DocumentActions.tsx
user: login: markkaylor
body: Why do we do this directly instead of getting`update` from `useDocumentActions`? That's where we get the publish mutation as well on line 540. The isLoading destructured as well and that includes loading state for publish || update. Not sure if that is beneficial or not but just fyi
created_at: 2025-04-24T08:55:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -788,16 +892,46 @@ const UpdateAction: DocumentActionComponent = ({
   const { create, update, clone, isLoading } = useDocumentActions();
   const [{ rawQuery }] = useQueryParams();
   const onPreview = usePreviewContext('UpdateAction', (state) => state.onPreview, false);
+  const { getInitialFormValues } = useDoc();
 
   const isSubmitting = useForm('UpdateAction', ({ isSubmitting }) => isSubmitting);
   const modified = useForm('UpdateAction', ({ modified }) => modified);
   const setSubmitting = useForm('UpdateAction', ({ setSubmitting }) => setSubmitting);
   const document = useForm('UpdateAction', ({ values }) => values);
   const validate = useForm('UpdateAction', (state) => state.validate);
   const setErrors = useForm('UpdateAction', (state) => state.setErrors);
-  const resetForm = useForm('PublishAction', ({ resetForm }) => resetForm);
+  const resetForm = useForm('UpdateAction', ({ resetForm }) => resetForm);
+
+  const dispatch = useRelationModal('UpdateAction', (state) => state.dispatch);
+
+  // need to discriminate if the update is coming from a relation modal or in the edit view
+  const relationContext = useRelationModal('UpdateAction', () => true, false);
+  const fieldToConnect = useRelationModal(
+    'UpdateAction',
+    (state) => state.state.fieldToConnect,
+    false
+  );
+  const documentHistory = useRelationModal(
+    'UpdateAction',
+    (state) => state.state.documentHistory,
+    false
+  );
+  const rootDocumentMeta = useRelationModal('UpdateAction', (state) => state.rootDocumentMeta);
+  const fromRelationModal = relationContext != undefined;
 
   const { currentDocumentMeta } = useDocumentContext('UpdateAction');
+  const [updateDocumentMutation] = useUpdateDocumentMutation();
path: packages/core/content-manager/admin/src/pages/EditView/components/DocumentActions.tsx
user: login: simotae14
body: We did it in this way with Remi because otherwise we would have Two notification success messages for the create and the update 
created_at: 2025-04-24T09:03:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -788,16 +892,46 @@ const UpdateAction: DocumentActionComponent = ({
   const { create, update, clone, isLoading } = useDocumentActions();
   const [{ rawQuery }] = useQueryParams();
   const onPreview = usePreviewContext('UpdateAction', (state) => state.onPreview, false);
+  const { getInitialFormValues } = useDoc();
 
   const isSubmitting = useForm('UpdateAction', ({ isSubmitting }) => isSubmitting);
   const modified = useForm('UpdateAction', ({ modified }) => modified);
   const setSubmitting = useForm('UpdateAction', ({ setSubmitting }) => setSubmitting);
   const document = useForm('UpdateAction', ({ values }) => values);
   const validate = useForm('UpdateAction', (state) => state.validate);
   const setErrors = useForm('UpdateAction', (state) => state.setErrors);
-  const resetForm = useForm('PublishAction', ({ resetForm }) => resetForm);
+  const resetForm = useForm('UpdateAction', ({ resetForm }) => resetForm);
+
+  const dispatch = useRelationModal('UpdateAction', (state) => state.dispatch);
+
+  // need to discriminate if the update is coming from a relation modal or in the edit view
+  const relationContext = useRelationModal('UpdateAction', () => true, false);
+  const fieldToConnect = useRelationModal(
+    'UpdateAction',
+    (state) => state.state.fieldToConnect,
+    false
+  );
+  const documentHistory = useRelationModal(
+    'UpdateAction',
+    (state) => state.state.documentHistory,
+    false
+  );
+  const rootDocumentMeta = useRelationModal('UpdateAction', (state) => state.rootDocumentMeta);
+  const fromRelationModal = relationContext != undefined;
 
   const { currentDocumentMeta } = useDocumentContext('UpdateAction');
+  const [updateDocumentMutation] = useUpdateDocumentMutation();
path: packages/core/content-manager/admin/src/pages/EditView/components/DocumentActions.tsx
user: login: markkaylor
body: ok what about the loading part, should the loading state returned in PublishAction consider the loading state of this mutation? See line 818
created_at: 2025-04-24T09:10:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -352,8 +394,12 @@ const RelationModal = ({ children }: { children: React.ReactNode }) => {
         </Modal.Header>
         <Modal.Body>
           <FormContext
-            method="PUT"
-            initialValues={currentDocument.getInitialFormValues()}
+            method={isCreating ? 'POST' : 'PUT'}
+            initialValues={
+              isCreating
+                ? currentDocument.getInitialFormValues(isCreating)
+                : currentDocument.getInitialFormValues()
path: packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/Relations/RelationModal.tsx
user: login: remidej
body: Why not always call getInitialFormValues with isCreating? I assume it can handle isCreating being false
created_at: 2025-04-24T09:16:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -522,7 +579,9 @@ const RelationModalForm = () => {
   const documentLayoutResponse = useDocumentLayout(currentDocumentMeta.model);
   const plugins = useStrapiApp('RelationModalForm', (state) => state.plugins);
 
-  const initialValues = currentDocument.getInitialFormValues(isCreating);
+  const initialValues = isCreating
+    ? currentDocument.getInitialFormValues(isCreating)
+    : currentDocument.getInitialFormValues();
path: packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/Relations/RelationModal.tsx
user: login: remidej
body: same question
created_at: 2025-04-24T09:17:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -788,16 +892,46 @@ const UpdateAction: DocumentActionComponent = ({
   const { create, update, clone, isLoading } = useDocumentActions();
   const [{ rawQuery }] = useQueryParams();
   const onPreview = usePreviewContext('UpdateAction', (state) => state.onPreview, false);
+  const { getInitialFormValues } = useDoc();
 
   const isSubmitting = useForm('UpdateAction', ({ isSubmitting }) => isSubmitting);
   const modified = useForm('UpdateAction', ({ modified }) => modified);
   const setSubmitting = useForm('UpdateAction', ({ setSubmitting }) => setSubmitting);
   const document = useForm('UpdateAction', ({ values }) => values);
   const validate = useForm('UpdateAction', (state) => state.validate);
   const setErrors = useForm('UpdateAction', (state) => state.setErrors);
-  const resetForm = useForm('PublishAction', ({ resetForm }) => resetForm);
+  const resetForm = useForm('UpdateAction', ({ resetForm }) => resetForm);
+
+  const dispatch = useRelationModal('UpdateAction', (state) => state.dispatch);
+
+  // need to discriminate if the update is coming from a relation modal or in the edit view
+  const relationContext = useRelationModal('UpdateAction', () => true, false);
+  const fieldToConnect = useRelationModal(
+    'UpdateAction',
+    (state) => state.state.fieldToConnect,
+    false
+  );
+  const documentHistory = useRelationModal(
+    'UpdateAction',
+    (state) => state.state.documentHistory,
+    false
+  );
+  const rootDocumentMeta = useRelationModal('UpdateAction', (state) => state.rootDocumentMeta);
+  const fromRelationModal = relationContext != undefined;
 
   const { currentDocumentMeta } = useDocumentContext('UpdateAction');
+  const [updateDocumentMutation] = useUpdateDocumentMutation();
path: packages/core/content-manager/admin/src/pages/EditView/components/DocumentActions.tsx
user: login: simotae14
body: for sure we have an issue with loading and we can open a GH issue for that, and refactor a little bit this code, before merging the future branch
created_at: 2025-04-24T09:19:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -788,16 +892,46 @@ const UpdateAction: DocumentActionComponent = ({
   const { create, update, clone, isLoading } = useDocumentActions();
   const [{ rawQuery }] = useQueryParams();
   const onPreview = usePreviewContext('UpdateAction', (state) => state.onPreview, false);
+  const { getInitialFormValues } = useDoc();
 
   const isSubmitting = useForm('UpdateAction', ({ isSubmitting }) => isSubmitting);
   const modified = useForm('UpdateAction', ({ modified }) => modified);
   const setSubmitting = useForm('UpdateAction', ({ setSubmitting }) => setSubmitting);
   const document = useForm('UpdateAction', ({ values }) => values);
   const validate = useForm('UpdateAction', (state) => state.validate);
   const setErrors = useForm('UpdateAction', (state) => state.setErrors);
-  const resetForm = useForm('PublishAction', ({ resetForm }) => resetForm);
+  const resetForm = useForm('UpdateAction', ({ resetForm }) => resetForm);
+
+  const dispatch = useRelationModal('UpdateAction', (state) => state.dispatch);
+
+  // need to discriminate if the update is coming from a relation modal or in the edit view
+  const relationContext = useRelationModal('UpdateAction', () => true, false);
+  const fieldToConnect = useRelationModal(
+    'UpdateAction',
+    (state) => state.state.fieldToConnect,
+    false
+  );
+  const documentHistory = useRelationModal(
+    'UpdateAction',
+    (state) => state.state.documentHistory,
+    false
+  );
+  const rootDocumentMeta = useRelationModal('UpdateAction', (state) => state.rootDocumentMeta);
+  const fromRelationModal = relationContext != undefined;
 
   const { currentDocumentMeta } = useDocumentContext('UpdateAction');
+  const [updateDocumentMutation] = useUpdateDocumentMutation();
path: packages/core/content-manager/admin/src/pages/EditView/components/DocumentActions.tsx
user: login: remidej
body: >We did it in this way with Remi because otherwise we would have Two notification success messages for the create and the update

also otherwise it would send 2 tracking events for the same action
created_at: 2025-04-24T09:25:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -352,8 +394,12 @@ const RelationModal = ({ children }: { children: React.ReactNode }) => {
         </Modal.Header>
         <Modal.Body>
           <FormContext
-            method="PUT"
-            initialValues={currentDocument.getInitialFormValues()}
+            method={isCreating ? 'POST' : 'PUT'}
+            initialValues={
+              isCreating
+                ? currentDocument.getInitialFormValues(isCreating)
+                : currentDocument.getInitialFormValues()
path: packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/Relations/RelationModal.tsx
user: login: simotae14
body: I tried and I received an empty object
created_at: 2025-04-24T09:30:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -522,7 +579,9 @@ const RelationModalForm = () => {
   const documentLayoutResponse = useDocumentLayout(currentDocumentMeta.model);
   const plugins = useStrapiApp('RelationModalForm', (state) => state.plugins);
 
-  const initialValues = currentDocument.getInitialFormValues(isCreating);
+  const initialValues = isCreating
+    ? currentDocument.getInitialFormValues(isCreating)
+    : currentDocument.getInitialFormValues();
path: packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/Relations/RelationModal.tsx
user: login: simotae14
body: same as before
created_at: 2025-04-24T09:30:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -788,16 +892,46 @@ const UpdateAction: DocumentActionComponent = ({
   const { create, update, clone, isLoading } = useDocumentActions();
   const [{ rawQuery }] = useQueryParams();
   const onPreview = usePreviewContext('UpdateAction', (state) => state.onPreview, false);
+  const { getInitialFormValues } = useDoc();
 
   const isSubmitting = useForm('UpdateAction', ({ isSubmitting }) => isSubmitting);
   const modified = useForm('UpdateAction', ({ modified }) => modified);
   const setSubmitting = useForm('UpdateAction', ({ setSubmitting }) => setSubmitting);
   const document = useForm('UpdateAction', ({ values }) => values);
   const validate = useForm('UpdateAction', (state) => state.validate);
   const setErrors = useForm('UpdateAction', (state) => state.setErrors);
-  const resetForm = useForm('PublishAction', ({ resetForm }) => resetForm);
+  const resetForm = useForm('UpdateAction', ({ resetForm }) => resetForm);
+
+  const dispatch = useRelationModal('UpdateAction', (state) => state.dispatch);
+
+  // need to discriminate if the update is coming from a relation modal or in the edit view
+  const relationContext = useRelationModal('UpdateAction', () => true, false);
+  const fieldToConnect = useRelationModal(
+    'UpdateAction',
+    (state) => state.state.fieldToConnect,
+    false
+  );
+  const documentHistory = useRelationModal(
+    'UpdateAction',
+    (state) => state.state.documentHistory,
+    false
+  );
+  const rootDocumentMeta = useRelationModal('UpdateAction', (state) => state.rootDocumentMeta);
+  const fromRelationModal = relationContext != undefined;
 
   const { currentDocumentMeta } = useDocumentContext('UpdateAction');
+  const [updateDocumentMutation] = useUpdateDocumentMutation();
path: packages/core/content-manager/admin/src/pages/EditView/components/DocumentActions.tsx
user: login: markkaylor
body: I get that we have issues to resolve around loading in the relation modal. Just want to clarify my point that here you are introducing a loading state that is not handled. It is not an existing issue. For example the publish button in the relation modal is only going to show a loader for the publish action, even though an update to the parent document could still be in progress after the publish is finished. 

Unless I'm missing something, and I'll leave it up to you if you want to handle it in another PR, but I would recommend going ahead and doing this in the relevant places:

```
const [updateDocumentMutation, { isLoading: isLoadingUpdate }] = useUpdateDocumentMutation();
// ....
return {
  loading: isLoading || isLoadingUpdate
}
```


created_at: 2025-04-24T09:50:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -352,8 +394,12 @@ const RelationModal = ({ children }: { children: React.ReactNode }) => {
         </Modal.Header>
         <Modal.Body>
           <FormContext
-            method="PUT"
-            initialValues={currentDocument.getInitialFormValues()}
+            method={isCreating ? 'POST' : 'PUT'}
+            initialValues={
+              isCreating
+                ? currentDocument.getInitialFormValues(isCreating)
+                : currentDocument.getInitialFormValues()
path: packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/Relations/RelationModal.tsx
user: login: remidej
body: Why not tweak its implementation to make it work then? Same thing to add single type support to it actually.

I'd rather fix small issues like this at the root when possible instead of doing unintuitive work arounds
created_at: 2025-04-24T10:18:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -683,11 +725,108 @@ const PublishAction: DocumentActionComponent = ({
         /**
          * TODO: refactor the router so we can just do `../${res.data.documentId}` instead of this.
          */
-        if (idToPublish === 'create') {
+        if (idToPublish === 'create' && !fromRelationModal) {
           navigate({
             pathname: `../${collectionType}/${model}/${res.data.documentId}`,
             search: rawQuery,
           });
+        } else if (fromRelationModal) {
+          const newRelation = {
+            documentId: res.data.documentId,
+            collectionType,
+            model,
+            params: currentDocumentMeta.params,
+          };
+
+          /*
+           * Update, if needed, the parent relation with the newly published document.
+           * Check if in history we have the parent relation otherwise use the
+           * rootDocument
+           */
+          if (
+            fieldToConnect &&
+            documentHistory &&
+            (parentDocumentMetaToUpdate.documentId ||
+              parentDocumentMetaToUpdate.collectionType === SINGLE_TYPES)
+          ) {
+            const parentDataToUpdate =
+              parentDocumentMetaToUpdate.collectionType === SINGLE_TYPES
+                ? getInitialFormValues()
+                : parentDocumentData.getInitialFormValues();
+            const metaDocumentToUpdate = documentHistory.at(-2) ?? rootDocumentMeta;
+
+            /*
+             * Check if the fieldToConnect is already present in the parentDataToUpdate.
+             * This happens in particular when in the parentDocument you have created
+             * a new component without saving.
+             */
+            const isFieldPresent = !!get(parentDataToUpdate, fieldToConnect);
+            const fieldToConnectPath = isFieldPresent
+              ? fieldToConnect
+              : // Compute the path to the parent object
+                fieldToConnect.split('.').slice(0, -1).join('.');
+            const fieldToConnectValue = isFieldPresent
+              ? {
+                  connect: [
+                    {
+                      id: res.data.documentId,
+                      documentId: res.data.documentId,
+                      locale: res.data.locale,
+                    },
+                  ],
+                }
+              : {
+                  [fieldToConnect.split('.').pop()!]: {
+                    connect: [
+                      {
+                        id: res.data.documentId,
+                        documentId: res.data.documentId,
+                        locale: res.data.locale,
+                      },
+                    ],
+                    disconnect: [],
+                  },
+                  __component: fieldToConnectUID,
+                };
+            // In case the object was not present you need to pass the componentUID of the parent document
path: packages/core/content-manager/admin/src/pages/EditView/components/DocumentActions.tsx
user: login: markkaylor
body: I think this comment might be in the wrong place now...Maybe it's supposed to be above line 789?
created_at: 2025-04-24T13:14:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -683,11 +725,108 @@ const PublishAction: DocumentActionComponent = ({
         /**
          * TODO: refactor the router so we can just do `../${res.data.documentId}` instead of this.
          */
-        if (idToPublish === 'create') {
+        if (idToPublish === 'create' && !fromRelationModal) {
           navigate({
             pathname: `../${collectionType}/${model}/${res.data.documentId}`,
             search: rawQuery,
           });
+        } else if (fromRelationModal) {
+          const newRelation = {
+            documentId: res.data.documentId,
+            collectionType,
+            model,
+            params: currentDocumentMeta.params,
+          };
+
+          /*
+           * Update, if needed, the parent relation with the newly published document.
+           * Check if in history we have the parent relation otherwise use the
+           * rootDocument
+           */
+          if (
+            fieldToConnect &&
+            documentHistory &&
+            (parentDocumentMetaToUpdate.documentId ||
+              parentDocumentMetaToUpdate.collectionType === SINGLE_TYPES)
+          ) {
+            const parentDataToUpdate =
+              parentDocumentMetaToUpdate.collectionType === SINGLE_TYPES
+                ? getInitialFormValues()
+                : parentDocumentData.getInitialFormValues();
+            const metaDocumentToUpdate = documentHistory.at(-2) ?? rootDocumentMeta;
+
+            /*
+             * Check if the fieldToConnect is already present in the parentDataToUpdate.
+             * This happens in particular when in the parentDocument you have created
+             * a new component without saving.
+             */
+            const isFieldPresent = !!get(parentDataToUpdate, fieldToConnect);
+            const fieldToConnectPath = isFieldPresent
+              ? fieldToConnect
+              : // Compute the path to the parent object
+                fieldToConnect.split('.').slice(0, -1).join('.');
+            const fieldToConnectValue = isFieldPresent
+              ? {
+                  connect: [
+                    {
+                      id: res.data.documentId,
+                      documentId: res.data.documentId,
+                      locale: res.data.locale,
+                    },
+                  ],
+                }
+              : {
+                  [fieldToConnect.split('.').pop()!]: {
+                    connect: [
+                      {
+                        id: res.data.documentId,
+                        documentId: res.data.documentId,
+                        locale: res.data.locale,
+                      },
+                    ],
+                    disconnect: [],
+                  },
+                  __component: fieldToConnectUID,
+                };
+            // In case the object was not present you need to pass the componentUID of the parent document
+            const objectToConnect = set({}, fieldToConnectPath, fieldToConnectValue);
+            const dataToUpdate = merge(parentDataToUpdate, objectToConnect);
+
+            try {
+              const updateRes = await updateDocumentMutation({
+                collectionType: metaDocumentToUpdate.collectionType,
+                model: metaDocumentToUpdate.model,
+                documentId:
+                  metaDocumentToUpdate.collectionType !== SINGLE_TYPES
+                    ? metaDocumentToUpdate.documentId
+                    : undefined,
+                params: metaDocumentToUpdate.params,
+                data: {
+                  ...dataToUpdate,
+                },
path: packages/core/content-manager/admin/src/pages/EditView/components/DocumentActions.tsx
user: login: markkaylor
body: I don't think the spread is necessary:

```suggestion
                data: dataToUpdate,
```
created_at: 2025-04-24T13:15:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -875,13 +1049,109 @@ const UpdateAction: DocumentActionComponent = ({
         );
 
         if ('data' in res && collectionType !== SINGLE_TYPES) {
-          navigate(
-            {
-              pathname: `../${res.data.documentId}`,
-              search: rawQuery,
-            },
-            { replace: true, relative: 'path' }
-          );
+          if (fromRelationModal) {
+            const createdRelation = {
+              documentId: res.data.documentId,
+              collectionType,
+              model,
+              params: currentDocumentMeta.params,
+            };
+            /*
+             * Update, if needed, the parent relation with the newly published document.
+             * Check if in history we have the parent relation otherwise use the
+             * rootDocument
+             */
+            if (
+              fieldToConnect &&
+              documentHistory &&
+              (parentDocumentMetaToUpdate.documentId ||
+                parentDocumentMetaToUpdate.collectionType === SINGLE_TYPES)
+            ) {
+              const parentDataToUpdate =
+                parentDocumentMetaToUpdate.collectionType === SINGLE_TYPES
+                  ? getInitialFormValues()
+                  : parentDocumentData.getInitialFormValues();
+
+              /*
+               * Check if the fieldToConnect is already present in the parentDataToUpdate.
+               * This happens in particular when in the parentDocument you have created
+               * a new component without saving.
+               */
+              const isFieldPresent = !!get(parentDataToUpdate, fieldToConnect);
+              const fieldToConnectPath = isFieldPresent
+                ? fieldToConnect
+                : // Compute the path to the parent object
+                  fieldToConnect.split('.').slice(0, -1).join('.');
+              const fieldToConnectValue = isFieldPresent
+                ? {
+                    connect: [
+                      {
+                        id: res.data.documentId,
+                        documentId: res.data.documentId,
+                        locale: res.data.locale,
+                      },
+                    ],
+                  }
+                : {
+                    [fieldToConnect.split('.').pop()!]: {
+                      connect: [
+                        {
+                          id: res.data.documentId,
+                          documentId: res.data.documentId,
+                          locale: res.data.locale,
+                        },
+                      ],
+                      disconnect: [],
+                    },
+                    __component: fieldToConnectUID,
+                  };
+              // In case the object was not present you need to pass the componentUID of the parent document
+              const objectToConnect = set({}, fieldToConnectPath, fieldToConnectValue);
+              const dataToUpdate = merge(parentDataToUpdate, objectToConnect);
+
path: packages/core/content-manager/admin/src/pages/EditView/components/DocumentActions.tsx
user: login: markkaylor
body: This same logic is used in two places. I think it should be a function with a signature like:

```tsx
connectRelationToParent(parentDataToUpdate, fieldToConnect, { data: res.data })
```
created_at: 2025-04-24T13:22:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -337,7 +375,17 @@ const RelationModal = ({ children }: { children: React.ReactNode }) => {
                   type: 'GO_FULL_PAGE',
                 });
                 if (!state.hasUnsavedChanges) {
-                  navigate(getFullPageUrl(currentDocumentMeta));
+                  if (isCreating) {
+                    navigate(
+                      `/content-manager/${currentDocumentMeta.collectionType}/${currentDocumentMeta.model}/create${
+                        currentDocumentMeta.params?.locale
+                          ? `?plugins[i18n][locale]=${currentDocumentMeta.params.locale}`
+                          : ''
+                      }`
path: packages/core/content-manager/admin/src/pages/EditView/components/FormInputs/Relations/RelationModal.tsx
user: login: markkaylor
body: It looks like you are computing this same value on line 484 could it be a function?
created_at: 2025-04-24T13:25:32Z
author_association: CONTRIBUTOR

PR comments:
user: login: vercel[bot]
created_at: 2025-04-16T14:42:44Z
body: [vc]: #4UMzyWZ1qWA7RLVKODP9FkjjVPsp6bI/znCFHT/C/zw=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJjb250cmlidXRvci1kb2NzIiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL3N0cmFwaWpzL2NvbnRyaWJ1dG9yLWRvY3MvTGRHd2NEMWJ3R3RoRmNSOHdXZFhqYXVzYzN4WSIsInByZXZpZXdVcmwiOiJjb250cmlidXRvci1kb2NzLWdpdC1yZWxhdGlvbnMtb24tdGhlLWZseS1jcmVhdC0xZWY1M2Itc3RyYXBpanMudmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJERVBMT1lFRCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJjb250cmlidXRvci1kb2NzLWdpdC1yZWxhdGlvbnMtb24tdGhlLWZseS1jcmVhdC0xZWY1M2Itc3RyYXBpanMudmVyY2VsLmFwcCJ9LCJyb290RGlyZWN0b3J5IjpudWxsfV19
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **contributor-docs** | ✅ Ready ([Inspect](https://vercel.com/strapijs/contributor-docs/LdGwcD1bwGthFcR8wWdXjausc3xY)) | [Visit Preview](https://contributor-docs-git-relations-on-the-fly-creat-1ef53b-strapijs.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/contributor-docs-git-relations-on-the-fly-creat-1ef53b-strapijs.vercel.app?via=pr-comment-feedback-link) | Apr 24, 2025 2:09pm |


author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-16T14:52:20Z
body: **Size Change:** -811 B (-0.01%) 

**Total Size:** 11.6 MB

| Filename | Size | Change |  |
| :--- | :---: | :---: | :---: |
| `examples/getstarted/build/ComponentConfigurationPage-***********` | 1.5 kB | -7 B (-0.46%) |  |
| `examples/getstarted/build/CreateView-***********` | 301 B | -62 B (-17.08%) | 👏 |
| `examples/getstarted/build/EditConfigurationPage-***********` | 1.06 kB | -5 B (-0.47%) |  |
| `examples/getstarted/build/EditViewPage-***********` | 5.02 kB | -9 B (-0.18%) |  |
| `examples/getstarted/build/HomePage-***********` | 248 B | -2.62 kB (-91.35%) | 🏆 |
| `examples/getstarted/build/index-***********` | 588 B | +24 B (+4.26%) |  |
| `examples/getstarted/build/ListPage-***********` | 3.41 kB | +507 B (+17.48%) | ⚠️ |
| `examples/getstarted/build/ListView-***********` | 1.38 kB | +17 B (+1.25%) |  |
| `examples/getstarted/build/PurchaseContentReleases-***********` | 138 kB | +8 B (+0.01%) |  |
| `examples/getstarted/build/ReleasesSettingsPage-***********` | 1.34 kB | -5 B (-0.37%) |  |
| `examples/getstarted/build/Settings-***********` | 1.61 kB | -212 B (-11.66%) | 👏 |
| `examples/getstarted/build/SettingsPage-***********` | 3.23 kB | +1.68 kB (+108.6%) | 🆘 |
| `examples/getstarted/build/strapi-***********` | 1.74 MB | -75 B (0%) |  |

<details><summary>ℹ️ <strong>View Unchanged</strong></summary>

| Filename | Size | Change |
| :--- | :---: | :---: |
| `examples/getstarted/build/_arrayIncludesWith-***********` | 123 B | 0 B |
| `examples/getstarted/build/_baseEach-***********` | 220 B | +1 B (+0.46%) |
| `examples/getstarted/build/_baseMap-***********` | 188 B | -3 B (-1.57%) |
| `examples/getstarted/build/AdminSeatInfo-***********` | 833 B | 0 B |
| `examples/getstarted/build/apiTokens-***********` | 442 B | -3 B (-0.67%) |
| `examples/getstarted/build/App-***********` | 8.39 kB | -2 B (-0.02%) |
| `examples/getstarted/build/ApplicationInfoPage-***********` | 4.29 kB | +1 B (+0.02%) |
| `examples/getstarted/build/ar.json-***********` | 16.4 kB | 0 B |
| `examples/getstarted/build/AuthenticatedLayout-***********` | 15.6 kB | -3 B (-0.02%) |
| `examples/getstarted/build/AuthResponse-***********` | 417 B | -4 B (-0.95%) |
| `examples/getstarted/build/ca.json-***********` | 3.76 kB | 0 B |
| `examples/getstarted/build/CardDragPreview-***********` | 566 B | +1 B (+0.18%) |
| `examples/getstarted/build/ColorPickerInput-***********` | 4.23 kB | 0 B |
| `examples/getstarted/build/ConfigureTheView-***********` | 1.53 kB | -2 B (-0.13%) |
| `examples/getstarted/build/constants-***********` | 72 B | 0 B |
| `examples/getstarted/build/CreateActionEE-***********` | 500 B | -1 B (-0.2%) |
| `examples/getstarted/build/CreatePage-***********` | 252 B | 0 B |
| `examples/getstarted/build/cs.json-***********` | 338 B | 0 B |
| `examples/getstarted/build/de.json-***********` | 1.49 kB | 0 B |
| `examples/getstarted/build/dk.json-***********` | 7.61 kB | 0 B |
| `examples/getstarted/build/dummy-preview-***********` | 948 B | 0 B |
| `examples/getstarted/build/EditPage-***********` | 4.68 kB | -1 B (-0.02%) |
| `examples/getstarted/build/EditView-***********` | 2.03 kB | -4 B (-0.2%) |
| `examples/getstarted/build/en-***********` | 74 B | 0 B |
| `examples/getstarted/build/en-GB-***********` | 12.9 kB | 0 B |
| `examples/getstarted/build/en.json-***********` | 381 B | 0 B |
| `examples/getstarted/build/es-***********` | 74 B | 0 B |
| `examples/getstarted/build/es.json-***********` | 3.61 kB | 0 B |
| `examples/getstarted/build/eu.json-***********` | 3.7 kB | 0 B |
| `examples/getstarted/build/EventsTable-***********` | 389 B | -2 B (-0.51%) |
| `examples/getstarted/build/FieldTypeIcon-***********` | 453 B | 0 B |
| `examples/getstarted/build/Form-***********` | 4.53 kB | -1 B (-0.02%) |
| `examples/getstarted/build/fr.json-***********` | 794 B | 0 B |
| `examples/getstarted/build/getTrad-***********` | 381 B | -3 B (-0.78%) |
| `examples/getstarted/build/groupBy-***********` | 368 B | -3 B (-0.81%) |
| `examples/getstarted/build/gu.json-***********` | 9.57 kB | 0 B |
| `examples/getstarted/build/he.json-***********` | 1.63 kB | 0 B |
| `examples/getstarted/build/hi.json-***********` | 11.6 kB | 0 B |
| `examples/getstarted/build/History-***********` | 6.12 kB | -3 B (-0.05%) |
| `examples/getstarted/build/hooks-***********` | 82 B | 0 B |
| `examples/getstarted/build/hu.json-***********` | 3.98 kB | 0 B |
| `examples/getstarted/build/id-***********` | 7.5 kB | -1 B (-0.01%) |
| `examples/getstarted/build/id.json-***********` | 3.21 kB | 0 B |
| `examples/getstarted/build/index.html` | 592 B | -2 B (-0.34%) |
| `examples/getstarted/build/InstalledPlugins-***********` | 776 B | -1 B (-0.13%) |
| `examples/getstarted/build/isNil-***********` | 121 B | 0 B |
| `examples/getstarted/build/it.json-***********` | 5.51 kB | 0 B |
| `examples/getstarted/build/ja.json-***********` | 1.41 kB | 0 B |
| `examples/getstarted/build/ko.json-***********` | 2.1 kB | 0 B |
| `examples/getstarted/build/layout-***********` | 3.24 kB | 0 B |
| `examples/getstarted/build/Layout-***********` | 1.66 kB | -3 B (-0.18%) |
| `examples/getstarted/build/ListConfigurationPage-***********` | 4.22 kB | -2 B (-0.05%) |
| `examples/getstarted/build/ListViewPage-***********` | 5.95 kB | -3 B (-0.05%) |
| `examples/getstarted/build/Login-***********` | 460 B | -3 B (-0.65%) |
| `examples/getstarted/build/lt-***********` | 2.82 kB | -2 B (-0.07%) |
| `examples/getstarted/build/MagicLinkEE-***********` | 387 B | 0 B |
| `examples/getstarted/build/MarketplacePage-***********` | 13.8 kB | 0 B |
| `examples/getstarted/build/ml.json-***********` | 11.9 kB | 0 B |
| `examples/getstarted/build/ModalForm-***********` | 243 B | 0 B |
| `examples/getstarted/build/ms.json-***********` | 1.18 kB | 0 B |
| `examples/getstarted/build/nl.json-***********` | 1.23 kB | 0 B |
| `examples/getstarted/build/no.json-***********` | 5.47 kB | 0 B |
| `examples/getstarted/build/NoContentTypePage-***********` | 454 B | 0 B |
| `examples/getstarted/build/NoPermissionsPage-***********` | 230 B | -1 B (-0.43%) |
| `examples/getstarted/build/Ornaments-***********` | 1.5 kB | -1 B (-0.07%) |
| `examples/getstarted/build/Permissions-***********` | 9.33 kB | 0 B |
| `examples/getstarted/build/pl.json-***********` | 2.09 kB | 0 B |
| `examples/getstarted/build/PrivateRoute-***********` | 248 B | -2 B (-0.8%) |
| `examples/getstarted/build/ProfilePage-***********` | 2.24 kB | -1 B (-0.04%) |
| `examples/getstarted/build/pt-BR.json-***********` | 1.51 kB | 0 B |
| `examples/getstarted/build/pt.json-***********` | 1.03 kB | 0 B |
| `examples/getstarted/build/purchase-review-workflows-***********` | 252 kB | +4 B (0%) |
| `examples/getstarted/build/PurchaseAuditLogs-***********` | 250 kB | +4 B (0%) |
| `examples/getstarted/build/PurchaseContentHistory-***********` | 8.62 MB | +4 B (0%) |
| `examples/getstarted/build/PurchaseSingleSignOn-***********` | 155 kB | +1 B (0%) |
| `examples/getstarted/build/RelativeTime-***********` | 396 B | -1 B (-0.25%) |
| `examples/getstarted/build/router-***********` | 397 B | 0 B |
| `examples/getstarted/build/ru.json-***********` | 1.85 kB | 0 B |
| `examples/getstarted/build/sa.json-***********` | 11.7 kB | 0 B |
| `examples/getstarted/build/schemas-***********` | 3.16 kB | 0 B |
| `examples/getstarted/build/SearchInput-***********` | 528 B | 0 B |
| `examples/getstarted/build/selectors-***********` | 111 B | +1 B (+0.91%) |
| `examples/getstarted/build/SelectRoles-***********` | 1.03 kB | +2 B (+0.19%) |
| `examples/getstarted/build/SingleSignOnPage-***********` | 1.75 kB | +2 B (+0.11%) |
| `examples/getstarted/build/sk.json-***********` | 748 B | 0 B |
| `examples/getstarted/build/sortBy-***********` | 695 B | -1 B (-0.14%) |
| `examples/getstarted/build/SSOProviders-***********` | 721 B | 0 B |
| `examples/getstarted/build/strapi-************` | 2 kB | 0 B |
| `examples/getstarted/build/sv.json-***********` | 4.04 kB | 0 B |
| `examples/getstarted/build/Table-***********` | 1.21 kB | 0 B |
| `examples/getstarted/build/tail-***********` | 245 B | +1 B (+0.41%) |
| `examples/getstarted/build/th.json-***********` | 4.07 kB | 0 B |
| `examples/getstarted/build/TokenTypeSelect-***********` | 121 kB | -2 B (0%) |
| `examples/getstarted/build/tr-***********` | 83 B | 0 B |
| `examples/getstarted/build/tr.json-***********` | 1.02 kB | 0 B |
| `examples/getstarted/build/transferTokens-***********` | 502 B | -2 B (-0.4%) |
| `examples/getstarted/build/uk-***********` | 91 B | 0 B |
| `examples/getstarted/build/uk.json-***********` | 3.32 kB | 0 B |
| `examples/getstarted/build/useAdminRoles-***********` | 242 B | -2 B (-0.82%) |
| `examples/getstarted/build/UseCasePage-***********` | 1.24 kB | -4 B (-0.32%) |
| `examples/getstarted/build/useDragLayer-***********` | 180 B | 0 B |
| `examples/getstarted/build/useLicenseLimitNotification-***********` | 710 B | -1 B (-0.14%) |
| `examples/getstarted/build/useOnce-***********` | 102 B | 0 B |
| `examples/getstarted/build/useReviewWorkflows-***********` | 1.33 kB | -2 B (-0.15%) |
| `examples/getstarted/build/users-***********` | 288 B | 0 B |
| `examples/getstarted/build/useWebhooks-***********` | 621 B | -3 B (-0.48%) |
| `examples/getstarted/build/validation-***********` | 520 B | 0 B |
| `examples/getstarted/build/vi.json-***********` | 1.41 kB | 0 B |
| `examples/getstarted/build/WidgetHelpers-***********` | 521 B | 0 B |
| `examples/getstarted/build/Widgets-***********` | 1.14 kB | +1 B (+0.09%) |
| `examples/getstarted/build/zh-Hans.json-***********` | 2.92 kB | 0 B |
| `examples/getstarted/build/zh.json-***********` | 1.09 kB | 0 B |

</details>



<a href="https://github.com/preactjs/compressed-size-action"><sub>compressed-size-action</sub></a>
author_association: CONTRIBUTOR
user: login: trunk-io[bot]
created_at: 2025-04-16T16:21:23Z
body: <!-- Trunk Test Analytics -->


[![Static Badge](https://raster.shields.io/badge/1051_tests-c0065227-purple)](https://app.trunk.io/strapi/flaky-tests/pr/23382?repo=strapi/strapi&commitHash=c00652273eea6858fecf546337b07ce43d459a9c&utm_source=gh-comment) &ensp; [![Static Badge](https://raster.shields.io/badge/1-failed-crimson)](https://app.trunk.io/strapi/flaky-tests/pr/23382?repo=strapi/strapi&commitHash=c00652273eea6858fecf546337b07ce43d459a9c&utm_source=gh-comment&runConclusion=FAILURE) &ensp; [![Static Badge](https://raster.shields.io/badge/0-flaky-red)](https://app.trunk.io/strapi/flaky-tests/pr/23382?repo=strapi/strapi&commitHash=c00652273eea6858fecf546337b07ce43d459a9c&utm_source=gh-comment&runConclusion=FAILURE) &ensp; [![Static Badge](https://raster.shields.io/badge/0-quarantined-yellow)](https://app.trunk.io/strapi/flaky-tests/pr/23382?repo=strapi/strapi&commitHash=c00652273eea6858fecf546337b07ce43d459a9c&utm_source=gh-comment&runConclusion=QUARANTINED)


| Failed Test | Failure Summary | Logs |
| --- | --- | --- |
| <code>Edit collection type › Can configure advanced settings for multiple fields sequentially</code> | The button with the text 'Text Small or long text like title or description' was not clickable within 10 seconds. | [Logs ↗︎](https://app.trunk.io/strapi/flaky-tests/pr/23382?repo=strapi/strapi&commitHash=c00652273eea6858fecf546337b07ce43d459a9c&utm_source=gh-comment&runConclusion=FAILURE&runId=98506820-2900-550f-9cc1-576928c5881d) |
<sub>

[View Full Report ↗︎](https://app.trunk.io/strapi/flaky-tests/pr/23382?repo=strapi/strapi&commitHash=c00652273eea6858fecf546337b07ce43d459a9c&utm_source=gh-comment) ⋅ [Docs](https://docs.trunk.io/flaky-tests?utm_source=gh-comment)

</sub>
author_association: NONE