@strapi/content-type-builder 5.56.0 → 5.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/dist/admin/components/AIChat/lib/transforms/schemas/toCTB.js +117 -13
  2. package/dist/admin/components/AIChat/lib/transforms/schemas/toCTB.js.map +1 -1
  3. package/dist/admin/components/AIChat/lib/transforms/schemas/toCTB.mjs +118 -13
  4. package/dist/admin/components/AIChat/lib/transforms/schemas/toCTB.mjs.map +1 -1
  5. package/dist/admin/components/AIChat/providers/SchemaProvider.js +31 -19
  6. package/dist/admin/components/AIChat/providers/SchemaProvider.js.map +1 -1
  7. package/dist/admin/components/AIChat/providers/SchemaProvider.mjs +31 -19
  8. package/dist/admin/components/AIChat/providers/SchemaProvider.mjs.map +1 -1
  9. package/dist/admin/components/DataManager/DataManagerContext.js.map +1 -1
  10. package/dist/admin/components/DataManager/DataManagerContext.mjs.map +1 -1
  11. package/dist/admin/components/DataManager/DataManagerProvider.js +166 -6
  12. package/dist/admin/components/DataManager/DataManagerProvider.js.map +1 -1
  13. package/dist/admin/components/DataManager/DataManagerProvider.mjs +165 -7
  14. package/dist/admin/components/DataManager/DataManagerProvider.mjs.map +1 -1
  15. package/dist/admin/components/DataManager/RenameMigrationModal.js +285 -0
  16. package/dist/admin/components/DataManager/RenameMigrationModal.js.map +1 -0
  17. package/dist/admin/components/DataManager/RenameMigrationModal.mjs +257 -0
  18. package/dist/admin/components/DataManager/RenameMigrationModal.mjs.map +1 -0
  19. package/dist/admin/components/DataManager/reducer.js +107 -2
  20. package/dist/admin/components/DataManager/reducer.js.map +1 -1
  21. package/dist/admin/components/DataManager/reducer.mjs +106 -2
  22. package/dist/admin/components/DataManager/reducer.mjs.map +1 -1
  23. package/dist/admin/components/DataManager/utils/cleanData.js +8 -1
  24. package/dist/admin/components/DataManager/utils/cleanData.js.map +1 -1
  25. package/dist/admin/components/DataManager/utils/cleanData.mjs +8 -1
  26. package/dist/admin/components/DataManager/utils/cleanData.mjs.map +1 -1
  27. package/dist/admin/components/DataManager/utils/groupRenameChains.js +86 -0
  28. package/dist/admin/components/DataManager/utils/groupRenameChains.js.map +1 -0
  29. package/dist/admin/components/DataManager/utils/groupRenameChains.mjs +84 -0
  30. package/dist/admin/components/DataManager/utils/groupRenameChains.mjs.map +1 -0
  31. package/dist/admin/components/DataManager/utils/isStorageCompatibleRename.js +29 -0
  32. package/dist/admin/components/DataManager/utils/isStorageCompatibleRename.js.map +1 -0
  33. package/dist/admin/components/DataManager/utils/isStorageCompatibleRename.mjs +27 -0
  34. package/dist/admin/components/DataManager/utils/isStorageCompatibleRename.mjs.map +1 -0
  35. package/dist/admin/components/DataManager/utils/resolveAfterEditRenameConsent.js +35 -0
  36. package/dist/admin/components/DataManager/utils/resolveAfterEditRenameConsent.js.map +1 -0
  37. package/dist/admin/components/DataManager/utils/resolveAfterEditRenameConsent.mjs +33 -0
  38. package/dist/admin/components/DataManager/utils/resolveAfterEditRenameConsent.mjs.map +1 -0
  39. package/dist/admin/components/DataManager/utils/splitChainsByInheritedConsent.js +48 -0
  40. package/dist/admin/components/DataManager/utils/splitChainsByInheritedConsent.js.map +1 -0
  41. package/dist/admin/components/DataManager/utils/splitChainsByInheritedConsent.mjs +45 -0
  42. package/dist/admin/components/DataManager/utils/splitChainsByInheritedConsent.mjs.map +1 -0
  43. package/dist/admin/components/FormModal/FormModal.js +142 -8
  44. package/dist/admin/components/FormModal/FormModal.js.map +1 -1
  45. package/dist/admin/components/FormModal/FormModal.mjs +143 -9
  46. package/dist/admin/components/FormModal/FormModal.mjs.map +1 -1
  47. package/dist/admin/components/FormModal/attributes/validation/common.js +6 -5
  48. package/dist/admin/components/FormModal/attributes/validation/common.js.map +1 -1
  49. package/dist/admin/components/FormModal/attributes/validation/common.mjs +1 -1
  50. package/dist/admin/components/FormModal/attributes/validation/common.mjs.map +1 -1
  51. package/dist/admin/components/FormModal/component/createComponentSchema.js +10 -7
  52. package/dist/admin/components/FormModal/component/createComponentSchema.js.map +1 -1
  53. package/dist/admin/components/FormModal/component/createComponentSchema.mjs +1 -1
  54. package/dist/admin/components/FormModal/component/createComponentSchema.mjs.map +1 -1
  55. package/dist/admin/components/FormModal/contentType/createContentTypeSchema.js +24 -21
  56. package/dist/admin/components/FormModal/contentType/createContentTypeSchema.js.map +1 -1
  57. package/dist/admin/components/FormModal/contentType/createContentTypeSchema.mjs +1 -1
  58. package/dist/admin/components/FormModal/contentType/createContentTypeSchema.mjs.map +1 -1
  59. package/dist/admin/components/FormModal/forms/utils/createCollectionName.js +3 -2
  60. package/dist/admin/components/FormModal/forms/utils/createCollectionName.js.map +1 -1
  61. package/dist/admin/components/FormModal/forms/utils/createCollectionName.mjs +1 -1
  62. package/dist/admin/components/FormModal/forms/utils/createCollectionName.mjs.map +1 -1
  63. package/dist/admin/components/FormModal/utils/getRenameStorageChange.js +73 -0
  64. package/dist/admin/components/FormModal/utils/getRenameStorageChange.js.map +1 -0
  65. package/dist/admin/components/FormModal/utils/getRenameStorageChange.mjs +70 -0
  66. package/dist/admin/components/FormModal/utils/getRenameStorageChange.mjs.map +1 -0
  67. package/dist/admin/src/components/AIChat/lib/transforms/schemas/toCTB.d.ts +4 -3
  68. package/dist/admin/src/components/AIChat/lib/types/schema.d.ts +6 -1
  69. package/dist/admin/src/components/DataManager/DataManagerContext.d.ts +28 -2
  70. package/dist/admin/src/components/DataManager/RenameMigrationModal.d.ts +73 -0
  71. package/dist/admin/src/components/DataManager/reducer.d.ts +17 -2
  72. package/dist/admin/src/components/DataManager/utils/cleanData.d.ts +16 -0
  73. package/dist/admin/src/components/DataManager/utils/groupRenameChains.d.ts +31 -0
  74. package/dist/admin/src/components/DataManager/utils/isStorageCompatibleRename.d.ts +10 -0
  75. package/dist/admin/src/components/DataManager/utils/resolveAfterEditRenameConsent.d.ts +24 -0
  76. package/dist/admin/src/components/DataManager/utils/splitChainsByInheritedConsent.d.ts +26 -0
  77. package/dist/admin/src/components/FormModal/utils/getRenameStorageChange.d.ts +40 -0
  78. package/dist/admin/src/types.d.ts +23 -0
  79. package/dist/admin/translations/en.json.js +12 -0
  80. package/dist/admin/translations/en.json.js.map +1 -1
  81. package/dist/admin/translations/en.json.mjs +12 -0
  82. package/dist/admin/translations/en.json.mjs.map +1 -1
  83. package/dist/server/config.js +24 -3
  84. package/dist/server/config.js.map +1 -1
  85. package/dist/server/config.mjs +21 -3
  86. package/dist/server/config.mjs.map +1 -1
  87. package/dist/server/controllers/schema.js +2 -2
  88. package/dist/server/controllers/schema.js.map +1 -1
  89. package/dist/server/controllers/schema.mjs +1 -1
  90. package/dist/server/controllers/schema.mjs.map +1 -1
  91. package/dist/server/controllers/validation/content-type.js +9 -11
  92. package/dist/server/controllers/validation/content-type.js.map +1 -1
  93. package/dist/server/controllers/validation/content-type.mjs +7 -9
  94. package/dist/server/controllers/validation/content-type.mjs.map +1 -1
  95. package/dist/server/controllers/validation/model-schema.js +2 -3
  96. package/dist/server/controllers/validation/model-schema.js.map +1 -1
  97. package/dist/server/controllers/validation/model-schema.mjs +1 -2
  98. package/dist/server/controllers/validation/model-schema.mjs.map +1 -1
  99. package/dist/server/controllers/validation/relations.js +1 -2
  100. package/dist/server/controllers/validation/relations.js.map +1 -1
  101. package/dist/server/controllers/validation/relations.mjs +1 -2
  102. package/dist/server/controllers/validation/relations.mjs.map +1 -1
  103. package/dist/server/controllers/validation/schema.js +42 -23
  104. package/dist/server/controllers/validation/schema.js.map +1 -1
  105. package/dist/server/controllers/validation/schema.mjs +43 -24
  106. package/dist/server/controllers/validation/schema.mjs.map +1 -1
  107. package/dist/server/index.js +1 -1
  108. package/dist/server/index.js.map +1 -1
  109. package/dist/server/services/content-types.js +1 -2
  110. package/dist/server/services/content-types.js.map +1 -1
  111. package/dist/server/services/content-types.mjs +2 -3
  112. package/dist/server/services/content-types.mjs.map +1 -1
  113. package/dist/server/services/migration-builder/index.js +580 -0
  114. package/dist/server/services/migration-builder/index.js.map +1 -0
  115. package/dist/server/services/migration-builder/index.mjs +573 -0
  116. package/dist/server/services/migration-builder/index.mjs.map +1 -0
  117. package/dist/server/services/schema-builder/content-type-builder.js +31 -1
  118. package/dist/server/services/schema-builder/content-type-builder.js.map +1 -1
  119. package/dist/server/services/schema-builder/content-type-builder.mjs +31 -1
  120. package/dist/server/services/schema-builder/content-type-builder.mjs.map +1 -1
  121. package/dist/server/services/schema-mutation.js +30 -2
  122. package/dist/server/services/schema-mutation.js.map +1 -1
  123. package/dist/server/services/schema-mutation.mjs +10 -2
  124. package/dist/server/services/schema-mutation.mjs.map +1 -1
  125. package/dist/server/services/schema.js +259 -15
  126. package/dist/server/services/schema.js.map +1 -1
  127. package/dist/server/services/schema.mjs +252 -9
  128. package/dist/server/services/schema.mjs.map +1 -1
  129. package/dist/server/src/config.d.ts +23 -2
  130. package/dist/server/src/config.d.ts.map +1 -1
  131. package/dist/server/src/controllers/validation/content-type.d.ts +0 -1
  132. package/dist/server/src/controllers/validation/content-type.d.ts.map +1 -1
  133. package/dist/server/src/controllers/validation/relations.d.ts.map +1 -1
  134. package/dist/server/src/controllers/validation/schema.d.ts +28 -0
  135. package/dist/server/src/controllers/validation/schema.d.ts.map +1 -1
  136. package/dist/server/src/index.d.ts +10 -2
  137. package/dist/server/src/index.d.ts.map +1 -1
  138. package/dist/server/src/services/content-types.d.ts.map +1 -1
  139. package/dist/server/src/services/migration-builder/index.d.ts +83 -0
  140. package/dist/server/src/services/migration-builder/index.d.ts.map +1 -0
  141. package/dist/server/src/services/schema-builder/content-type-builder.d.ts.map +1 -1
  142. package/dist/server/src/services/schema-mutation.d.ts +4 -2
  143. package/dist/server/src/services/schema-mutation.d.ts.map +1 -1
  144. package/dist/server/src/services/schema.d.ts +19 -0
  145. package/dist/server/src/services/schema.d.ts.map +1 -1
  146. package/package.json +7 -6
