@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.
- package/dist/admin/components/AIChat/lib/transforms/schemas/toCTB.js +117 -13
- package/dist/admin/components/AIChat/lib/transforms/schemas/toCTB.js.map +1 -1
- package/dist/admin/components/AIChat/lib/transforms/schemas/toCTB.mjs +118 -13
- package/dist/admin/components/AIChat/lib/transforms/schemas/toCTB.mjs.map +1 -1
- package/dist/admin/components/AIChat/providers/SchemaProvider.js +31 -19
- package/dist/admin/components/AIChat/providers/SchemaProvider.js.map +1 -1
- package/dist/admin/components/AIChat/providers/SchemaProvider.mjs +31 -19
- package/dist/admin/components/AIChat/providers/SchemaProvider.mjs.map +1 -1
- package/dist/admin/components/DataManager/DataManagerContext.js.map +1 -1
- package/dist/admin/components/DataManager/DataManagerContext.mjs.map +1 -1
- package/dist/admin/components/DataManager/DataManagerProvider.js +166 -6
- package/dist/admin/components/DataManager/DataManagerProvider.js.map +1 -1
- package/dist/admin/components/DataManager/DataManagerProvider.mjs +165 -7
- package/dist/admin/components/DataManager/DataManagerProvider.mjs.map +1 -1
- package/dist/admin/components/DataManager/RenameMigrationModal.js +285 -0
- package/dist/admin/components/DataManager/RenameMigrationModal.js.map +1 -0
- package/dist/admin/components/DataManager/RenameMigrationModal.mjs +257 -0
- package/dist/admin/components/DataManager/RenameMigrationModal.mjs.map +1 -0
- package/dist/admin/components/DataManager/reducer.js +107 -2
- package/dist/admin/components/DataManager/reducer.js.map +1 -1
- package/dist/admin/components/DataManager/reducer.mjs +106 -2
- package/dist/admin/components/DataManager/reducer.mjs.map +1 -1
- package/dist/admin/components/DataManager/utils/cleanData.js +8 -1
- package/dist/admin/components/DataManager/utils/cleanData.js.map +1 -1
- package/dist/admin/components/DataManager/utils/cleanData.mjs +8 -1
- package/dist/admin/components/DataManager/utils/cleanData.mjs.map +1 -1
- package/dist/admin/components/DataManager/utils/groupRenameChains.js +86 -0
- package/dist/admin/components/DataManager/utils/groupRenameChains.js.map +1 -0
- package/dist/admin/components/DataManager/utils/groupRenameChains.mjs +84 -0
- package/dist/admin/components/DataManager/utils/groupRenameChains.mjs.map +1 -0
- package/dist/admin/components/DataManager/utils/isStorageCompatibleRename.js +29 -0
- package/dist/admin/components/DataManager/utils/isStorageCompatibleRename.js.map +1 -0
- package/dist/admin/components/DataManager/utils/isStorageCompatibleRename.mjs +27 -0
- package/dist/admin/components/DataManager/utils/isStorageCompatibleRename.mjs.map +1 -0
- package/dist/admin/components/DataManager/utils/resolveAfterEditRenameConsent.js +35 -0
- package/dist/admin/components/DataManager/utils/resolveAfterEditRenameConsent.js.map +1 -0
- package/dist/admin/components/DataManager/utils/resolveAfterEditRenameConsent.mjs +33 -0
- package/dist/admin/components/DataManager/utils/resolveAfterEditRenameConsent.mjs.map +1 -0
- package/dist/admin/components/DataManager/utils/splitChainsByInheritedConsent.js +48 -0
- package/dist/admin/components/DataManager/utils/splitChainsByInheritedConsent.js.map +1 -0
- package/dist/admin/components/DataManager/utils/splitChainsByInheritedConsent.mjs +45 -0
- package/dist/admin/components/DataManager/utils/splitChainsByInheritedConsent.mjs.map +1 -0
- package/dist/admin/components/FormModal/FormModal.js +142 -8
- package/dist/admin/components/FormModal/FormModal.js.map +1 -1
- package/dist/admin/components/FormModal/FormModal.mjs +143 -9
- package/dist/admin/components/FormModal/FormModal.mjs.map +1 -1
- package/dist/admin/components/FormModal/attributes/validation/common.js +6 -5
- package/dist/admin/components/FormModal/attributes/validation/common.js.map +1 -1
- package/dist/admin/components/FormModal/attributes/validation/common.mjs +1 -1
- package/dist/admin/components/FormModal/attributes/validation/common.mjs.map +1 -1
- package/dist/admin/components/FormModal/component/createComponentSchema.js +10 -7
- package/dist/admin/components/FormModal/component/createComponentSchema.js.map +1 -1
- package/dist/admin/components/FormModal/component/createComponentSchema.mjs +1 -1
- package/dist/admin/components/FormModal/component/createComponentSchema.mjs.map +1 -1
- package/dist/admin/components/FormModal/contentType/createContentTypeSchema.js +24 -21
- package/dist/admin/components/FormModal/contentType/createContentTypeSchema.js.map +1 -1
- package/dist/admin/components/FormModal/contentType/createContentTypeSchema.mjs +1 -1
- package/dist/admin/components/FormModal/contentType/createContentTypeSchema.mjs.map +1 -1
- package/dist/admin/components/FormModal/forms/utils/createCollectionName.js +3 -2
- package/dist/admin/components/FormModal/forms/utils/createCollectionName.js.map +1 -1
- package/dist/admin/components/FormModal/forms/utils/createCollectionName.mjs +1 -1
- package/dist/admin/components/FormModal/forms/utils/createCollectionName.mjs.map +1 -1
- package/dist/admin/components/FormModal/utils/getRenameStorageChange.js +73 -0
- package/dist/admin/components/FormModal/utils/getRenameStorageChange.js.map +1 -0
- package/dist/admin/components/FormModal/utils/getRenameStorageChange.mjs +70 -0
- package/dist/admin/components/FormModal/utils/getRenameStorageChange.mjs.map +1 -0
- package/dist/admin/src/components/AIChat/lib/transforms/schemas/toCTB.d.ts +4 -3
- package/dist/admin/src/components/AIChat/lib/types/schema.d.ts +6 -1
- package/dist/admin/src/components/DataManager/DataManagerContext.d.ts +28 -2
- package/dist/admin/src/components/DataManager/RenameMigrationModal.d.ts +73 -0
- package/dist/admin/src/components/DataManager/reducer.d.ts +17 -2
- package/dist/admin/src/components/DataManager/utils/cleanData.d.ts +16 -0
- package/dist/admin/src/components/DataManager/utils/groupRenameChains.d.ts +31 -0
- package/dist/admin/src/components/DataManager/utils/isStorageCompatibleRename.d.ts +10 -0
- package/dist/admin/src/components/DataManager/utils/resolveAfterEditRenameConsent.d.ts +24 -0
- package/dist/admin/src/components/DataManager/utils/splitChainsByInheritedConsent.d.ts +26 -0
- package/dist/admin/src/components/FormModal/utils/getRenameStorageChange.d.ts +40 -0
- package/dist/admin/src/types.d.ts +23 -0
- package/dist/admin/translations/en.json.js +12 -0
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +12 -0
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/server/config.js +24 -3
- package/dist/server/config.js.map +1 -1
- package/dist/server/config.mjs +21 -3
- package/dist/server/config.mjs.map +1 -1
- package/dist/server/controllers/schema.js +2 -2
- package/dist/server/controllers/schema.js.map +1 -1
- package/dist/server/controllers/schema.mjs +1 -1
- package/dist/server/controllers/schema.mjs.map +1 -1
- package/dist/server/controllers/validation/content-type.js +9 -11
- package/dist/server/controllers/validation/content-type.js.map +1 -1
- package/dist/server/controllers/validation/content-type.mjs +7 -9
- package/dist/server/controllers/validation/content-type.mjs.map +1 -1
- package/dist/server/controllers/validation/model-schema.js +2 -3
- package/dist/server/controllers/validation/model-schema.js.map +1 -1
- package/dist/server/controllers/validation/model-schema.mjs +1 -2
- package/dist/server/controllers/validation/model-schema.mjs.map +1 -1
- package/dist/server/controllers/validation/relations.js +1 -2
- package/dist/server/controllers/validation/relations.js.map +1 -1
- package/dist/server/controllers/validation/relations.mjs +1 -2
- package/dist/server/controllers/validation/relations.mjs.map +1 -1
- package/dist/server/controllers/validation/schema.js +42 -23
- package/dist/server/controllers/validation/schema.js.map +1 -1
- package/dist/server/controllers/validation/schema.mjs +43 -24
- package/dist/server/controllers/validation/schema.mjs.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/services/content-types.js +1 -2
- package/dist/server/services/content-types.js.map +1 -1
- package/dist/server/services/content-types.mjs +2 -3
- package/dist/server/services/content-types.mjs.map +1 -1
- package/dist/server/services/migration-builder/index.js +580 -0
- package/dist/server/services/migration-builder/index.js.map +1 -0
- package/dist/server/services/migration-builder/index.mjs +573 -0
- package/dist/server/services/migration-builder/index.mjs.map +1 -0
- package/dist/server/services/schema-builder/content-type-builder.js +31 -1
- package/dist/server/services/schema-builder/content-type-builder.js.map +1 -1
- package/dist/server/services/schema-builder/content-type-builder.mjs +31 -1
- package/dist/server/services/schema-builder/content-type-builder.mjs.map +1 -1
- package/dist/server/services/schema-mutation.js +30 -2
- package/dist/server/services/schema-mutation.js.map +1 -1
- package/dist/server/services/schema-mutation.mjs +10 -2
- package/dist/server/services/schema-mutation.mjs.map +1 -1
- package/dist/server/services/schema.js +259 -15
- package/dist/server/services/schema.js.map +1 -1
- package/dist/server/services/schema.mjs +252 -9
- package/dist/server/services/schema.mjs.map +1 -1
- package/dist/server/src/config.d.ts +23 -2
- package/dist/server/src/config.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/content-type.d.ts +0 -1
- package/dist/server/src/controllers/validation/content-type.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/relations.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/schema.d.ts +28 -0
- package/dist/server/src/controllers/validation/schema.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +10 -2
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/services/content-types.d.ts.map +1 -1
- package/dist/server/src/services/migration-builder/index.d.ts +83 -0
- package/dist/server/src/services/migration-builder/index.d.ts.map +1 -0
- package/dist/server/src/services/schema-builder/content-type-builder.d.ts.map +1 -1
- package/dist/server/src/services/schema-mutation.d.ts +4 -2
- package/dist/server/src/services/schema-mutation.d.ts.map +1 -1
- package/dist/server/src/services/schema.d.ts +19 -0
- package/dist/server/src/services/schema.d.ts.map +1 -1
- package/package.json +7 -6
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { Modal, Flex, Typography, Checkbox, Button } from '@strapi/design-system';
|
|
4
|
+
import { ArrowRight } from '@strapi/icons';
|
|
5
|
+
import { useIntl } from 'react-intl';
|
|
6
|
+
import { getTrad } from '../../utils/getTrad.mjs';
|
|
7
|
+
import { groupRenameChains } from './utils/groupRenameChains.mjs';
|
|
8
|
+
|
|
9
|
+
const getAttributeRenameDecision = (mode)=>{
|
|
10
|
+
if (mode === 'never') {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
if (mode === 'prompt-after-edit') {
|
|
14
|
+
return 'prompt';
|
|
15
|
+
}
|
|
16
|
+
return true;
|
|
17
|
+
};
|
|
18
|
+
const shouldPromptForRenamesBeforeSave = (mode)=>mode === 'prompt-before-save';
|
|
19
|
+
const RenameMigrationModal = ({ renames, onConfirm, onCancel })=>{
|
|
20
|
+
const { formatMessage } = useIntl();
|
|
21
|
+
const descriptionId = React.useId();
|
|
22
|
+
const [checkedKeys, setCheckedKeys] = React.useState(()=>new Set(renames.map((rename)=>rename.key)));
|
|
23
|
+
const chainNames = (rename)=>rename.pairs.map((pair)=>pair.oldName).join(', ');
|
|
24
|
+
const toggle = (key, checked)=>{
|
|
25
|
+
setCheckedKeys((previous)=>{
|
|
26
|
+
const next = new Set(previous);
|
|
27
|
+
if (checked) {
|
|
28
|
+
next.add(key);
|
|
29
|
+
} else {
|
|
30
|
+
next.delete(key);
|
|
31
|
+
}
|
|
32
|
+
return next;
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
return /*#__PURE__*/ jsx(Modal.Root, {
|
|
36
|
+
open: true,
|
|
37
|
+
onOpenChange: (open)=>!open && onCancel(),
|
|
38
|
+
children: /*#__PURE__*/ jsxs(Modal.Content, {
|
|
39
|
+
"aria-describedby": descriptionId,
|
|
40
|
+
children: [
|
|
41
|
+
/*#__PURE__*/ jsx(Modal.Header, {
|
|
42
|
+
children: /*#__PURE__*/ jsx(Modal.Title, {
|
|
43
|
+
children: formatMessage({
|
|
44
|
+
id: getTrad('migration.confirmation.title'),
|
|
45
|
+
defaultMessage: 'Preserve data for renamed fields?'
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
}),
|
|
49
|
+
/*#__PURE__*/ jsx(Modal.Body, {
|
|
50
|
+
children: /*#__PURE__*/ jsxs(Flex, {
|
|
51
|
+
direction: "column",
|
|
52
|
+
alignItems: "stretch",
|
|
53
|
+
gap: 4,
|
|
54
|
+
children: [
|
|
55
|
+
/*#__PURE__*/ jsx(Typography, {
|
|
56
|
+
id: descriptionId,
|
|
57
|
+
variant: "omega",
|
|
58
|
+
textColor: "neutral600",
|
|
59
|
+
children: formatMessage({
|
|
60
|
+
id: getTrad('migration.confirmation.description'),
|
|
61
|
+
defaultMessage: 'You renamed the fields below. Strapi can generate a migration to preserve existing data by renaming the underlying database columns. Untick a rename to let those fields start empty instead.'
|
|
62
|
+
})
|
|
63
|
+
}),
|
|
64
|
+
/*#__PURE__*/ jsx(Flex, {
|
|
65
|
+
direction: "column",
|
|
66
|
+
alignItems: "stretch",
|
|
67
|
+
gap: 2,
|
|
68
|
+
children: renames.map((rename)=>/*#__PURE__*/ jsxs(Flex, {
|
|
69
|
+
justifyContent: "space-between",
|
|
70
|
+
alignItems: "center",
|
|
71
|
+
hasRadius: true,
|
|
72
|
+
borderColor: "neutral200",
|
|
73
|
+
padding: 3,
|
|
74
|
+
gap: 3,
|
|
75
|
+
children: [
|
|
76
|
+
/*#__PURE__*/ jsxs(Flex, {
|
|
77
|
+
direction: "column",
|
|
78
|
+
alignItems: "start",
|
|
79
|
+
gap: 1,
|
|
80
|
+
children: [
|
|
81
|
+
rename.pairs.map((pair)=>/*#__PURE__*/ jsxs(Flex, {
|
|
82
|
+
gap: 2,
|
|
83
|
+
alignItems: "center",
|
|
84
|
+
children: [
|
|
85
|
+
/*#__PURE__*/ jsx(Typography, {
|
|
86
|
+
variant: "omega",
|
|
87
|
+
fontWeight: "bold",
|
|
88
|
+
children: pair.oldName
|
|
89
|
+
}),
|
|
90
|
+
/*#__PURE__*/ jsx(ArrowRight, {
|
|
91
|
+
width: "1.2rem",
|
|
92
|
+
height: "1.2rem",
|
|
93
|
+
fill: "neutral500"
|
|
94
|
+
}),
|
|
95
|
+
/*#__PURE__*/ jsx(Typography, {
|
|
96
|
+
variant: "omega",
|
|
97
|
+
fontWeight: "bold",
|
|
98
|
+
children: pair.newName
|
|
99
|
+
})
|
|
100
|
+
]
|
|
101
|
+
}, `${pair.oldName}->${pair.newName}`)),
|
|
102
|
+
rename.isSwapBack && /*#__PURE__*/ jsx(Typography, {
|
|
103
|
+
variant: "pi",
|
|
104
|
+
textColor: "neutral600",
|
|
105
|
+
children: formatMessage({
|
|
106
|
+
id: getTrad('migration.confirmation.field.swap'),
|
|
107
|
+
defaultMessage: 'Fields swapped back to their original names; data still moves.'
|
|
108
|
+
})
|
|
109
|
+
}),
|
|
110
|
+
rename.via.length > 0 && /*#__PURE__*/ jsx(Typography, {
|
|
111
|
+
variant: "pi",
|
|
112
|
+
textColor: "neutral600",
|
|
113
|
+
children: formatMessage({
|
|
114
|
+
id: getTrad('migration.confirmation.field.via'),
|
|
115
|
+
defaultMessage: 'via {names}'
|
|
116
|
+
}, {
|
|
117
|
+
names: rename.via.join(', ')
|
|
118
|
+
})
|
|
119
|
+
}),
|
|
120
|
+
/*#__PURE__*/ jsx(Typography, {
|
|
121
|
+
variant: "pi",
|
|
122
|
+
textColor: "neutral600",
|
|
123
|
+
children: formatMessage({
|
|
124
|
+
id: getTrad('migration.confirmation.field.in'),
|
|
125
|
+
defaultMessage: 'in {typeName}'
|
|
126
|
+
}, {
|
|
127
|
+
typeName: rename.typeName
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
]
|
|
131
|
+
}),
|
|
132
|
+
/*#__PURE__*/ jsx(Checkbox, {
|
|
133
|
+
"aria-label": formatMessage({
|
|
134
|
+
id: getTrad('migration.confirmation.field.preserve'),
|
|
135
|
+
defaultMessage: 'Preserve data of {names} in {typeName}'
|
|
136
|
+
}, {
|
|
137
|
+
names: chainNames(rename),
|
|
138
|
+
typeName: rename.typeName
|
|
139
|
+
}),
|
|
140
|
+
checked: checkedKeys.has(rename.key),
|
|
141
|
+
onCheckedChange: (checked)=>toggle(rename.key, checked === true)
|
|
142
|
+
})
|
|
143
|
+
]
|
|
144
|
+
}, rename.key))
|
|
145
|
+
})
|
|
146
|
+
]
|
|
147
|
+
})
|
|
148
|
+
}),
|
|
149
|
+
/*#__PURE__*/ jsxs(Modal.Footer, {
|
|
150
|
+
children: [
|
|
151
|
+
/*#__PURE__*/ jsx(Modal.Close, {
|
|
152
|
+
children: /*#__PURE__*/ jsx(Button, {
|
|
153
|
+
variant: "tertiary",
|
|
154
|
+
onClick: onCancel,
|
|
155
|
+
children: formatMessage({
|
|
156
|
+
id: getTrad('migration.confirmation.cancel'),
|
|
157
|
+
defaultMessage: 'Cancel'
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
}),
|
|
161
|
+
/*#__PURE__*/ jsx(Button, {
|
|
162
|
+
variant: "danger-light",
|
|
163
|
+
onClick: ()=>onConfirm(new Set()),
|
|
164
|
+
children: formatMessage({
|
|
165
|
+
id: getTrad('migration.confirmation.decline'),
|
|
166
|
+
defaultMessage: "Don't preserve data"
|
|
167
|
+
})
|
|
168
|
+
}),
|
|
169
|
+
/*#__PURE__*/ jsx(Button, {
|
|
170
|
+
disabled: checkedKeys.size === 0,
|
|
171
|
+
onClick: ()=>onConfirm(checkedKeys),
|
|
172
|
+
children: formatMessage({
|
|
173
|
+
id: getTrad('migration.confirmation.confirm'),
|
|
174
|
+
defaultMessage: 'Preserve data'
|
|
175
|
+
})
|
|
176
|
+
})
|
|
177
|
+
]
|
|
178
|
+
})
|
|
179
|
+
]
|
|
180
|
+
})
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
const getTypeName = (entry)=>entry.displayName ?? entry.uid;
|
|
184
|
+
/**
|
|
185
|
+
* Builds the modal row for one chain. A chain whose net effect is empty (the
|
|
186
|
+
* user renamed fields back to their original names through a swap) still has
|
|
187
|
+
* to run every hop, so its raw hops are shown instead of an empty row.
|
|
188
|
+
*/ const toPendingRename = (chain, renames, { uid, typeName })=>{
|
|
189
|
+
const isSwapBack = chain.pairs.length === 0;
|
|
190
|
+
return {
|
|
191
|
+
key: chain.id,
|
|
192
|
+
uid,
|
|
193
|
+
typeName,
|
|
194
|
+
pairs: isSwapBack ? chain.hopIndexes.map((index)=>renames[index]) : chain.pairs,
|
|
195
|
+
via: chain.via,
|
|
196
|
+
...isSwapBack ? {
|
|
197
|
+
isSwapBack: true
|
|
198
|
+
} : {}
|
|
199
|
+
};
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* Groups the ordered per-type `renames` arrays in a request payload into
|
|
203
|
+
* chains for display, one `PendingRename` per chain, preserving order.
|
|
204
|
+
*/ const collectPendingRenames = (requestData)=>{
|
|
205
|
+
const items = [];
|
|
206
|
+
const visit = (entries)=>{
|
|
207
|
+
entries.forEach((entry)=>{
|
|
208
|
+
if (entry.action === 'update' && Array.isArray(entry.renames)) {
|
|
209
|
+
const { renames } = entry;
|
|
210
|
+
groupRenameChains(entry.uid, renames).forEach((chain)=>{
|
|
211
|
+
items.push(toPendingRename(chain, renames, {
|
|
212
|
+
uid: entry.uid,
|
|
213
|
+
typeName: getTypeName(entry)
|
|
214
|
+
}));
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
visit(requestData.contentTypes ?? []);
|
|
220
|
+
visit(requestData.components ?? []);
|
|
221
|
+
return items;
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* Keeps only the hops of `renames` that belong to an accepted chain. Filtering
|
|
225
|
+
* by index preserves the original order, and a chain is always kept or dropped
|
|
226
|
+
* as a whole, so the result can never be a truncated chain.
|
|
227
|
+
*/ const filterRenamesByAcceptedChains = (uid, renames, acceptedChainIds)=>{
|
|
228
|
+
const keptIndexes = new Set();
|
|
229
|
+
groupRenameChains(uid, renames).forEach((chain)=>{
|
|
230
|
+
if (acceptedChainIds.has(chain.id)) {
|
|
231
|
+
chain.hopIndexes.forEach((index)=>keptIndexes.add(index));
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
return renames.filter((_, index)=>keptIndexes.has(index));
|
|
235
|
+
};
|
|
236
|
+
/**
|
|
237
|
+
* Mutates the request payload so each type keeps only the rename chains the
|
|
238
|
+
* user accepted; types left with no accepted hops drop their `renames` entirely.
|
|
239
|
+
*/ const applyRenameDecisions = (requestData, acceptedKeys)=>{
|
|
240
|
+
const apply = (entries)=>{
|
|
241
|
+
entries.forEach((entry)=>{
|
|
242
|
+
if (entry.action === 'update' && Array.isArray(entry.renames)) {
|
|
243
|
+
const kept = filterRenamesByAcceptedChains(entry.uid, entry.renames, acceptedKeys);
|
|
244
|
+
if (kept.length > 0) {
|
|
245
|
+
entry.renames = kept;
|
|
246
|
+
} else {
|
|
247
|
+
delete entry.renames;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
};
|
|
252
|
+
apply(requestData.contentTypes ?? []);
|
|
253
|
+
apply(requestData.components ?? []);
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
export { RenameMigrationModal, applyRenameDecisions, collectPendingRenames, filterRenamesByAcceptedChains, getAttributeRenameDecision, shouldPromptForRenamesBeforeSave, toPendingRename };
|
|
257
|
+
//# sourceMappingURL=RenameMigrationModal.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RenameMigrationModal.mjs","sources":["../../../../admin/src/components/DataManager/RenameMigrationModal.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { Button, Checkbox, Flex, Modal, Typography } from '@strapi/design-system';\nimport { ArrowRight } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\n\nimport { getTrad } from '../../utils/getTrad';\n\nimport { groupRenameChains, type RenameChain, type RenamePair } from './utils/groupRenameChains';\n\nimport type { RenameHop } from '../../types';\n\nexport type AttributeRenameMigrationMode =\n | 'always'\n | 'never'\n | 'prompt-after-edit'\n | 'prompt-before-save';\n\nexport type AttributeRenameDecision = boolean | 'prompt';\n\nexport const getAttributeRenameDecision = (\n mode: AttributeRenameMigrationMode\n): AttributeRenameDecision => {\n if (mode === 'never') {\n return false;\n }\n\n if (mode === 'prompt-after-edit') {\n return 'prompt';\n }\n\n return true;\n};\n\nexport const shouldPromptForRenamesBeforeSave = (mode: AttributeRenameMigrationMode): boolean =>\n mode === 'prompt-before-save';\n\n/**\n * One rename *chain* on an existing type, shown as a single row in the\n * confirmation modal. Consent is given per chain, never per hop: replaying a\n * partial chain either fails or silently skips hops whose target column still\n * exists. `key` is the chain id (`${uid}:chain:${firstHopIndex}`) so a decision\n * maps back to every hop of that chain in the type's ordered `renames` array.\n */\nexport interface PendingRename {\n key: string;\n uid: string;\n typeName: string;\n /** Net effect of the chain (`old -> new`); the raw hops for a pure swap-back. */\n pairs: RenamePair[];\n /** Intermediate names the chain routes through (e.g. `tmp`). */\n via: string[];\n /** True when the chain's net effect is empty (fields swapped back) but data still moves. */\n isSwapBack?: boolean;\n}\n\ninterface RenameMigrationModalProps {\n renames: PendingRename[];\n /**\n * Resolves with the keys of the hops the user chose to preserve (every row\n * is checked by default), or an empty set for \"don't preserve data\".\n */\n onConfirm: (acceptedKeys: Set<string>) => void;\n /** Aborts the current edit or save and returns to editing. */\n onCancel: () => void;\n}\n\nconst RenameMigrationModal = ({ renames, onConfirm, onCancel }: RenameMigrationModalProps) => {\n const { formatMessage } = useIntl();\n const descriptionId = React.useId();\n\n const [checkedKeys, setCheckedKeys] = React.useState<Set<string>>(\n () => new Set(renames.map((rename) => rename.key))\n );\n\n const chainNames = (rename: PendingRename) => rename.pairs.map((pair) => pair.oldName).join(', ');\n\n const toggle = (key: string, checked: boolean) => {\n setCheckedKeys((previous) => {\n const next = new Set(previous);\n if (checked) {\n next.add(key);\n } else {\n next.delete(key);\n }\n return next;\n });\n };\n\n return (\n <Modal.Root open onOpenChange={(open) => !open && onCancel()}>\n <Modal.Content aria-describedby={descriptionId}>\n <Modal.Header>\n <Modal.Title>\n {formatMessage({\n id: getTrad('migration.confirmation.title'),\n defaultMessage: 'Preserve data for renamed fields?',\n })}\n </Modal.Title>\n </Modal.Header>\n <Modal.Body>\n <Flex direction=\"column\" alignItems=\"stretch\" gap={4}>\n <Typography id={descriptionId} variant=\"omega\" textColor=\"neutral600\">\n {formatMessage({\n id: getTrad('migration.confirmation.description'),\n defaultMessage:\n 'You renamed the fields below. Strapi can generate a migration to preserve existing data by renaming the underlying database columns. Untick a rename to let those fields start empty instead.',\n })}\n </Typography>\n <Flex direction=\"column\" alignItems=\"stretch\" gap={2}>\n {renames.map((rename) => (\n <Flex\n key={rename.key}\n justifyContent=\"space-between\"\n alignItems=\"center\"\n hasRadius\n borderColor=\"neutral200\"\n padding={3}\n gap={3}\n >\n <Flex direction=\"column\" alignItems=\"start\" gap={1}>\n {rename.pairs.map((pair) => (\n <Flex key={`${pair.oldName}->${pair.newName}`} gap={2} alignItems=\"center\">\n <Typography variant=\"omega\" fontWeight=\"bold\">\n {pair.oldName}\n </Typography>\n <ArrowRight width=\"1.2rem\" height=\"1.2rem\" fill=\"neutral500\" />\n <Typography variant=\"omega\" fontWeight=\"bold\">\n {pair.newName}\n </Typography>\n </Flex>\n ))}\n {rename.isSwapBack && (\n <Typography variant=\"pi\" textColor=\"neutral600\">\n {formatMessage({\n id: getTrad('migration.confirmation.field.swap'),\n defaultMessage:\n 'Fields swapped back to their original names; data still moves.',\n })}\n </Typography>\n )}\n {rename.via.length > 0 && (\n <Typography variant=\"pi\" textColor=\"neutral600\">\n {formatMessage(\n {\n id: getTrad('migration.confirmation.field.via'),\n defaultMessage: 'via {names}',\n },\n { names: rename.via.join(', ') }\n )}\n </Typography>\n )}\n <Typography variant=\"pi\" textColor=\"neutral600\">\n {formatMessage(\n {\n id: getTrad('migration.confirmation.field.in'),\n defaultMessage: 'in {typeName}',\n },\n { typeName: rename.typeName }\n )}\n </Typography>\n </Flex>\n <Checkbox\n aria-label={formatMessage(\n {\n id: getTrad('migration.confirmation.field.preserve'),\n defaultMessage: 'Preserve data of {names} in {typeName}',\n },\n { names: chainNames(rename), typeName: rename.typeName }\n )}\n checked={checkedKeys.has(rename.key)}\n onCheckedChange={(checked) => toggle(rename.key, checked === true)}\n />\n </Flex>\n ))}\n </Flex>\n </Flex>\n </Modal.Body>\n <Modal.Footer>\n <Modal.Close>\n <Button variant=\"tertiary\" onClick={onCancel}>\n {formatMessage({\n id: getTrad('migration.confirmation.cancel'),\n defaultMessage: 'Cancel',\n })}\n </Button>\n </Modal.Close>\n <Button variant=\"danger-light\" onClick={() => onConfirm(new Set())}>\n {formatMessage({\n id: getTrad('migration.confirmation.decline'),\n defaultMessage: \"Don't preserve data\",\n })}\n </Button>\n <Button disabled={checkedKeys.size === 0} onClick={() => onConfirm(checkedKeys)}>\n {formatMessage({\n id: getTrad('migration.confirmation.confirm'),\n defaultMessage: 'Preserve data',\n })}\n </Button>\n </Modal.Footer>\n </Modal.Content>\n </Modal.Root>\n );\n};\n\nexport { RenameMigrationModal };\n\nconst getTypeName = (entry: { displayName?: string; uid: string }): string =>\n entry.displayName ?? entry.uid;\n\ntype RenameAwareEntry = {\n action?: string;\n uid: string;\n displayName?: string;\n renames?: RenameHop[];\n};\n\n/**\n * Builds the modal row for one chain. A chain whose net effect is empty (the\n * user renamed fields back to their original names through a swap) still has\n * to run every hop, so its raw hops are shown instead of an empty row.\n */\nexport const toPendingRename = (\n chain: RenameChain,\n renames: RenameHop[],\n { uid, typeName }: { uid: string; typeName: string }\n): PendingRename => {\n const isSwapBack = chain.pairs.length === 0;\n\n return {\n key: chain.id,\n uid,\n typeName,\n pairs: isSwapBack ? chain.hopIndexes.map((index) => renames[index]) : chain.pairs,\n via: chain.via,\n ...(isSwapBack ? { isSwapBack: true } : {}),\n };\n};\n\n/**\n * Groups the ordered per-type `renames` arrays in a request payload into\n * chains for display, one `PendingRename` per chain, preserving order.\n */\nexport const collectPendingRenames = (requestData: {\n contentTypes: RenameAwareEntry[];\n components: RenameAwareEntry[];\n}): PendingRename[] => {\n const items: PendingRename[] = [];\n\n const visit = (entries: RenameAwareEntry[]) => {\n entries.forEach((entry) => {\n if (entry.action === 'update' && Array.isArray(entry.renames)) {\n const { renames } = entry;\n groupRenameChains(entry.uid, renames).forEach((chain) => {\n items.push(\n toPendingRename(chain, renames, { uid: entry.uid, typeName: getTypeName(entry) })\n );\n });\n }\n });\n };\n\n visit(requestData.contentTypes ?? []);\n visit(requestData.components ?? []);\n\n return items;\n};\n\n/**\n * Keeps only the hops of `renames` that belong to an accepted chain. Filtering\n * by index preserves the original order, and a chain is always kept or dropped\n * as a whole, so the result can never be a truncated chain.\n */\nexport const filterRenamesByAcceptedChains = (\n uid: string,\n renames: RenameHop[],\n acceptedChainIds: Set<string>\n): RenameHop[] => {\n const keptIndexes = new Set<number>();\n groupRenameChains(uid, renames).forEach((chain) => {\n if (acceptedChainIds.has(chain.id)) {\n chain.hopIndexes.forEach((index) => keptIndexes.add(index));\n }\n });\n\n return renames.filter((_, index) => keptIndexes.has(index));\n};\n\n/**\n * Mutates the request payload so each type keeps only the rename chains the\n * user accepted; types left with no accepted hops drop their `renames` entirely.\n */\nexport const applyRenameDecisions = (\n requestData: { contentTypes: RenameAwareEntry[]; components: RenameAwareEntry[] },\n acceptedKeys: Set<string>\n): void => {\n const apply = (entries: RenameAwareEntry[]) => {\n entries.forEach((entry) => {\n if (entry.action === 'update' && Array.isArray(entry.renames)) {\n const kept = filterRenamesByAcceptedChains(entry.uid, entry.renames, acceptedKeys);\n if (kept.length > 0) {\n entry.renames = kept;\n } else {\n delete entry.renames;\n }\n }\n });\n };\n\n apply(requestData.contentTypes ?? []);\n apply(requestData.components ?? []);\n};\n"],"names":["getAttributeRenameDecision","mode","shouldPromptForRenamesBeforeSave","RenameMigrationModal","renames","onConfirm","onCancel","formatMessage","useIntl","descriptionId","React","useId","checkedKeys","setCheckedKeys","useState","Set","map","rename","key","chainNames","pairs","pair","oldName","join","toggle","checked","previous","next","add","delete","_jsx","Modal","Root","open","onOpenChange","_jsxs","Content","aria-describedby","Header","Title","id","getTrad","defaultMessage","Body","Flex","direction","alignItems","gap","Typography","variant","textColor","justifyContent","hasRadius","borderColor","padding","fontWeight","ArrowRight","width","height","fill","newName","isSwapBack","via","length","names","typeName","Checkbox","aria-label","has","onCheckedChange","Footer","Close","Button","onClick","disabled","size","getTypeName","entry","displayName","uid","toPendingRename","chain","hopIndexes","index","collectPendingRenames","requestData","items","visit","entries","forEach","action","Array","isArray","groupRenameChains","push","contentTypes","components","filterRenamesByAcceptedChains","acceptedChainIds","keptIndexes","filter","_","applyRenameDecisions","acceptedKeys","apply","kept"],"mappings":"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"}
|
|
@@ -2,16 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
var merge = require('lodash/merge');
|
|
4
4
|
var omit = require('lodash/omit');
|
|
5
|
+
var uniq = require('lodash/uniq');
|
|
5
6
|
var applyPrivateSearchDefault = require('../../utils/applyPrivateSearchDefault.js');
|
|
6
7
|
var getRelationType = require('../../utils/getRelationType.js');
|
|
7
8
|
var makeUnique = require('../../utils/makeUnique.js');
|
|
8
9
|
var undoRedo = require('./undoRedo.js');
|
|
9
10
|
var contentStructure = require('./utils/contentStructure.js');
|
|
11
|
+
var isStorageCompatibleRename = require('./utils/isStorageCompatibleRename.js');
|
|
10
12
|
|
|
11
13
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
14
|
|
|
13
15
|
var merge__default = /*#__PURE__*/_interopDefault(merge);
|
|
14
16
|
var omit__default = /*#__PURE__*/_interopDefault(omit);
|
|
17
|
+
var uniq__default = /*#__PURE__*/_interopDefault(uniq);
|
|
15
18
|
|
|
16
19
|
const initialState = {
|
|
17
20
|
components: {},
|
|
@@ -80,6 +83,65 @@ const createAttribute = (properties)=>{
|
|
|
80
83
|
status: 'NEW'
|
|
81
84
|
};
|
|
82
85
|
};
|
|
86
|
+
/**
|
|
87
|
+
* Records an attribute rename hop in the order the user performed it, so the
|
|
88
|
+
* server can replay the exact path as a data-preserving migration.
|
|
89
|
+
*
|
|
90
|
+
* - Only renames of fields that already exist in the database are recorded; a
|
|
91
|
+
* brand-new field (status NEW) has no data yet, so its renames are ignored.
|
|
92
|
+
* - A rename that also changes the field's type / relation / component is not
|
|
93
|
+
* recorded (see `isStorageCompatibleRename`).
|
|
94
|
+
* - Each hop (`previousName -> newName`) is appended verbatim. The recorded
|
|
95
|
+
* sequence is inherently collision-free because the CTB never allows two
|
|
96
|
+
* fields to share a name — a "swap" is expressed through the user's own
|
|
97
|
+
* intermediate-name hop, so no synthetic temp column is ever needed.
|
|
98
|
+
*/ const recordRename = (type, previousAttribute, newAttribute)=>{
|
|
99
|
+
if (previousAttribute.status === 'NEW') {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const oldName = previousAttribute.name;
|
|
103
|
+
const newName = newAttribute?.name;
|
|
104
|
+
if (!newName || oldName === newName) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (!isStorageCompatibleRename.isStorageCompatibleRename(previousAttribute, newAttribute)) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (!type.renames) {
|
|
111
|
+
type.renames = [];
|
|
112
|
+
}
|
|
113
|
+
type.renames.push({
|
|
114
|
+
oldName,
|
|
115
|
+
newName
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Remembers the names of a rename hop the user declined to preserve data for,
|
|
120
|
+
* so the rest of that chain is declined without another prompt.
|
|
121
|
+
*/ const recordDeclinedRename = (type, previousAttribute, newAttribute)=>{
|
|
122
|
+
const oldName = previousAttribute.name;
|
|
123
|
+
const newName = newAttribute?.name;
|
|
124
|
+
if (!newName || oldName === newName) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
type.declinedRenameNames = uniq__default.default([
|
|
128
|
+
...type.declinedRenameNames ?? [],
|
|
129
|
+
oldName,
|
|
130
|
+
newName
|
|
131
|
+
]);
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Applies the rename consent carried by an edit: either records the hop or
|
|
135
|
+
* remembers it as declined. Never both.
|
|
136
|
+
*/ const applyRenameConsent = (type, previousAttribute, newAttribute, { recordRename: shouldRecordRename = true, declineRename: shouldDecline = false })=>{
|
|
137
|
+
if (shouldDecline) {
|
|
138
|
+
recordDeclinedRename(type, previousAttribute, newAttribute);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (shouldRecordRename) {
|
|
142
|
+
recordRename(type, previousAttribute, newAttribute);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
83
145
|
const setAttributeAt = (type, index, attribute)=>{
|
|
84
146
|
const previousAttribute = type.attributes[index];
|
|
85
147
|
const newStatus = getNewStatus(previousAttribute.status, 'CHANGED');
|
|
@@ -119,6 +181,30 @@ const removeAttributeByName = (type, name)=>{
|
|
|
119
181
|
setAttributeStatus(attr, 'REMOVED');
|
|
120
182
|
}
|
|
121
183
|
};
|
|
184
|
+
/**
|
|
185
|
+
* Carries the pending rename state of `existing` over to a full replacement of
|
|
186
|
+
* the type (the AI chat's `applyChange`). Appending the incoming hops after the
|
|
187
|
+
* existing ones is order-correct: the chat transform diffs against the current
|
|
188
|
+
* reducer entry, whose attributes already carry the manual renames, so its hops
|
|
189
|
+
* start from where the manual hops ended.
|
|
190
|
+
*/ const mergeRenameState = (existing, incoming)=>{
|
|
191
|
+
const renames = [
|
|
192
|
+
...existing?.renames ?? [],
|
|
193
|
+
...incoming.renames ?? []
|
|
194
|
+
];
|
|
195
|
+
const declinedRenameNames = uniq__default.default([
|
|
196
|
+
...existing?.declinedRenameNames ?? [],
|
|
197
|
+
...incoming.declinedRenameNames ?? []
|
|
198
|
+
]);
|
|
199
|
+
return {
|
|
200
|
+
...renames.length > 0 ? {
|
|
201
|
+
renames
|
|
202
|
+
} : {},
|
|
203
|
+
...declinedRenameNames.length > 0 ? {
|
|
204
|
+
declinedRenameNames
|
|
205
|
+
} : {}
|
|
206
|
+
};
|
|
207
|
+
};
|
|
122
208
|
const updateType = (type, data)=>{
|
|
123
209
|
merge__default.default(type, data);
|
|
124
210
|
setStatus(type, 'CHANGED');
|
|
@@ -408,7 +494,7 @@ const slice = undoRedo.createUndoRedoSlice({
|
|
|
408
494
|
attr.components = updatedComponents;
|
|
409
495
|
},
|
|
410
496
|
editAttribute: (state, action)=>{
|
|
411
|
-
const { name, attributeToSet, forTarget, targetUid } = action.payload;
|
|
497
|
+
const { name, attributeToSet, forTarget, targetUid, recordRename, declineRename, recordTargetRename, declineTargetRename } = action.payload;
|
|
412
498
|
const type = getType(state, {
|
|
413
499
|
forTarget,
|
|
414
500
|
targetUid
|
|
@@ -418,6 +504,10 @@ const slice = undoRedo.createUndoRedoSlice({
|
|
|
418
504
|
return;
|
|
419
505
|
}
|
|
420
506
|
const previousAttribute = type.attributes[initialAttributeIndex];
|
|
507
|
+
applyRenameConsent(type, previousAttribute, attributeToSet, {
|
|
508
|
+
recordRename,
|
|
509
|
+
declineRename
|
|
510
|
+
});
|
|
421
511
|
setAttributeAt(type, initialAttributeIndex, attributeToSet);
|
|
422
512
|
if (previousAttribute.type !== attributeToSet.type) {
|
|
423
513
|
return;
|
|
@@ -457,6 +547,14 @@ const slice = undoRedo.createUndoRedoSlice({
|
|
|
457
547
|
};
|
|
458
548
|
// create or recreate(at old index) targetAttribute
|
|
459
549
|
if (previousTargetAttributeIndex !== -1 && previousTarget.uid === newTarget.uid) {
|
|
550
|
+
// The counterpart is renamed on the target type: without a hop
|
|
551
|
+
// there, the side owning the join table would lose its links.
|
|
552
|
+
if (previousTargetAttribute) {
|
|
553
|
+
applyRenameConsent(newTarget, previousTargetAttribute, newTargetAttribute, {
|
|
554
|
+
recordRename: recordTargetRename,
|
|
555
|
+
declineRename: declineTargetRename
|
|
556
|
+
});
|
|
557
|
+
}
|
|
460
558
|
// re-create at previousIdx if possible
|
|
461
559
|
replaceAttributeAt(newTarget, previousTargetAttributeIndex, newTargetAttribute);
|
|
462
560
|
} else {
|
|
@@ -468,14 +566,19 @@ const slice = undoRedo.createUndoRedoSlice({
|
|
|
468
566
|
}
|
|
469
567
|
},
|
|
470
568
|
editCustomFieldAttribute: (state, action)=>{
|
|
471
|
-
const { forTarget, targetUid, name, attributeToSet } = action.payload;
|
|
569
|
+
const { forTarget, targetUid, name, attributeToSet, recordRename, declineRename } = action.payload;
|
|
472
570
|
const initialAttributeName = name;
|
|
473
571
|
const type = getType(state, {
|
|
474
572
|
forTarget,
|
|
475
573
|
targetUid
|
|
476
574
|
});
|
|
477
575
|
const initialAttributeIndex = findAttributeIndex(type, initialAttributeName);
|
|
576
|
+
const previousAttribute = type.attributes[initialAttributeIndex];
|
|
478
577
|
setAttributeAt(type, initialAttributeIndex, attributeToSet);
|
|
578
|
+
applyRenameConsent(type, previousAttribute, attributeToSet, {
|
|
579
|
+
recordRename,
|
|
580
|
+
declineRename
|
|
581
|
+
});
|
|
479
582
|
},
|
|
480
583
|
reloadPlugin: ()=>{
|
|
481
584
|
return initialState;
|
|
@@ -894,6 +997,7 @@ const slice = undoRedo.createUndoRedoSlice({
|
|
|
894
997
|
const component = state.components[uid];
|
|
895
998
|
state.components[uid] = {
|
|
896
999
|
...schema,
|
|
1000
|
+
...mergeRenameState(component, schema),
|
|
897
1001
|
status: component?.status === 'NEW' ? 'NEW' : schema.status
|
|
898
1002
|
};
|
|
899
1003
|
} else {
|
|
@@ -901,6 +1005,7 @@ const slice = undoRedo.createUndoRedoSlice({
|
|
|
901
1005
|
const previousKind = contentType?.kind;
|
|
902
1006
|
state.contentTypes[uid] = {
|
|
903
1007
|
...schema,
|
|
1008
|
+
...mergeRenameState(contentType, schema),
|
|
904
1009
|
status: contentType?.status === 'NEW' ? 'NEW' : schema.status
|
|
905
1010
|
};
|
|
906
1011
|
if (previousKind && schema.kind && schema.kind !== previousKind) {
|