@@ -52,27 +52,39 @@ const SchemaChatProvider = ({ children })=>{
52
52
  const schemaChanges = extractSchemaChangesFromMessage(latestMessage);
53
53
  // Check if addField action is already completed
54
54
  const isAddFieldCompleted = state.completedActions.includes(strapiAdmin.GUIDED_TOUR_REQUIRED_ACTIONS.contentTypeBuilder.addField);
55
- schemaChanges.forEach((change)=>{
56
- const oldSchema = change.schema.modelType === 'contentType' ? contentTypes[change.schema.uid] : components[change.schema.uid];
57
- const newSchema = toCTB.transformChatToCTB(change.schema, oldSchema);
58
- // Check if any attributes/fields are being added to any schema (existing or new)
59
- if (!isAddFieldCompleted && change.schema.attributes) {
60
- // If a field is being added or updated, dispatch guided tour action to show Save tooltip
61
- if (change.type !== 'remove' && Object.keys(change.schema.attributes).length > 0) {
62
- dispatch({
63
- type: 'set_completed_actions',
64
- payload: [
65
- strapiAdmin.GUIDED_TOUR_REQUIRED_ACTIONS.contentTypeBuilder.addField
66
- ]
67
- });
55
+ // Changes are applied one at a time: a change that renames existing fields
56
+ // may prompt the user for consent, and prompts must not overlap.
57
+ const applyChanges = async ()=>{
58
+ for (const change of schemaChanges){
59
+ const oldSchema = change.schema.modelType === 'contentType' ? contentTypes[change.schema.uid] : components[change.schema.uid];
60
+ const newSchema = toCTB.transformChatToCTB(change.schema, oldSchema);
61
+ // Check if any attributes/fields are being added to any schema (existing or new)
62
+ if (!isAddFieldCompleted && change.schema.attributes) {
63
+ // If a field is being added or updated, dispatch guided tour action to show Save tooltip
64
+ if (change.type !== 'remove' && Object.keys(change.schema.attributes).length > 0) {
65
+ dispatch({
66
+ type: 'set_completed_actions',
67
+ payload: [
68
+ strapiAdmin.GUIDED_TOUR_REQUIRED_ACTIONS.contentTypeBuilder.addField
69
+ ]
70
+ });
71
+ }
72
+ }
73
+ // eslint-disable-next-line no-await-in-loop
74
+ const applied = await applyChange({
75
+ action: TYPE_TO_ACTION[change.type],
76
+ schema: newSchema
77
+ });
78
+ // The user cancelled a rename prompt: stop here. Earlier changes in this
79
+ // batch stay applied (they are in the undo history); the message is not
80
+ // marked as revised.
81
+ if (!applied) {
82
+ return;
68
83
  }
69
84
  }
70
- applyChange({
71
- action: TYPE_TO_ACTION[change.type],
72
- schema: newSchema
73
- });
74
- });
75
- setLastRevisedId(latestMessage.id);
85
+ setLastRevisedId(latestMessage.id);
86
+ };
87
+ applyChanges();
76
88
  // eslint-disable-next-line react-hooks/exhaustive-deps
77
89
  }, [
78
90
  messages
@@ -1 +1 @@
1
- {"version":3,"file":"SchemaProvider.js","sources":["../../../../../admin/src/components/AIChat/providers/SchemaProvider.tsx"],"sourcesContent":["import { createContext, useContext, ReactNode, useEffect, useState } from 'react';\n\nimport { GUIDED_TOUR_REQUIRED_ACTIONS, useGuidedTour } from '@strapi/admin/strapi-admin';\n\nimport { useDataManager } from '../../DataManager/useDataManager';\nimport { transformChatToCTB } from '../lib/transforms/schemas/toCTB';\nimport { SchemaChange } from '../lib/types/annotations';\nimport { AIMessage } from '../lib/types/messages';\n\nimport { useStrapiChat } from './ChatProvider';\n\nimport type { Schema } from '../lib/types/schema';\nimport type { Internal } from '@strapi/types';\n\ninterface SchemaContextType {\n lastRevisedId: string | null;\n setLastRevisedId: (id: string | null) => void;\n}\n\nconst SchemaContext = createContext<SchemaContextType | undefined>(undefined);\n\nconst TYPE_TO_ACTION: Record<string, 'add' | 'update' | 'delete'> = {\n create: 'add',\n update: 'update',\n remove: 'delete',\n};\n\ntype SchemaGenerationOutput = {\n schemas?: Schema[];\n error?: unknown;\n};\n\nfunction extractSchemaChangesFromMessage(message: AIMessage): SchemaChange[] {\n if (message.role !== 'assistant') return [];\n\n const changes: SchemaChange[] = [];\n\n message.parts?.forEach((part, partIndex) => {\n // We only care about the schema generation tool\n if (part && typeof part === 'object' && part.type === 'tool-schemaGenerationTool') {\n // Prefer validated schemas from output; ignore if there's an error or no output yet\n const output = part.output as SchemaGenerationOutput | undefined;\n if (!output || output.error || !Array.isArray(output.schemas)) return;\n\n const baseId = part.toolCallId ?? `${message.id}-${partIndex}`;\n\n output.schemas.forEach((schema, schemaIndex) => {\n const revisionId = `${baseId}-${schema.uid ?? schema.name ?? schemaIndex}`;\n const type = (schema.action as SchemaChange['type']) || 'update';\n changes.push({ type, schema, revisionId });\n });\n }\n });\n\n return changes;\n}\n\nexport const SchemaChatProvider = ({ children }: { children: ReactNode }) => {\n const [lastRevisedId, setLastRevisedId] = useState<string | null>(null);\n const { messages, status } = useStrapiChat();\n const { contentTypes, components, applyChange } = useDataManager();\n const dispatch = useGuidedTour('SchemaChatProvider', (s) => s.dispatch);\n const state = useGuidedTour('SchemaChatProvider', (s) => s.state);\n\n useEffect(() => {\n const latestMessage = messages[messages.length - 1];\n if (!latestMessage) return;\n if (latestMessage.role !== 'assistant') return;\n // Wait until message streaming has finished\n if (status !== 'ready') return;\n\n // const schemaChanges = latestMessage.schemaChanges;\n const schemaChanges = extractSchemaChangesFromMessage(latestMessage);\n\n // Check if addField action is already completed\n const isAddFieldCompleted = state.completedActions.includes(\n GUIDED_TOUR_REQUIRED_ACTIONS.contentTypeBuilder.addField\n );\n\n schemaChanges.forEach((change: SchemaChange) => {\n const oldSchema =\n change.schema.modelType === 'contentType'\n ? contentTypes[change.schema.uid as Internal.UID.ContentType]\n : components[change.schema.uid as Internal.UID.Component];\n const newSchema = transformChatToCTB(change.schema, oldSchema);\n\n // Check if any attributes/fields are being added to any schema (existing or new)\n if (!isAddFieldCompleted && change.schema.attributes) {\n // If a field is being added or updated, dispatch guided tour action to show Save tooltip\n if (change.type !== 'remove' && Object.keys(change.schema.attributes).length > 0) {\n dispatch({\n type: 'set_completed_actions',\n payload: [GUIDED_TOUR_REQUIRED_ACTIONS.contentTypeBuilder.addField],\n });\n }\n }\n\n applyChange({\n action: TYPE_TO_ACTION[change.type]!,\n schema: newSchema,\n });\n });\n setLastRevisedId(latestMessage.id);\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [messages]);\n\n return (\n <SchemaContext.Provider value={{ lastRevisedId, setLastRevisedId }}>\n {children}\n </SchemaContext.Provider>\n );\n};\n\nexport const useChatSchema = () => {\n const context = useContext(SchemaContext);\n if (context === undefined) {\n throw new Error('useSchema must be used within a SchemaProvider');\n }\n return context;\n};\n"],"names":["SchemaContext","createContext","undefined","TYPE_TO_ACTION","create","update","remove","extractSchemaChangesFromMessage","message","role","changes","parts","forEach","part","partIndex","type","output","error","Array","isArray","schemas","baseId","toolCallId","id","schema","schemaIndex","revisionId","uid","name","action","push","SchemaChatProvider","children","lastRevisedId","setLastRevisedId","useState","messages","status","useStrapiChat","contentTypes","components","applyChange","useDataManager","dispatch","useGuidedTour","s","state","useEffect","latestMessage","length","schemaChanges","isAddFieldCompleted","completedActions","includes","GUIDED_TOUR_REQUIRED_ACTIONS","contentTypeBuilder","addField","change","oldSchema","modelType","newSchema","transformChatToCTB","attributes","Object","keys","payload","_jsx","Provider","value"],"mappings":"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"}
1
+ {"version":3,"file":"SchemaProvider.js","sources":["../../../../../admin/src/components/AIChat/providers/SchemaProvider.tsx"],"sourcesContent":["import { createContext, useContext, ReactNode, useEffect, useState } from 'react';\n\nimport { GUIDED_TOUR_REQUIRED_ACTIONS, useGuidedTour } from '@strapi/admin/strapi-admin';\n\nimport { useDataManager } from '../../DataManager/useDataManager';\nimport { transformChatToCTB } from '../lib/transforms/schemas/toCTB';\nimport { SchemaChange } from '../lib/types/annotations';\nimport { AIMessage } from '../lib/types/messages';\n\nimport { useStrapiChat } from './ChatProvider';\n\nimport type { Schema } from '../lib/types/schema';\nimport type { Internal } from '@strapi/types';\n\ninterface SchemaContextType {\n lastRevisedId: string | null;\n setLastRevisedId: (id: string | null) => void;\n}\n\nconst SchemaContext = createContext<SchemaContextType | undefined>(undefined);\n\nconst TYPE_TO_ACTION: Record<string, 'add' | 'update' | 'delete'> = {\n create: 'add',\n update: 'update',\n remove: 'delete',\n};\n\ntype SchemaGenerationOutput = {\n schemas?: Schema[];\n error?: unknown;\n};\n\nfunction extractSchemaChangesFromMessage(message: AIMessage): SchemaChange[] {\n if (message.role !== 'assistant') return [];\n\n const changes: SchemaChange[] = [];\n\n message.parts?.forEach((part, partIndex) => {\n // We only care about the schema generation tool\n if (part && typeof part === 'object' && part.type === 'tool-schemaGenerationTool') {\n // Prefer validated schemas from output; ignore if there's an error or no output yet\n const output = part.output as SchemaGenerationOutput | undefined;\n if (!output || output.error || !Array.isArray(output.schemas)) return;\n\n const baseId = part.toolCallId ?? `${message.id}-${partIndex}`;\n\n output.schemas.forEach((schema, schemaIndex) => {\n const revisionId = `${baseId}-${schema.uid ?? schema.name ?? schemaIndex}`;\n const type = (schema.action as SchemaChange['type']) || 'update';\n changes.push({ type, schema, revisionId });\n });\n }\n });\n\n return changes;\n}\n\nexport const SchemaChatProvider = ({ children }: { children: ReactNode }) => {\n const [lastRevisedId, setLastRevisedId] = useState<string | null>(null);\n const { messages, status } = useStrapiChat();\n const { contentTypes, components, applyChange } = useDataManager();\n const dispatch = useGuidedTour('SchemaChatProvider', (s) => s.dispatch);\n const state = useGuidedTour('SchemaChatProvider', (s) => s.state);\n\n useEffect(() => {\n const latestMessage = messages[messages.length - 1];\n if (!latestMessage) return;\n if (latestMessage.role !== 'assistant') return;\n // Wait until message streaming has finished\n if (status !== 'ready') return;\n\n // const schemaChanges = latestMessage.schemaChanges;\n const schemaChanges = extractSchemaChangesFromMessage(latestMessage);\n\n // Check if addField action is already completed\n const isAddFieldCompleted = state.completedActions.includes(\n GUIDED_TOUR_REQUIRED_ACTIONS.contentTypeBuilder.addField\n );\n\n // Changes are applied one at a time: a change that renames existing fields\n // may prompt the user for consent, and prompts must not overlap.\n const applyChanges = async () => {\n for (const change of schemaChanges) {\n const oldSchema =\n change.schema.modelType === 'contentType'\n ? contentTypes[change.schema.uid as Internal.UID.ContentType]\n : components[change.schema.uid as Internal.UID.Component];\n const newSchema = transformChatToCTB(change.schema, oldSchema);\n\n // Check if any attributes/fields are being added to any schema (existing or new)\n if (!isAddFieldCompleted && change.schema.attributes) {\n // If a field is being added or updated, dispatch guided tour action to show Save tooltip\n if (change.type !== 'remove' && Object.keys(change.schema.attributes).length > 0) {\n dispatch({\n type: 'set_completed_actions',\n payload: [GUIDED_TOUR_REQUIRED_ACTIONS.contentTypeBuilder.addField],\n });\n }\n }\n\n // eslint-disable-next-line no-await-in-loop\n const applied = await applyChange({\n action: TYPE_TO_ACTION[change.type]!,\n schema: newSchema,\n });\n\n // The user cancelled a rename prompt: stop here. Earlier changes in this\n // batch stay applied (they are in the undo history); the message is not\n // marked as revised.\n if (!applied) {\n return;\n }\n }\n\n setLastRevisedId(latestMessage.id);\n };\n\n applyChanges();\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [messages]);\n\n return (\n <SchemaContext.Provider value={{ lastRevisedId, setLastRevisedId }}>\n {children}\n </SchemaContext.Provider>\n );\n};\n\nexport const useChatSchema = () => {\n const context = useContext(SchemaContext);\n if (context === undefined) {\n throw new Error('useSchema must be used within a SchemaProvider');\n }\n return context;\n};\n"],"names":["SchemaContext","createContext","undefined","TYPE_TO_ACTION","create","update","remove","extractSchemaChangesFromMessage","message","role","changes","parts","forEach","part","partIndex","type","output","error","Array","isArray","schemas","baseId","toolCallId","id","schema","schemaIndex","revisionId","uid","name","action","push","SchemaChatProvider","children","lastRevisedId","setLastRevisedId","useState","messages","status","useStrapiChat","contentTypes","components","applyChange","useDataManager","dispatch","useGuidedTour","s","state","useEffect","latestMessage","length","schemaChanges","isAddFieldCompleted","completedActions","includes","GUIDED_TOUR_REQUIRED_ACTIONS","contentTypeBuilder","addField","applyChanges","change","oldSchema","modelType","newSchema","transformChatToCTB","attributes","Object","keys","payload","applied","_jsx","Provider","value"],"mappings":"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"}
@@ -50,27 +50,39 @@ const SchemaChatProvider = ({ children })=>{
50
50
  const schemaChanges = extractSchemaChangesFromMessage(latestMessage);
51
51
  // Check if addField action is already completed
52
52
  const isAddFieldCompleted = state.completedActions.includes(GUIDED_TOUR_REQUIRED_ACTIONS.contentTypeBuilder.addField);
53
- schemaChanges.forEach((change)=>{
54
- const oldSchema = change.schema.modelType === 'contentType' ? contentTypes[change.schema.uid] : components[change.schema.uid];
55
- const newSchema = transformChatToCTB(change.schema, oldSchema);
56
- // Check if any attributes/fields are being added to any schema (existing or new)
57
- if (!isAddFieldCompleted && change.schema.attributes) {
58
- // If a field is being added or updated, dispatch guided tour action to show Save tooltip
59
- if (change.type !== 'remove' && Object.keys(change.schema.attributes).length > 0) {
60
- dispatch({
61
- type: 'set_completed_actions',
62
- payload: [
63
- GUIDED_TOUR_REQUIRED_ACTIONS.contentTypeBuilder.addField
64
- ]
65
- });
53
+ // Changes are applied one at a time: a change that renames existing fields
54
+ // may prompt the user for consent, and prompts must not overlap.
55
+ const applyChanges = async ()=>{
56
+ for (const change of schemaChanges){
57
+ const oldSchema = change.schema.modelType === 'contentType' ? contentTypes[change.schema.uid] : components[change.schema.uid];
58
+ const newSchema = transformChatToCTB(change.schema, oldSchema);
59
+ // Check if any attributes/fields are being added to any schema (existing or new)
60
+ if (!isAddFieldCompleted && change.schema.attributes) {
61
+ // If a field is being added or updated, dispatch guided tour action to show Save tooltip
62
+ if (change.type !== 'remove' && Object.keys(change.schema.attributes).length > 0) {
63
+ dispatch({
64
+ type: 'set_completed_actions',
65
+ payload: [
66
+ GUIDED_TOUR_REQUIRED_ACTIONS.contentTypeBuilder.addField
67
+ ]
68
+ });
69
+ }
70
+ }
71
+ // eslint-disable-next-line no-await-in-loop
72
+ const applied = await applyChange({
73
+ action: TYPE_TO_ACTION[change.type],
74
+ schema: newSchema
75
+ });
76
+ // The user cancelled a rename prompt: stop here. Earlier changes in this
77
+ // batch stay applied (they are in the undo history); the message is not
78
+ // marked as revised.
79
+ if (!applied) {
80
+ return;
66
81
  }
67
82
  }
68
- applyChange({
69
- action: TYPE_TO_ACTION[change.type],
70
- schema: newSchema
71
- });
72
- });
73
- setLastRevisedId(latestMessage.id);
83
+ setLastRevisedId(latestMessage.id);
84
+ };
85
+ applyChanges();
74
86
  // eslint-disable-next-line react-hooks/exhaustive-deps
75
87
  }, [
76
88
  messages
@@ -1 +1 @@
1
- {"version":3,"file":"SchemaProvider.mjs","sources":["../../../../../admin/src/components/AIChat/providers/SchemaProvider.tsx"],"sourcesContent":["import { createContext, useContext, ReactNode, useEffect, useState } from 'react';\n\nimport { GUIDED_TOUR_REQUIRED_ACTIONS, useGuidedTour } from '@strapi/admin/strapi-admin';\n\nimport { useDataManager } from '../../DataManager/useDataManager';\nimport { transformChatToCTB } from '../lib/transforms/schemas/toCTB';\nimport { SchemaChange } from '../lib/types/annotations';\nimport { AIMessage } from '../lib/types/messages';\n\nimport { useStrapiChat } from './ChatProvider';\n\nimport type { Schema } from '../lib/types/schema';\nimport type { Internal } from '@strapi/types';\n\ninterface SchemaContextType {\n lastRevisedId: string | null;\n setLastRevisedId: (id: string | null) => void;\n}\n\nconst SchemaContext = createContext<SchemaContextType | undefined>(undefined);\n\nconst TYPE_TO_ACTION: Record<string, 'add' | 'update' | 'delete'> = {\n create: 'add',\n update: 'update',\n remove: 'delete',\n};\n\ntype SchemaGenerationOutput = {\n schemas?: Schema[];\n error?: unknown;\n};\n\nfunction extractSchemaChangesFromMessage(message: AIMessage): SchemaChange[] {\n if (message.role !== 'assistant') return [];\n\n const changes: SchemaChange[] = [];\n\n message.parts?.forEach((part, partIndex) => {\n // We only care about the schema generation tool\n if (part && typeof part === 'object' && part.type === 'tool-schemaGenerationTool') {\n // Prefer validated schemas from output; ignore if there's an error or no output yet\n const output = part.output as SchemaGenerationOutput | undefined;\n if (!output || output.error || !Array.isArray(output.schemas)) return;\n\n const baseId = part.toolCallId ?? `${message.id}-${partIndex}`;\n\n output.schemas.forEach((schema, schemaIndex) => {\n const revisionId = `${baseId}-${schema.uid ?? schema.name ?? schemaIndex}`;\n const type = (schema.action as SchemaChange['type']) || 'update';\n changes.push({ type, schema, revisionId });\n });\n }\n });\n\n return changes;\n}\n\nexport const SchemaChatProvider = ({ children }: { children: ReactNode }) => {\n const [lastRevisedId, setLastRevisedId] = useState<string | null>(null);\n const { messages, status } = useStrapiChat();\n const { contentTypes, components, applyChange } = useDataManager();\n const dispatch = useGuidedTour('SchemaChatProvider', (s) => s.dispatch);\n const state = useGuidedTour('SchemaChatProvider', (s) => s.state);\n\n useEffect(() => {\n const latestMessage = messages[messages.length - 1];\n if (!latestMessage) return;\n if (latestMessage.role !== 'assistant') return;\n // Wait until message streaming has finished\n if (status !== 'ready') return;\n\n // const schemaChanges = latestMessage.schemaChanges;\n const schemaChanges = extractSchemaChangesFromMessage(latestMessage);\n\n // Check if addField action is already completed\n const isAddFieldCompleted = state.completedActions.includes(\n GUIDED_TOUR_REQUIRED_ACTIONS.contentTypeBuilder.addField\n );\n\n schemaChanges.forEach((change: SchemaChange) => {\n const oldSchema =\n change.schema.modelType === 'contentType'\n ? contentTypes[change.schema.uid as Internal.UID.ContentType]\n : components[change.schema.uid as Internal.UID.Component];\n const newSchema = transformChatToCTB(change.schema, oldSchema);\n\n // Check if any attributes/fields are being added to any schema (existing or new)\n if (!isAddFieldCompleted && change.schema.attributes) {\n // If a field is being added or updated, dispatch guided tour action to show Save tooltip\n if (change.type !== 'remove' && Object.keys(change.schema.attributes).length > 0) {\n dispatch({\n type: 'set_completed_actions',\n payload: [GUIDED_TOUR_REQUIRED_ACTIONS.contentTypeBuilder.addField],\n });\n }\n }\n\n applyChange({\n action: TYPE_TO_ACTION[change.type]!,\n schema: newSchema,\n });\n });\n setLastRevisedId(latestMessage.id);\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [messages]);\n\n return (\n <SchemaContext.Provider value={{ lastRevisedId, setLastRevisedId }}>\n {children}\n </SchemaContext.Provider>\n );\n};\n\nexport const useChatSchema = () => {\n const context = useContext(SchemaContext);\n if (context === undefined) {\n throw new Error('useSchema must be used within a SchemaProvider');\n }\n return context;\n};\n"],"names":["SchemaContext","createContext","undefined","TYPE_TO_ACTION","create","update","remove","extractSchemaChangesFromMessage","message","role","changes","parts","forEach","part","partIndex","type","output","error","Array","isArray","schemas","baseId","toolCallId","id","schema","schemaIndex","revisionId","uid","name","action","push","SchemaChatProvider","children","lastRevisedId","setLastRevisedId","useState","messages","status","useStrapiChat","contentTypes","components","applyChange","useDataManager","dispatch","useGuidedTour","s","state","useEffect","latestMessage","length","schemaChanges","isAddFieldCompleted","completedActions","includes","GUIDED_TOUR_REQUIRED_ACTIONS","contentTypeBuilder","addField","change","oldSchema","modelType","newSchema","transformChatToCTB","attributes","Object","keys","payload","_jsx","Provider","value"],"mappings":"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"}
1
+ {"version":3,"file":"SchemaProvider.mjs","sources":["../../../../../admin/src/components/AIChat/providers/SchemaProvider.tsx"],"sourcesContent":["import { createContext, useContext, ReactNode, useEffect, useState } from 'react';\n\nimport { GUIDED_TOUR_REQUIRED_ACTIONS, useGuidedTour } from '@strapi/admin/strapi-admin';\n\nimport { useDataManager } from '../../DataManager/useDataManager';\nimport { transformChatToCTB } from '../lib/transforms/schemas/toCTB';\nimport { SchemaChange } from '../lib/types/annotations';\nimport { AIMessage } from '../lib/types/messages';\n\nimport { useStrapiChat } from './ChatProvider';\n\nimport type { Schema } from '../lib/types/schema';\nimport type { Internal } from '@strapi/types';\n\ninterface SchemaContextType {\n lastRevisedId: string | null;\n setLastRevisedId: (id: string | null) => void;\n}\n\nconst SchemaContext = createContext<SchemaContextType | undefined>(undefined);\n\nconst TYPE_TO_ACTION: Record<string, 'add' | 'update' | 'delete'> = {\n create: 'add',\n update: 'update',\n remove: 'delete',\n};\n\ntype SchemaGenerationOutput = {\n schemas?: Schema[];\n error?: unknown;\n};\n\nfunction extractSchemaChangesFromMessage(message: AIMessage): SchemaChange[] {\n if (message.role !== 'assistant') return [];\n\n const changes: SchemaChange[] = [];\n\n message.parts?.forEach((part, partIndex) => {\n // We only care about the schema generation tool\n if (part && typeof part === 'object' && part.type === 'tool-schemaGenerationTool') {\n // Prefer validated schemas from output; ignore if there's an error or no output yet\n const output = part.output as SchemaGenerationOutput | undefined;\n if (!output || output.error || !Array.isArray(output.schemas)) return;\n\n const baseId = part.toolCallId ?? `${message.id}-${partIndex}`;\n\n output.schemas.forEach((schema, schemaIndex) => {\n const revisionId = `${baseId}-${schema.uid ?? schema.name ?? schemaIndex}`;\n const type = (schema.action as SchemaChange['type']) || 'update';\n changes.push({ type, schema, revisionId });\n });\n }\n });\n\n return changes;\n}\n\nexport const SchemaChatProvider = ({ children }: { children: ReactNode }) => {\n const [lastRevisedId, setLastRevisedId] = useState<string | null>(null);\n const { messages, status } = useStrapiChat();\n const { contentTypes, components, applyChange } = useDataManager();\n const dispatch = useGuidedTour('SchemaChatProvider', (s) => s.dispatch);\n const state = useGuidedTour('SchemaChatProvider', (s) => s.state);\n\n useEffect(() => {\n const latestMessage = messages[messages.length - 1];\n if (!latestMessage) return;\n if (latestMessage.role !== 'assistant') return;\n // Wait until message streaming has finished\n if (status !== 'ready') return;\n\n // const schemaChanges = latestMessage.schemaChanges;\n const schemaChanges = extractSchemaChangesFromMessage(latestMessage);\n\n // Check if addField action is already completed\n const isAddFieldCompleted = state.completedActions.includes(\n GUIDED_TOUR_REQUIRED_ACTIONS.contentTypeBuilder.addField\n );\n\n // Changes are applied one at a time: a change that renames existing fields\n // may prompt the user for consent, and prompts must not overlap.\n const applyChanges = async () => {\n for (const change of schemaChanges) {\n const oldSchema =\n change.schema.modelType === 'contentType'\n ? contentTypes[change.schema.uid as Internal.UID.ContentType]\n : components[change.schema.uid as Internal.UID.Component];\n const newSchema = transformChatToCTB(change.schema, oldSchema);\n\n // Check if any attributes/fields are being added to any schema (existing or new)\n if (!isAddFieldCompleted && change.schema.attributes) {\n // If a field is being added or updated, dispatch guided tour action to show Save tooltip\n if (change.type !== 'remove' && Object.keys(change.schema.attributes).length > 0) {\n dispatch({\n type: 'set_completed_actions',\n payload: [GUIDED_TOUR_REQUIRED_ACTIONS.contentTypeBuilder.addField],\n });\n }\n }\n\n // eslint-disable-next-line no-await-in-loop\n const applied = await applyChange({\n action: TYPE_TO_ACTION[change.type]!,\n schema: newSchema,\n });\n\n // The user cancelled a rename prompt: stop here. Earlier changes in this\n // batch stay applied (they are in the undo history); the message is not\n // marked as revised.\n if (!applied) {\n return;\n }\n }\n\n setLastRevisedId(latestMessage.id);\n };\n\n applyChanges();\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [messages]);\n\n return (\n <SchemaContext.Provider value={{ lastRevisedId, setLastRevisedId }}>\n {children}\n </SchemaContext.Provider>\n );\n};\n\nexport const useChatSchema = () => {\n const context = useContext(SchemaContext);\n if (context === undefined) {\n throw new Error('useSchema must be used within a SchemaProvider');\n }\n return context;\n};\n"],"names":["SchemaContext","createContext","undefined","TYPE_TO_ACTION","create","update","remove","extractSchemaChangesFromMessage","message","role","changes","parts","forEach","part","partIndex","type","output","error","Array","isArray","schemas","baseId","toolCallId","id","schema","schemaIndex","revisionId","uid","name","action","push","SchemaChatProvider","children","lastRevisedId","setLastRevisedId","useState","messages","status","useStrapiChat","contentTypes","components","applyChange","useDataManager","dispatch","useGuidedTour","s","state","useEffect","latestMessage","length","schemaChanges","isAddFieldCompleted","completedActions","includes","GUIDED_TOUR_REQUIRED_ACTIONS","contentTypeBuilder","addField","applyChanges","change","oldSchema","modelType","newSchema","transformChatToCTB","attributes","Object","keys","payload","applied","_jsx","Provider","value"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"DataManagerContext.js","sources":["../../../../admin/src/components/DataManager/DataManagerContext.ts"],"sourcesContent":["/* eslint-disable check-file/filename-naming-convention */\nimport { createContext } from 'react';\n\nimport type { ContentStructure, FolderSelection, SectionKey } from './utils/contentStructure';\nimport type { Component, ContentType } from '../../types';\nimport type { ComponentWithChildren } from './utils/retrieveComponentsThatHaveComponents';\nimport type { NestedComponent } from './utils/retrieveNestedComponents';\nimport type { Internal, Struct } from '@strapi/types';\n\nexport interface DataManagerContextValue {\n isLoading: boolean;\n addAttribute: (opts: {\n attributeToSet: Record<string, unknown>;\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n }) => void;\n editAttribute: (opts: {\n attributeToSet: Record<string, unknown>;\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n name: string;\n }) => void;\n moveAttribute: (opts: {\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n from: number;\n to: number;\n }) => void;\n addCustomFieldAttribute: (params: {\n attributeToSet: Record<string, unknown>;\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n }) => void;\n editCustomFieldAttribute: (params: {\n attributeToSet: Record<string, unknown>;\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n name: string;\n }) => void;\n addCreatedComponentToDynamicZone: (opts: {\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n dynamicZoneTarget: string;\n componentsToAdd: Internal.UID.Component[];\n }) => void;\n createComponentSchema: (opts: {\n data: {\n icon?: string;\n displayName: string;\n };\n componentCategory: string;\n uid: Internal.UID.Component;\n }) => void;\n createSchema: (opts: {\n data: {\n displayName: string;\n singularName: string;\n pluralName: string;\n kind: Struct.ContentTypeKind;\n draftAndPublish: boolean;\n pluginOptions: Record<string, unknown>;\n };\n uid: Internal.UID.Schema;\n folder?: FolderSelection;\n }) => void;\n changeDynamicZoneComponents: (opts: {\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n dynamicZoneTarget: string;\n newComponents: Internal.UID.Component[];\n }) => void;\n removeAttribute: (opts: {\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n attributeToRemoveName: string;\n }) => void;\n deleteComponent(uid: Internal.UID.Component): void;\n deleteContentType(uid: Internal.UID.ContentType): void;\n contentStructure: ContentStructure;\n createFolder: (opts: { section: SectionKey; name: string; parentId: string | null }) => void;\n renameFolder: (opts: { section: SectionKey; id: string; name: string }) => void;\n moveFolder: (opts: {\n section: SectionKey;\n id: string;\n newParentId: string | null;\n index?: number;\n }) => void;\n deleteFolderOnly: (opts: { section: SectionKey; id: string }) => void;\n deleteFolderAndContent: (opts: {\n section: SectionKey;\n id: string;\n contentTypeUids: Internal.UID.ContentType[];\n }) => void;\n assignContentTypeToFolder: (opts: {\n section: SectionKey;\n uid: Internal.UID.ContentType;\n targetGroupId: string | null;\n index?: number;\n }) => void;\n reorderFolderChildren: (opts: {\n section: SectionKey;\n groupId: string;\n from: number;\n to: number;\n }) => void;\n removeComponentFromDynamicZone: (opts: {\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n dzName: string;\n componentToRemoveIndex: number;\n }) => void;\n sortedContentTypesList: {\n visible: ContentType['visible'];\n name: ContentType['uid'];\n title: ContentType['info']['displayName'];\n plugin: ContentType['plugin'];\n uid: ContentType['uid'];\n to: string;\n kind: ContentType['kind'];\n restrictRelationsTo: ContentType['restrictRelationsTo'];\n status: ContentType['status'];\n }[];\n updateComponentSchema: (opts: {\n data: {\n icon?: string;\n displayName: string;\n };\n componentUID: Internal.UID.Component;\n }) => void;\n updateComponentUid: (opts: {\n newComponentUID: Internal.UID.Component;\n componentUID: Internal.UID.Component;\n }) => void;\n updateSchema: (opts: {\n data: {\n displayName: string;\n kind: Struct.ContentTypeKind;\n draftAndPublish: boolean;\n pluginOptions: Record<string, unknown>;\n };\n uid: Internal.UID.ContentType;\n folder?: FolderSelection;\n }) => void;\n initialComponents: Record<Internal.UID.Component, Component>;\n components: Record<Internal.UID.Component, Component>;\n componentsGroupedByCategory: Record<string, Component[]>;\n componentsThatHaveOtherComponentInTheirAttributes: ComponentWithChildren[];\n initialContentTypes: Record<Internal.UID.ContentType, ContentType>;\n contentTypes: Record<Internal.UID.ContentType, ContentType>;\n isInDevelopmentMode?: boolean;\n nestedComponents: NestedComponent[];\n reservedNames: {\n models: string[];\n attributes: string[];\n };\n allComponentsCategories: string[];\n saveSchema(): Promise<void>;\n isModified: boolean;\n isSaving: boolean;\n applyChange: (opts: {\n action: 'add' | 'update' | 'delete';\n schema: ContentType | Component;\n }) => void;\n history: {\n undo(): void;\n redo(): void;\n discardAllChanges(): void;\n canUndo: boolean;\n canRedo: boolean;\n canDiscardAll: boolean;\n };\n}\n\n// @ts-expect-error need to pass initial value to params\nexport const DataManagerContext = createContext<DataManagerContextValue>();\n"],"names":["DataManagerContext","createContext"],"mappings":";;;;AA6KA;AACO,MAAMA,qBAAqBC,mBAAAA;;;;"}
1
+ {"version":3,"file":"DataManagerContext.js","sources":["../../../../admin/src/components/DataManager/DataManagerContext.ts"],"sourcesContent":["/* eslint-disable check-file/filename-naming-convention */\nimport { createContext } from 'react';\n\nimport type { AttributeRenameMigrationMode } from './RenameMigrationModal';\nimport type { ContentStructure, FolderSelection, SectionKey } from './utils/contentStructure';\nimport type { Component, ContentType } from '../../types';\nimport type { ComponentWithChildren } from './utils/retrieveComponentsThatHaveComponents';\nimport type { NestedComponent } from './utils/retrieveNestedComponents';\nimport type { Internal, Struct } from '@strapi/types';\n\nexport interface DataManagerContextValue {\n isLoading: boolean;\n addAttribute: (opts: {\n attributeToSet: Record<string, unknown>;\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n }) => void;\n editAttribute: (opts: {\n attributeToSet: Record<string, unknown>;\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n name: string;\n recordRename?: boolean;\n declineRename?: boolean;\n recordTargetRename?: boolean;\n declineTargetRename?: boolean;\n }) => void;\n moveAttribute: (opts: {\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n from: number;\n to: number;\n }) => void;\n addCustomFieldAttribute: (params: {\n attributeToSet: Record<string, unknown>;\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n }) => void;\n editCustomFieldAttribute: (params: {\n attributeToSet: Record<string, unknown>;\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n name: string;\n recordRename?: boolean;\n declineRename?: boolean;\n }) => void;\n /**\n * Resolves the user's consent to preserve data for one rename hop performed\n * in the attribute form: `true` records the hop, `false` declines it (and the\n * rest of its chain), `null` means the user cancelled the edit.\n */\n confirmAttributeRenameMigration: (rename: {\n forTarget: Struct.ModelType;\n uid: Internal.UID.Schema;\n oldName: string;\n newName: string;\n }) => Promise<boolean | null>;\n /** Effective `renameMigrations.attributes` plugin setting. */\n attributeRenameMigrationMode: AttributeRenameMigrationMode;\n addCreatedComponentToDynamicZone: (opts: {\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n dynamicZoneTarget: string;\n componentsToAdd: Internal.UID.Component[];\n }) => void;\n createComponentSchema: (opts: {\n data: {\n icon?: string;\n displayName: string;\n };\n componentCategory: string;\n uid: Internal.UID.Component;\n }) => void;\n createSchema: (opts: {\n data: {\n displayName: string;\n singularName: string;\n pluralName: string;\n kind: Struct.ContentTypeKind;\n draftAndPublish: boolean;\n pluginOptions: Record<string, unknown>;\n };\n uid: Internal.UID.Schema;\n folder?: FolderSelection;\n }) => void;\n changeDynamicZoneComponents: (opts: {\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n dynamicZoneTarget: string;\n newComponents: Internal.UID.Component[];\n }) => void;\n removeAttribute: (opts: {\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n attributeToRemoveName: string;\n }) => void;\n deleteComponent(uid: Internal.UID.Component): void;\n deleteContentType(uid: Internal.UID.ContentType): void;\n contentStructure: ContentStructure;\n createFolder: (opts: { section: SectionKey; name: string; parentId: string | null }) => void;\n renameFolder: (opts: { section: SectionKey; id: string; name: string }) => void;\n moveFolder: (opts: {\n section: SectionKey;\n id: string;\n newParentId: string | null;\n index?: number;\n }) => void;\n deleteFolderOnly: (opts: { section: SectionKey; id: string }) => void;\n deleteFolderAndContent: (opts: {\n section: SectionKey;\n id: string;\n contentTypeUids: Internal.UID.ContentType[];\n }) => void;\n assignContentTypeToFolder: (opts: {\n section: SectionKey;\n uid: Internal.UID.ContentType;\n targetGroupId: string | null;\n index?: number;\n }) => void;\n reorderFolderChildren: (opts: {\n section: SectionKey;\n groupId: string;\n from: number;\n to: number;\n }) => void;\n removeComponentFromDynamicZone: (opts: {\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n dzName: string;\n componentToRemoveIndex: number;\n }) => void;\n sortedContentTypesList: {\n visible: ContentType['visible'];\n name: ContentType['uid'];\n title: ContentType['info']['displayName'];\n plugin: ContentType['plugin'];\n uid: ContentType['uid'];\n to: string;\n kind: ContentType['kind'];\n restrictRelationsTo: ContentType['restrictRelationsTo'];\n status: ContentType['status'];\n }[];\n updateComponentSchema: (opts: {\n data: {\n icon?: string;\n displayName: string;\n };\n componentUID: Internal.UID.Component;\n }) => void;\n updateComponentUid: (opts: {\n newComponentUID: Internal.UID.Component;\n componentUID: Internal.UID.Component;\n }) => void;\n updateSchema: (opts: {\n data: {\n displayName: string;\n kind: Struct.ContentTypeKind;\n draftAndPublish: boolean;\n pluginOptions: Record<string, unknown>;\n };\n uid: Internal.UID.ContentType;\n folder?: FolderSelection;\n }) => void;\n initialComponents: Record<Internal.UID.Component, Component>;\n components: Record<Internal.UID.Component, Component>;\n componentsGroupedByCategory: Record<string, Component[]>;\n componentsThatHaveOtherComponentInTheirAttributes: ComponentWithChildren[];\n initialContentTypes: Record<Internal.UID.ContentType, ContentType>;\n contentTypes: Record<Internal.UID.ContentType, ContentType>;\n isInDevelopmentMode?: boolean;\n nestedComponents: NestedComponent[];\n reservedNames: {\n models: string[];\n attributes: string[];\n };\n allComponentsCategories: string[];\n saveSchema(): Promise<void>;\n isModified: boolean;\n isSaving: boolean;\n /**\n * Applies a whole-type change (AI chat). Rename hops declared by the change\n * go through the same preservation consent as manual renames, so this may\n * prompt the user. Resolves `false` when the user cancelled and nothing was\n * applied.\n */\n applyChange: (opts: {\n action: 'add' | 'update' | 'delete';\n schema: ContentType | Component;\n }) => Promise<boolean>;\n history: {\n undo(): void;\n redo(): void;\n discardAllChanges(): void;\n canUndo: boolean;\n canRedo: boolean;\n canDiscardAll: boolean;\n };\n}\n\n// @ts-expect-error need to pass initial value to params\nexport const DataManagerContext = createContext<DataManagerContextValue>();\n"],"names":["DataManagerContext","createContext"],"mappings":";;;;AAuMA;AACO,MAAMA,qBAAqBC,mBAAAA;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"DataManagerContext.mjs","sources":["../../../../admin/src/components/DataManager/DataManagerContext.ts"],"sourcesContent":["/* eslint-disable check-file/filename-naming-convention */\nimport { createContext } from 'react';\n\nimport type { ContentStructure, FolderSelection, SectionKey } from './utils/contentStructure';\nimport type { Component, ContentType } from '../../types';\nimport type { ComponentWithChildren } from './utils/retrieveComponentsThatHaveComponents';\nimport type { NestedComponent } from './utils/retrieveNestedComponents';\nimport type { Internal, Struct } from '@strapi/types';\n\nexport interface DataManagerContextValue {\n isLoading: boolean;\n addAttribute: (opts: {\n attributeToSet: Record<string, unknown>;\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n }) => void;\n editAttribute: (opts: {\n attributeToSet: Record<string, unknown>;\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n name: string;\n }) => void;\n moveAttribute: (opts: {\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n from: number;\n to: number;\n }) => void;\n addCustomFieldAttribute: (params: {\n attributeToSet: Record<string, unknown>;\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n }) => void;\n editCustomFieldAttribute: (params: {\n attributeToSet: Record<string, unknown>;\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n name: string;\n }) => void;\n addCreatedComponentToDynamicZone: (opts: {\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n dynamicZoneTarget: string;\n componentsToAdd: Internal.UID.Component[];\n }) => void;\n createComponentSchema: (opts: {\n data: {\n icon?: string;\n displayName: string;\n };\n componentCategory: string;\n uid: Internal.UID.Component;\n }) => void;\n createSchema: (opts: {\n data: {\n displayName: string;\n singularName: string;\n pluralName: string;\n kind: Struct.ContentTypeKind;\n draftAndPublish: boolean;\n pluginOptions: Record<string, unknown>;\n };\n uid: Internal.UID.Schema;\n folder?: FolderSelection;\n }) => void;\n changeDynamicZoneComponents: (opts: {\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n dynamicZoneTarget: string;\n newComponents: Internal.UID.Component[];\n }) => void;\n removeAttribute: (opts: {\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n attributeToRemoveName: string;\n }) => void;\n deleteComponent(uid: Internal.UID.Component): void;\n deleteContentType(uid: Internal.UID.ContentType): void;\n contentStructure: ContentStructure;\n createFolder: (opts: { section: SectionKey; name: string; parentId: string | null }) => void;\n renameFolder: (opts: { section: SectionKey; id: string; name: string }) => void;\n moveFolder: (opts: {\n section: SectionKey;\n id: string;\n newParentId: string | null;\n index?: number;\n }) => void;\n deleteFolderOnly: (opts: { section: SectionKey; id: string }) => void;\n deleteFolderAndContent: (opts: {\n section: SectionKey;\n id: string;\n contentTypeUids: Internal.UID.ContentType[];\n }) => void;\n assignContentTypeToFolder: (opts: {\n section: SectionKey;\n uid: Internal.UID.ContentType;\n targetGroupId: string | null;\n index?: number;\n }) => void;\n reorderFolderChildren: (opts: {\n section: SectionKey;\n groupId: string;\n from: number;\n to: number;\n }) => void;\n removeComponentFromDynamicZone: (opts: {\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n dzName: string;\n componentToRemoveIndex: number;\n }) => void;\n sortedContentTypesList: {\n visible: ContentType['visible'];\n name: ContentType['uid'];\n title: ContentType['info']['displayName'];\n plugin: ContentType['plugin'];\n uid: ContentType['uid'];\n to: string;\n kind: ContentType['kind'];\n restrictRelationsTo: ContentType['restrictRelationsTo'];\n status: ContentType['status'];\n }[];\n updateComponentSchema: (opts: {\n data: {\n icon?: string;\n displayName: string;\n };\n componentUID: Internal.UID.Component;\n }) => void;\n updateComponentUid: (opts: {\n newComponentUID: Internal.UID.Component;\n componentUID: Internal.UID.Component;\n }) => void;\n updateSchema: (opts: {\n data: {\n displayName: string;\n kind: Struct.ContentTypeKind;\n draftAndPublish: boolean;\n pluginOptions: Record<string, unknown>;\n };\n uid: Internal.UID.ContentType;\n folder?: FolderSelection;\n }) => void;\n initialComponents: Record<Internal.UID.Component, Component>;\n components: Record<Internal.UID.Component, Component>;\n componentsGroupedByCategory: Record<string, Component[]>;\n componentsThatHaveOtherComponentInTheirAttributes: ComponentWithChildren[];\n initialContentTypes: Record<Internal.UID.ContentType, ContentType>;\n contentTypes: Record<Internal.UID.ContentType, ContentType>;\n isInDevelopmentMode?: boolean;\n nestedComponents: NestedComponent[];\n reservedNames: {\n models: string[];\n attributes: string[];\n };\n allComponentsCategories: string[];\n saveSchema(): Promise<void>;\n isModified: boolean;\n isSaving: boolean;\n applyChange: (opts: {\n action: 'add' | 'update' | 'delete';\n schema: ContentType | Component;\n }) => void;\n history: {\n undo(): void;\n redo(): void;\n discardAllChanges(): void;\n canUndo: boolean;\n canRedo: boolean;\n canDiscardAll: boolean;\n };\n}\n\n// @ts-expect-error need to pass initial value to params\nexport const DataManagerContext = createContext<DataManagerContextValue>();\n"],"names":["DataManagerContext","createContext"],"mappings":";;AA6KA;AACO,MAAMA,qBAAqBC,aAAAA;;;;"}
1
+ {"version":3,"file":"DataManagerContext.mjs","sources":["../../../../admin/src/components/DataManager/DataManagerContext.ts"],"sourcesContent":["/* eslint-disable check-file/filename-naming-convention */\nimport { createContext } from 'react';\n\nimport type { AttributeRenameMigrationMode } from './RenameMigrationModal';\nimport type { ContentStructure, FolderSelection, SectionKey } from './utils/contentStructure';\nimport type { Component, ContentType } from '../../types';\nimport type { ComponentWithChildren } from './utils/retrieveComponentsThatHaveComponents';\nimport type { NestedComponent } from './utils/retrieveNestedComponents';\nimport type { Internal, Struct } from '@strapi/types';\n\nexport interface DataManagerContextValue {\n isLoading: boolean;\n addAttribute: (opts: {\n attributeToSet: Record<string, unknown>;\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n }) => void;\n editAttribute: (opts: {\n attributeToSet: Record<string, unknown>;\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n name: string;\n recordRename?: boolean;\n declineRename?: boolean;\n recordTargetRename?: boolean;\n declineTargetRename?: boolean;\n }) => void;\n moveAttribute: (opts: {\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n from: number;\n to: number;\n }) => void;\n addCustomFieldAttribute: (params: {\n attributeToSet: Record<string, unknown>;\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n }) => void;\n editCustomFieldAttribute: (params: {\n attributeToSet: Record<string, unknown>;\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n name: string;\n recordRename?: boolean;\n declineRename?: boolean;\n }) => void;\n /**\n * Resolves the user's consent to preserve data for one rename hop performed\n * in the attribute form: `true` records the hop, `false` declines it (and the\n * rest of its chain), `null` means the user cancelled the edit.\n */\n confirmAttributeRenameMigration: (rename: {\n forTarget: Struct.ModelType;\n uid: Internal.UID.Schema;\n oldName: string;\n newName: string;\n }) => Promise<boolean | null>;\n /** Effective `renameMigrations.attributes` plugin setting. */\n attributeRenameMigrationMode: AttributeRenameMigrationMode;\n addCreatedComponentToDynamicZone: (opts: {\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n dynamicZoneTarget: string;\n componentsToAdd: Internal.UID.Component[];\n }) => void;\n createComponentSchema: (opts: {\n data: {\n icon?: string;\n displayName: string;\n };\n componentCategory: string;\n uid: Internal.UID.Component;\n }) => void;\n createSchema: (opts: {\n data: {\n displayName: string;\n singularName: string;\n pluralName: string;\n kind: Struct.ContentTypeKind;\n draftAndPublish: boolean;\n pluginOptions: Record<string, unknown>;\n };\n uid: Internal.UID.Schema;\n folder?: FolderSelection;\n }) => void;\n changeDynamicZoneComponents: (opts: {\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n dynamicZoneTarget: string;\n newComponents: Internal.UID.Component[];\n }) => void;\n removeAttribute: (opts: {\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n attributeToRemoveName: string;\n }) => void;\n deleteComponent(uid: Internal.UID.Component): void;\n deleteContentType(uid: Internal.UID.ContentType): void;\n contentStructure: ContentStructure;\n createFolder: (opts: { section: SectionKey; name: string; parentId: string | null }) => void;\n renameFolder: (opts: { section: SectionKey; id: string; name: string }) => void;\n moveFolder: (opts: {\n section: SectionKey;\n id: string;\n newParentId: string | null;\n index?: number;\n }) => void;\n deleteFolderOnly: (opts: { section: SectionKey; id: string }) => void;\n deleteFolderAndContent: (opts: {\n section: SectionKey;\n id: string;\n contentTypeUids: Internal.UID.ContentType[];\n }) => void;\n assignContentTypeToFolder: (opts: {\n section: SectionKey;\n uid: Internal.UID.ContentType;\n targetGroupId: string | null;\n index?: number;\n }) => void;\n reorderFolderChildren: (opts: {\n section: SectionKey;\n groupId: string;\n from: number;\n to: number;\n }) => void;\n removeComponentFromDynamicZone: (opts: {\n forTarget: Struct.ModelType;\n targetUid: Internal.UID.Schema;\n dzName: string;\n componentToRemoveIndex: number;\n }) => void;\n sortedContentTypesList: {\n visible: ContentType['visible'];\n name: ContentType['uid'];\n title: ContentType['info']['displayName'];\n plugin: ContentType['plugin'];\n uid: ContentType['uid'];\n to: string;\n kind: ContentType['kind'];\n restrictRelationsTo: ContentType['restrictRelationsTo'];\n status: ContentType['status'];\n }[];\n updateComponentSchema: (opts: {\n data: {\n icon?: string;\n displayName: string;\n };\n componentUID: Internal.UID.Component;\n }) => void;\n updateComponentUid: (opts: {\n newComponentUID: Internal.UID.Component;\n componentUID: Internal.UID.Component;\n }) => void;\n updateSchema: (opts: {\n data: {\n displayName: string;\n kind: Struct.ContentTypeKind;\n draftAndPublish: boolean;\n pluginOptions: Record<string, unknown>;\n };\n uid: Internal.UID.ContentType;\n folder?: FolderSelection;\n }) => void;\n initialComponents: Record<Internal.UID.Component, Component>;\n components: Record<Internal.UID.Component, Component>;\n componentsGroupedByCategory: Record<string, Component[]>;\n componentsThatHaveOtherComponentInTheirAttributes: ComponentWithChildren[];\n initialContentTypes: Record<Internal.UID.ContentType, ContentType>;\n contentTypes: Record<Internal.UID.ContentType, ContentType>;\n isInDevelopmentMode?: boolean;\n nestedComponents: NestedComponent[];\n reservedNames: {\n models: string[];\n attributes: string[];\n };\n allComponentsCategories: string[];\n saveSchema(): Promise<void>;\n isModified: boolean;\n isSaving: boolean;\n /**\n * Applies a whole-type change (AI chat). Rename hops declared by the change\n * go through the same preservation consent as manual renames, so this may\n * prompt the user. Resolves `false` when the user cancelled and nothing was\n * applied.\n */\n applyChange: (opts: {\n action: 'add' | 'update' | 'delete';\n schema: ContentType | Component;\n }) => Promise<boolean>;\n history: {\n undo(): void;\n redo(): void;\n discardAllChanges(): void;\n canUndo: boolean;\n canRedo: boolean;\n canDiscardAll: boolean;\n };\n}\n\n// @ts-expect-error need to pass initial value to params\nexport const DataManagerContext = createContext<DataManagerContextValue>();\n"],"names":["DataManagerContext","createContext"],"mappings":";;AAuMA;AACO,MAAMA,qBAAqBC,aAAAA;;;;"}
@@ -6,6 +6,8 @@ var strapiAdmin = require('@strapi/admin/strapi-admin');
6
6
  var groupBy = require('lodash/groupBy');
7
7
  var isEqual = require('lodash/isEqual');
8
8
  var mapValues = require('lodash/mapValues');
9
+ var omit = require('lodash/omit');
10
+ var uniq = require('lodash/uniq');
9
11
  var reactIntl = require('react-intl');
10
12
  var reactRedux = require('react-redux');
11
13
  var reactRouterDom = require('react-router-dom');
@@ -17,12 +19,16 @@ var useCTBTracking = require('../CTBSession/useCTBTracking.js');
17
19
  var useFormModalNavigation = require('../FormModalNavigation/useFormModalNavigation.js');
18
20
  var DataManagerContext = require('./DataManagerContext.js');
19
21
  var reducer = require('./reducer.js');
22
+ var RenameMigrationModal = require('./RenameMigrationModal.js');
20
23
  var useServerRestartWatcher = require('./useServerRestartWatcher.js');
21
24
  var cleanData = require('./utils/cleanData.js');
22
25
  var contentStructure = require('./utils/contentStructure.js');
26
+ var groupRenameChains = require('./utils/groupRenameChains.js');
27
+ var resolveAfterEditRenameConsent = require('./utils/resolveAfterEditRenameConsent.js');
23
28
  var retrieveComponentsThatHaveComponents = require('./utils/retrieveComponentsThatHaveComponents.js');
24
29
  var retrieveNestedComponents = require('./utils/retrieveNestedComponents.js');
25
30
  var retrieveSpecificInfoFromComponents = require('./utils/retrieveSpecificInfoFromComponents.js');
31
+ var splitChainsByInheritedConsent = require('./utils/splitChainsByInheritedConsent.js');
26
32
 
27
33
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
28
34
 
@@ -48,6 +54,8 @@ var React__namespace = /*#__PURE__*/_interopNamespace(React);
48
54
  var groupBy__default = /*#__PURE__*/_interopDefault(groupBy);
49
55
  var isEqual__default = /*#__PURE__*/_interopDefault(isEqual);
50
56
  var mapValues__default = /*#__PURE__*/_interopDefault(mapValues);
57
+ var omit__default = /*#__PURE__*/_interopDefault(omit);
58
+ var uniq__default = /*#__PURE__*/_interopDefault(uniq);
51
59
 
52
60
  /**
53
61
  * Including the new folder state in the same payload as create/edit transactions ensures the
@@ -78,7 +86,9 @@ const selectState = (state)=>state['content-type-builder_dataManagerProvider'] |
78
86
  'InitialData',
79
87
  'ContentTypesConfiguration',
80
88
  'ContentTypeSettings',
81
- 'ComponentConfiguration'
89
+ 'ComponentConfiguration',
90
+ // Existing cached documents still use the pre-change field names.
91
+ 'Document'
82
92
  ];
83
93
  const invalidateContentManagerSchemaCaches = ()=>strapiAdmin.adminApi.util.invalidateTags(CONTENT_MANAGER_SCHEMA_CACHE_TAGS);
84
94
  const DataManagerProvider = ({ children })=>{
@@ -100,6 +110,10 @@ const DataManagerProvider = ({ children })=>{
100
110
  const { onCloseModal } = useFormModalNavigation.useFormModalNavigation();
101
111
  const [isSaving, setIsSaving] = React__namespace.useState(false);
102
112
  const previousLocationRef = React__namespace.useRef(null);
113
+ const renameMigrationModeRef = React__namespace.useRef('prompt-before-save');
114
+ // When `modal` mode prompts the user, we hold the pending renames plus the
115
+ // promise resolver here so `saveSchema` can await the decision.
116
+ const [renameModal, setRenameModal] = React__namespace.useState(null);
103
117
  const isModified = React__namespace.useMemo(()=>{
104
118
  return !(isEqual__default.default(components, initialComponents) && isEqual__default.default(contentTypes, initialContentTypes) && isEqual__default.default(contentStructure$1, initialContentStructure));
105
119
  }, [
@@ -119,7 +133,10 @@ const DataManagerProvider = ({ children })=>{
119
133
  fetchClient.get(`/content-type-builder/schema`),
120
134
  fetchClient.get(`/content-type-builder/reserved-names`)
121
135
  ]);
122
- const { components, contentTypes, contentStructure: contentStructure$1 } = schemaResponse.data.data;
136
+ const { components, contentTypes, contentStructure: contentStructure$1, settings } = schemaResponse.data.data;
137
+ if (settings?.renameMigrations?.attributes) {
138
+ renameMigrationModeRef.current = settings.renameMigrations.attributes;
139
+ }
123
140
  dispatch(reducer.actions.init({
124
141
  components: mapValues__default.default(components, (component)=>({
125
142
  ...component,
@@ -198,6 +215,16 @@ const DataManagerProvider = ({ children })=>{
198
215
  const updatePermissions = async ()=>{
199
216
  await refetchPermissions();
200
217
  };
218
+ const requestRenameDecision = async (renames)=>{
219
+ const acceptedKeys = await new Promise((resolve)=>{
220
+ setRenameModal({
221
+ renames,
222
+ resolve
223
+ });
224
+ });
225
+ setRenameModal(null);
226
+ return acceptedKeys;
227
+ };
201
228
  const saveSchema = async ()=>{
202
229
  setIsSaving(true);
203
230
  const PluginForms = plugin?.apis?.forms;
@@ -211,6 +238,18 @@ const DataManagerProvider = ({ children })=>{
211
238
  contentStructure: state.current.contentStructure,
212
239
  initialContentStructure: state.current.initialContentStructure
213
240
  });
241
+ if (RenameMigrationModal.shouldPromptForRenamesBeforeSave(renameMigrationModeRef.current)) {
242
+ const pendingRenames = RenameMigrationModal.collectPendingRenames(requestData);
243
+ if (pendingRenames.length > 0) {
244
+ const acceptedKeys = await requestRenameDecision(pendingRenames);
245
+ // The user cancelled the whole save — return to editing untouched.
246
+ if (acceptedKeys === null) {
247
+ setIsSaving(false);
248
+ return;
249
+ }
250
+ RenameMigrationModal.applyRenameDecisions(requestData, acceptedKeys);
251
+ }
252
+ }
214
253
  // Track that the save button was clicked (includes session ID via useCTBTracking)
215
254
  trackUsage('willUpdateCTBSchema', {
216
255
  ...trackingEventProperties,
@@ -316,6 +355,7 @@ const DataManagerProvider = ({ children })=>{
316
355
  componentsGroupedByCategory,
317
356
  sortedContentTypesList,
318
357
  isLoading,
358
+ attributeRenameMigrationMode: renameMigrationModeRef.current,
319
359
  addAttribute (payload) {
320
360
  dispatch(reducer.actions.addAttribute({
321
361
  ...payload,
@@ -340,6 +380,49 @@ const DataManagerProvider = ({ children })=>{
340
380
  attributeToSet: payload.attributeToSet
341
381
  }));
342
382
  },
383
+ async confirmAttributeRenameMigration ({ forTarget, uid, oldName, newName }) {
384
+ if (oldName === newName) {
385
+ return true;
386
+ }
387
+ const schema = forTarget === 'contentType' ? contentTypes[uid] : components[uid];
388
+ // A field that was never saved has no data to preserve: accept without
389
+ // prompting so neither name ends up in `declinedRenameNames`.
390
+ const initialAttribute = schema?.attributes.find((attribute)=>attribute.name === oldName);
391
+ if (initialAttribute?.status === 'NEW') {
392
+ return true;
393
+ }
394
+ // Consent inherits along a chain: a hop joining an accepted chain is
395
+ // accepted, one touching a declined name is declined, neither prompts.
396
+ const consent = resolveAfterEditRenameConsent.resolveAfterEditRenameConsent({
397
+ renames: schema?.renames ?? [],
398
+ declinedRenameNames: schema?.declinedRenameNames ?? [],
399
+ oldName,
400
+ newName,
401
+ mode: renameMigrationModeRef.current
402
+ });
403
+ if (consent !== 'prompt') {
404
+ return consent === 'accept';
405
+ }
406
+ const key = `${uid}:edit`;
407
+ const acceptedKeys = await requestRenameDecision([
408
+ {
409
+ key,
410
+ uid,
411
+ typeName: schema?.info.displayName ?? uid,
412
+ pairs: [
413
+ {
414
+ oldName,
415
+ newName
416
+ }
417
+ ],
418
+ via: []
419
+ }
420
+ ]);
421
+ if (acceptedKeys === null) {
422
+ return null;
423
+ }
424
+ return acceptedKeys.has(key);
425
+ },
343
426
  addCreatedComponentToDynamicZone (payload) {
344
427
  dispatch(reducer.actions.addCreatedComponentToDynamicZone(payload));
345
428
  },
@@ -445,8 +528,78 @@ const DataManagerProvider = ({ children })=>{
445
528
  moveAttribute (args) {
446
529
  dispatch(reducer.actions.moveAttribute(args));
447
530
  },
448
- applyChange (args) {
449
- dispatch(reducer.actions.applyChange(args));
531
+ async applyChange ({ action, schema }) {
532
+ const renames = schema.renames ?? [];
533
+ if (action !== 'update' || renames.length === 0) {
534
+ dispatch(reducer.actions.applyChange({
535
+ action,
536
+ schema
537
+ }));
538
+ return true;
539
+ }
540
+ const mode = renameMigrationModeRef.current;
541
+ if (mode === 'never') {
542
+ dispatch(reducer.actions.applyChange({
543
+ action,
544
+ schema: omit__default.default(schema, [
545
+ 'renames'
546
+ ])
547
+ }));
548
+ return true;
549
+ }
550
+ // 'always' | 'prompt-before-save': the before-save modal decides per chain.
551
+ if (mode !== 'prompt-after-edit') {
552
+ dispatch(reducer.actions.applyChange({
553
+ action,
554
+ schema
555
+ }));
556
+ return true;
557
+ }
558
+ // 'prompt-after-edit': decide per chain now, inheriting consent already
559
+ // given (or refused) for the hops recorded on the type.
560
+ const existing = schema.modelType === 'contentType' ? contentTypes[schema.uid] : components[schema.uid];
561
+ const chains = groupRenameChains.groupRenameChains(schema.uid, renames);
562
+ const { keep, prompt } = splitChainsByInheritedConsent.splitChainsByInheritedConsent({
563
+ recorded: existing?.renames ?? [],
564
+ declinedRenameNames: existing?.declinedRenameNames ?? [],
565
+ chains,
566
+ renames
567
+ });
568
+ const accepted = new Set(keep.map((chain)=>chain.id));
569
+ if (prompt.length > 0) {
570
+ const typeName = schema.info?.displayName ?? schema.uid;
571
+ const decision = await requestRenameDecision(prompt.map((chain)=>RenameMigrationModal.toPendingRename(chain, renames, {
572
+ uid: schema.uid,
573
+ typeName
574
+ })));
575
+ // The user cancelled: apply nothing.
576
+ if (decision === null) {
577
+ return false;
578
+ }
579
+ decision.forEach((id)=>accepted.add(id));
580
+ }
581
+ const declinedChains = chains.filter((chain)=>!accepted.has(chain.id));
582
+ const keptRenames = RenameMigrationModal.filterRenamesByAcceptedChains(schema.uid, renames, accepted);
583
+ const declinedRenameNames = uniq__default.default([
584
+ ...schema.declinedRenameNames ?? [],
585
+ ...splitChainsByInheritedConsent.namesOfChains(declinedChains, renames)
586
+ ]);
587
+ dispatch(reducer.actions.applyChange({
588
+ action,
589
+ schema: {
590
+ ...omit__default.default(schema, [
591
+ 'renames',
592
+ 'declinedRenameNames'
593
+ ]),
594
+ ...keptRenames.length > 0 ? {
595
+ renames: keptRenames
596
+ } : {},
597
+ ...declinedRenameNames.length > 0 ? {
598
+ declinedRenameNames
599
+ } : {}
600
+ }
601
+ }));
602
+ return true;
450
603
  },
451
604
  history: {
452
605
  undo () {
@@ -463,9 +616,16 @@ const DataManagerProvider = ({ children })=>{
463
616
  canDiscardAll: isModified
464
617
  }
465
618
  };
466
- return /*#__PURE__*/ jsxRuntime.jsx(DataManagerContext.DataManagerContext.Provider, {
619
+ return /*#__PURE__*/ jsxRuntime.jsxs(DataManagerContext.DataManagerContext.Provider, {
467
620
  value: context,
468
- children: children
621
+ children: [
622
+ children,
623
+ renameModal && /*#__PURE__*/ jsxRuntime.jsx(RenameMigrationModal.RenameMigrationModal, {
624
+ renames: renameModal.renames,
625
+ onConfirm: (acceptedKeys)=>renameModal.resolve(acceptedKeys),
626
+ onCancel: ()=>renameModal.resolve(null)
627
+ })
628
+ ]
469
629
  });
470
630
  };
471
631