multi-content-type-relation 2.1.2 → 2.2.1
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/README.md +6 -0
- package/dist/_chunks/{index-DdX2rVzB.mjs → index-BGSdnZnr.mjs} +37 -12
- package/dist/_chunks/index-Bu99PZdl.js +470 -0
- package/dist/_chunks/{index-BtldAbIW.js → index-CEXSrD4g.js} +40 -15
- package/dist/_chunks/index-DEkcD0zR.mjs +470 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/components/Input/InputContentSuggestions.d.ts +1 -2
- package/dist/admin/src/components/Input/TableItem.d.ts +2 -3
- package/dist/admin/src/helpers/content.d.ts +1 -1
- package/dist/server/index.js +130 -61
- package/dist/server/index.mjs +130 -61
- package/dist/server/src/controllers/controller.d.ts +1 -0
- package/dist/server/src/controllers/index.d.ts +1 -0
- package/dist/server/src/index.d.ts +1 -0
- package/dist/server/src/interface.d.ts +2 -0
- package/package.json +2 -1
- package/dist/_chunks/index-CyBD50Lc.js +0 -3567
- package/dist/_chunks/index-DGcImy9s.mjs +0 -3565
package/README.md
CHANGED
|
@@ -74,6 +74,12 @@ If, for some reasons, you want to hydrate the relations of the relations of the
|
|
|
74
74
|
|
|
75
75
|
This setting show debug log of the plugin for better understanding
|
|
76
76
|
|
|
77
|
+
###### Key: `disableRevertRelations`
|
|
78
|
+
|
|
79
|
+
> `required:` no | `type:` Boolean | default false
|
|
80
|
+
|
|
81
|
+
This setting disable relaction MCTR if needed for performances/stability concerns
|
|
82
|
+
|
|
77
83
|
## Submit an issue
|
|
78
84
|
|
|
79
85
|
You can use github issues to raise an issue about this plugin
|
|
@@ -51,15 +51,22 @@ const fetchMatchingContent = async (keyword, contentTypes, locale) => {
|
|
|
51
51
|
total
|
|
52
52
|
};
|
|
53
53
|
};
|
|
54
|
-
const formatToStrapiField = (entries) => {
|
|
54
|
+
const formatToStrapiField = (entries, locale) => {
|
|
55
55
|
if (entries.length === 0) return "";
|
|
56
|
-
|
|
57
|
-
entries.map((entry) =>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
const storedData = JSON.stringify(
|
|
57
|
+
entries.map((entry) => {
|
|
58
|
+
const obj = {
|
|
59
|
+
uid: entry.uid,
|
|
60
|
+
documentId: entry.item.documentId,
|
|
61
|
+
MRCT: true
|
|
62
|
+
};
|
|
63
|
+
if (locale) {
|
|
64
|
+
obj.locale = locale;
|
|
65
|
+
}
|
|
66
|
+
return obj;
|
|
67
|
+
}).filter(Boolean)
|
|
62
68
|
);
|
|
69
|
+
return storedData;
|
|
63
70
|
};
|
|
64
71
|
const validateCurrentRelations = async (entries) => {
|
|
65
72
|
const { post } = getFetchClient();
|
|
@@ -108,23 +115,41 @@ const SidePanel = () => {
|
|
|
108
115
|
model: uid,
|
|
109
116
|
isSingleType
|
|
110
117
|
} = unstable_useContentManagerContext();
|
|
118
|
+
const currentLocale = new URLSearchParams(location.search).get(
|
|
119
|
+
"plugins[i18n][locale]"
|
|
120
|
+
);
|
|
111
121
|
const [linkedContent, setLinkedContent] = useState([]);
|
|
112
|
-
const [loading, setLoading] = useState(
|
|
122
|
+
const [loading, setLoading] = useState(true);
|
|
123
|
+
const [disabled, setDisabledFeature] = useState(false);
|
|
113
124
|
useEffect(() => {
|
|
114
125
|
const fetchRevertRelations = async () => {
|
|
115
126
|
const { post } = getFetchClient();
|
|
116
127
|
const response = await post(`/${pluginId}/fetch-revert-relations`, {
|
|
117
128
|
documentId,
|
|
118
129
|
uid,
|
|
119
|
-
isSingleType
|
|
130
|
+
isSingleType,
|
|
131
|
+
locale: currentLocale
|
|
120
132
|
});
|
|
121
133
|
setLinkedContent(Array.isArray(response.data) ? response.data.filter(Boolean) : []);
|
|
122
134
|
setLoading(false);
|
|
123
135
|
};
|
|
124
|
-
|
|
136
|
+
const fetchConfiguration = async () => {
|
|
137
|
+
const { get } = getFetchClient();
|
|
138
|
+
const response = await get(`/${pluginId}/get-configuration`);
|
|
139
|
+
if (response.data.disableRevertRelations) {
|
|
140
|
+
setDisabledFeature(true);
|
|
141
|
+
setLoading(false);
|
|
142
|
+
} else {
|
|
143
|
+
fetchRevertRelations();
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
fetchConfiguration();
|
|
125
147
|
}, []);
|
|
126
148
|
if (loading)
|
|
127
149
|
return /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsx(Loader, {}) });
|
|
150
|
+
if (disabled) {
|
|
151
|
+
return /* @__PURE__ */ jsx("div", {});
|
|
152
|
+
}
|
|
128
153
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
129
154
|
/* @__PURE__ */ jsx(Divider, {}),
|
|
130
155
|
/* @__PURE__ */ jsxs(Box, { width: "100%", marginTop: 4, children: [
|
|
@@ -164,7 +189,7 @@ const SidePanel = () => {
|
|
|
164
189
|
/* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(
|
|
165
190
|
"a",
|
|
166
191
|
{
|
|
167
|
-
href: content.isSingleType ? `/admin/content-manager/single-types/${content.uid}` : `/admin/content-manager/collection-types/${content.uid}/${content.documentId}`,
|
|
192
|
+
href: content.isSingleType ? `/admin/content-manager/single-types/${content.uid}${currentLocale ? `?plugins[i18n][locale]=${currentLocale}` : ""}` : `/admin/content-manager/collection-types/${content.uid}/${content.documentId}${currentLocale ? `?plugins[i18n][locale]=${currentLocale}` : ""}`,
|
|
168
193
|
target: "_blank",
|
|
169
194
|
style: { textDecoration: "none" },
|
|
170
195
|
children: /* @__PURE__ */ jsx(
|
|
@@ -223,7 +248,7 @@ const index = {
|
|
|
223
248
|
components: {
|
|
224
249
|
Input: () => import(
|
|
225
250
|
/* webpackChunkName: "input-component" */
|
|
226
|
-
"./index-
|
|
251
|
+
"./index-DEkcD0zR.mjs"
|
|
227
252
|
)
|
|
228
253
|
},
|
|
229
254
|
inputSize: {
|
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const react = require("react");
|
|
5
|
+
const reactIntl = require("react-intl");
|
|
6
|
+
const reactRouterDom = require("react-router-dom");
|
|
7
|
+
const designSystem = require("@strapi/design-system");
|
|
8
|
+
const index = require("./index-CEXSrD4g.js");
|
|
9
|
+
const core = require("@dnd-kit/core");
|
|
10
|
+
const sortable = require("@dnd-kit/sortable");
|
|
11
|
+
const icons = require("@strapi/icons");
|
|
12
|
+
function useSearchedEntries(keyword, contentTypes, locale) {
|
|
13
|
+
const [loading, setLoading] = react.useState(false);
|
|
14
|
+
const [results, setResults] = react.useState([]);
|
|
15
|
+
const [total, setTotal] = react.useState(0);
|
|
16
|
+
async function fetchEntries() {
|
|
17
|
+
setResults([]);
|
|
18
|
+
setTotal(0);
|
|
19
|
+
if (loading || !keyword) return;
|
|
20
|
+
try {
|
|
21
|
+
const { data, total: total2 } = await index.fetchMatchingContent(
|
|
22
|
+
keyword,
|
|
23
|
+
contentTypes,
|
|
24
|
+
locale
|
|
25
|
+
);
|
|
26
|
+
setResults(data);
|
|
27
|
+
setTotal(total2);
|
|
28
|
+
} finally {
|
|
29
|
+
setLoading(false);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
react.useEffect(() => {
|
|
33
|
+
const timeout = setTimeout(() => fetchEntries(), 500);
|
|
34
|
+
return () => clearTimeout(timeout);
|
|
35
|
+
}, [keyword]);
|
|
36
|
+
return react.useMemo(
|
|
37
|
+
() => ({
|
|
38
|
+
loading,
|
|
39
|
+
results,
|
|
40
|
+
total
|
|
41
|
+
}),
|
|
42
|
+
[results, total]
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
const PublicationState = ({
|
|
46
|
+
isPublished,
|
|
47
|
+
hasDraftAndPublish
|
|
48
|
+
}) => {
|
|
49
|
+
const { translate } = index.useTranslate();
|
|
50
|
+
const configuration = react.useMemo(() => {
|
|
51
|
+
const conf = {
|
|
52
|
+
variant: "alternative",
|
|
53
|
+
text: translate("publicationState.na")
|
|
54
|
+
};
|
|
55
|
+
if (hasDraftAndPublish) {
|
|
56
|
+
conf.variant = isPublished ? "success" : "secondary";
|
|
57
|
+
conf.text = isPublished ? translate("publicationState.published") : translate("publicationState.draft");
|
|
58
|
+
}
|
|
59
|
+
return conf;
|
|
60
|
+
}, [isPublished, hasDraftAndPublish, translate]);
|
|
61
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
62
|
+
designSystem.Status,
|
|
63
|
+
{
|
|
64
|
+
showbullet: "false",
|
|
65
|
+
variant: configuration.variant,
|
|
66
|
+
size: "S",
|
|
67
|
+
width: "min-content",
|
|
68
|
+
style: { paddingLeft: 12, paddingRight: 12 },
|
|
69
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "bold", textColor: `${configuration.variant}700`, children: configuration.text })
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
const CSS = /* @__PURE__ */ Object.freeze({
|
|
74
|
+
Translate: {
|
|
75
|
+
toString(transform) {
|
|
76
|
+
if (!transform) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const {
|
|
80
|
+
x,
|
|
81
|
+
y
|
|
82
|
+
} = transform;
|
|
83
|
+
return "translate3d(" + (x ? Math.round(x) : 0) + "px, " + (y ? Math.round(y) : 0) + "px, 0)";
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
Scale: {
|
|
87
|
+
toString(transform) {
|
|
88
|
+
if (!transform) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const {
|
|
92
|
+
scaleX,
|
|
93
|
+
scaleY
|
|
94
|
+
} = transform;
|
|
95
|
+
return "scaleX(" + scaleX + ") scaleY(" + scaleY + ")";
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
Transform: {
|
|
99
|
+
toString(transform) {
|
|
100
|
+
if (!transform) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
return [CSS.Translate.toString(transform), CSS.Scale.toString(transform)].join(" ");
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
Transition: {
|
|
107
|
+
toString(_ref) {
|
|
108
|
+
let {
|
|
109
|
+
property,
|
|
110
|
+
duration,
|
|
111
|
+
easing
|
|
112
|
+
} = _ref;
|
|
113
|
+
return property + " " + duration + "ms " + easing;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
const TableItem = ({
|
|
118
|
+
entry,
|
|
119
|
+
id,
|
|
120
|
+
type,
|
|
121
|
+
disabled,
|
|
122
|
+
onAdd,
|
|
123
|
+
onDelete
|
|
124
|
+
}) => {
|
|
125
|
+
const { translate } = index.useTranslate();
|
|
126
|
+
const contentType = index.getContentTypeForUid(entry.uid);
|
|
127
|
+
const location = reactRouterDom.useLocation();
|
|
128
|
+
const { setDraggableNodeRef, setDroppableNodeRef, transform, transition, attributes, listeners } = sortable.useSortable({ id });
|
|
129
|
+
const style = {
|
|
130
|
+
transform: CSS.Transform.toString(transform),
|
|
131
|
+
transition
|
|
132
|
+
};
|
|
133
|
+
const [currentLocale, setCurrentLocale] = react.useState("");
|
|
134
|
+
react.useEffect(() => {
|
|
135
|
+
const searchParams = new URLSearchParams(location.search);
|
|
136
|
+
const locale = searchParams.get("plugins[i18n][locale]");
|
|
137
|
+
if (!locale) return;
|
|
138
|
+
setCurrentLocale(locale);
|
|
139
|
+
}, [location]);
|
|
140
|
+
const goToEntry = () => {
|
|
141
|
+
if (!currentLocale) return;
|
|
142
|
+
const contentTypes = window.sessionStorage.getItem("mctr::content_types");
|
|
143
|
+
if (contentTypes) {
|
|
144
|
+
try {
|
|
145
|
+
const parsedContentTypes = JSON.parse(contentTypes);
|
|
146
|
+
if (Array.isArray(parsedContentTypes)) {
|
|
147
|
+
const contentType2 = parsedContentTypes.find(
|
|
148
|
+
(ct) => ct.uid === entry.uid
|
|
149
|
+
);
|
|
150
|
+
if (contentType2) {
|
|
151
|
+
const kind = contentType2.kind;
|
|
152
|
+
let url = "";
|
|
153
|
+
if (kind === "collectionType") {
|
|
154
|
+
url = `/admin/content-manager/collection-types/${entry.uid}/${entry.item.documentId}`;
|
|
155
|
+
} else {
|
|
156
|
+
url = `/admin/content-manager/single-types/${entry.uid}`;
|
|
157
|
+
}
|
|
158
|
+
url += `?plugins[i18n][locale]=${currentLocale}`;
|
|
159
|
+
window.open(url, "_blank");
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
} catch (e) {
|
|
164
|
+
console.error("[MCTR] Failed to retrieve content types");
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
alert(translate("tableItem.error"));
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
171
|
+
"tr",
|
|
172
|
+
{
|
|
173
|
+
style,
|
|
174
|
+
ref: setDroppableNodeRef,
|
|
175
|
+
...attributes,
|
|
176
|
+
children: [
|
|
177
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: type === "selected" ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.IconButton, { ref: setDraggableNodeRef, ...listeners, children: /* @__PURE__ */ jsxRuntime.jsx(icons.Drag, {}) }) : null }),
|
|
178
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { color: "neutral800", children: entry.item[entry.searchableField] }) }),
|
|
179
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { color: "neutral800", children: entry.displayName }) }),
|
|
180
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
181
|
+
PublicationState,
|
|
182
|
+
{
|
|
183
|
+
isPublished: !!entry.item.publishedAt,
|
|
184
|
+
hasDraftAndPublish: contentType?.options?.draftAndPublish
|
|
185
|
+
}
|
|
186
|
+
) }),
|
|
187
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { children: [
|
|
188
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
189
|
+
designSystem.IconButton,
|
|
190
|
+
{
|
|
191
|
+
label: translate("tableItem.goToEntry"),
|
|
192
|
+
onClick: goToEntry,
|
|
193
|
+
style: { "marginRight": "5px" },
|
|
194
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(icons.Eye, {})
|
|
195
|
+
}
|
|
196
|
+
),
|
|
197
|
+
type === "suggestion" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
198
|
+
designSystem.IconButton,
|
|
199
|
+
{
|
|
200
|
+
label: translate("tableItem.add"),
|
|
201
|
+
onClick: () => onAdd(entry),
|
|
202
|
+
disabled,
|
|
203
|
+
marginLeft: 1,
|
|
204
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {})
|
|
205
|
+
}
|
|
206
|
+
) : type === "selected" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
207
|
+
designSystem.IconButton,
|
|
208
|
+
{
|
|
209
|
+
label: translate("tableItem.delete"),
|
|
210
|
+
onClick: () => onDelete(entry),
|
|
211
|
+
marginLeft: 1,
|
|
212
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
|
|
213
|
+
}
|
|
214
|
+
) : null
|
|
215
|
+
] }) })
|
|
216
|
+
]
|
|
217
|
+
}
|
|
218
|
+
);
|
|
219
|
+
};
|
|
220
|
+
function InputContentSuggestions({
|
|
221
|
+
uniqueId,
|
|
222
|
+
suggestions,
|
|
223
|
+
selected,
|
|
224
|
+
onAddEntry,
|
|
225
|
+
onDeleteEntry,
|
|
226
|
+
onEntriesSorted,
|
|
227
|
+
maximum
|
|
228
|
+
}) {
|
|
229
|
+
const { translate } = index.useTranslate();
|
|
230
|
+
const suggestionAsSelectedEntry = react.useMemo(() => {
|
|
231
|
+
return (suggestions || []).flatMap(
|
|
232
|
+
(suggestion) => suggestion.results.map((entrySuggestion) => ({
|
|
233
|
+
displayName: suggestion.displayName,
|
|
234
|
+
item: entrySuggestion,
|
|
235
|
+
searchableField: suggestion.searchableField,
|
|
236
|
+
uid: suggestion.uid
|
|
237
|
+
}))
|
|
238
|
+
).slice(0, 10);
|
|
239
|
+
}, [suggestions]);
|
|
240
|
+
const buildSelectedId = (entry) => {
|
|
241
|
+
return `${uniqueId}-${entry.uid}-${entry.item.documentId}`;
|
|
242
|
+
};
|
|
243
|
+
const availableSuggestions = react.useMemo(() => {
|
|
244
|
+
const selectedIdentifiers = (selected || []).map(buildSelectedId);
|
|
245
|
+
return suggestionAsSelectedEntry.filter(
|
|
246
|
+
(suggestion) => !selectedIdentifiers.includes(buildSelectedId(suggestion))
|
|
247
|
+
);
|
|
248
|
+
}, [suggestions, selected]);
|
|
249
|
+
const onAdd = (entry) => {
|
|
250
|
+
if (typeof onAddEntry === "function") {
|
|
251
|
+
onAddEntry(entry);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
const onDelete = (entry) => {
|
|
255
|
+
if (typeof onDeleteEntry === "function") {
|
|
256
|
+
onDeleteEntry(entry);
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
const onSort = (entries) => {
|
|
260
|
+
if (typeof onEntriesSorted === "function") {
|
|
261
|
+
onEntriesSorted(entries);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
const sensors = core.useSensors(
|
|
265
|
+
core.useSensor(core.PointerSensor)
|
|
266
|
+
);
|
|
267
|
+
const sortableItems = react.useMemo(() => {
|
|
268
|
+
const items = selected?.map((entry) => buildSelectedId(entry)) ?? [];
|
|
269
|
+
return items;
|
|
270
|
+
}, [selected]);
|
|
271
|
+
const handleDragEnd = (event) => {
|
|
272
|
+
const { active, over } = event;
|
|
273
|
+
if (!active || !over) return;
|
|
274
|
+
if (active.id !== over.id) {
|
|
275
|
+
const oldIndex = selected.findIndex(
|
|
276
|
+
(entry) => buildSelectedId(entry) === active.id
|
|
277
|
+
);
|
|
278
|
+
const newIndex = selected.findIndex(
|
|
279
|
+
(entry) => buildSelectedId(entry) === over.id
|
|
280
|
+
);
|
|
281
|
+
onSort(sortable.arrayMove(selected, oldIndex, newIndex));
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
if (!availableSuggestions?.length && !selected?.length) return null;
|
|
285
|
+
return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: [2, 0, 2, 0], background: "neutral100", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { style: { whiteSpace: "unset", borderCollapse: "separate", borderSpacing: "0 10px" }, children: [
|
|
286
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
|
|
287
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, {}),
|
|
288
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: translate("contentSuggestions.title") }) }),
|
|
289
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: translate("contentSuggestions.contentType") }) }),
|
|
290
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: translate("contentSuggestions.state") }) })
|
|
291
|
+
] }) }),
|
|
292
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tbody, { children: [
|
|
293
|
+
selected?.length ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
294
|
+
core.DndContext,
|
|
295
|
+
{
|
|
296
|
+
sensors,
|
|
297
|
+
collisionDetection: core.closestCenter,
|
|
298
|
+
onDragEnd: handleDragEnd,
|
|
299
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
300
|
+
sortable.SortableContext,
|
|
301
|
+
{
|
|
302
|
+
items: sortableItems,
|
|
303
|
+
strategy: sortable.verticalListSortingStrategy,
|
|
304
|
+
children: selected.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
305
|
+
TableItem,
|
|
306
|
+
{
|
|
307
|
+
id: buildSelectedId(entry),
|
|
308
|
+
entry,
|
|
309
|
+
type: "selected",
|
|
310
|
+
onDelete
|
|
311
|
+
},
|
|
312
|
+
buildSelectedId(entry)
|
|
313
|
+
))
|
|
314
|
+
}
|
|
315
|
+
)
|
|
316
|
+
}
|
|
317
|
+
) }) : null,
|
|
318
|
+
availableSuggestions.length && selected?.length ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { colSpan: 5, children: /* @__PURE__ */ jsxRuntime.jsx("hr", { style: { width: "100%" } }) }) }) : null,
|
|
319
|
+
availableSuggestions.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
320
|
+
TableItem,
|
|
321
|
+
{
|
|
322
|
+
id: buildSelectedId(entry),
|
|
323
|
+
entry,
|
|
324
|
+
type: "suggestion",
|
|
325
|
+
onAdd,
|
|
326
|
+
disabled: typeof maximum === "number" ? (selected?.length ?? 0) >= maximum : false
|
|
327
|
+
},
|
|
328
|
+
buildSelectedId(entry)
|
|
329
|
+
))
|
|
330
|
+
] })
|
|
331
|
+
] }) });
|
|
332
|
+
}
|
|
333
|
+
const MainInput = ({
|
|
334
|
+
name,
|
|
335
|
+
error,
|
|
336
|
+
description,
|
|
337
|
+
onChange,
|
|
338
|
+
value,
|
|
339
|
+
labelAction,
|
|
340
|
+
label,
|
|
341
|
+
attribute,
|
|
342
|
+
required
|
|
343
|
+
}) => {
|
|
344
|
+
const { formatMessage } = reactIntl.useIntl();
|
|
345
|
+
const { translate } = index.useTranslate();
|
|
346
|
+
const location = reactRouterDom.useLocation();
|
|
347
|
+
const maximumItems = attribute.options.max;
|
|
348
|
+
const minimumItems = attribute.options.min || 0;
|
|
349
|
+
const currentLocale = new URLSearchParams(location.search).get(
|
|
350
|
+
"plugins[i18n][locale]"
|
|
351
|
+
);
|
|
352
|
+
const [keyword, setKeyword] = react.useState("");
|
|
353
|
+
const [selected, setSelected] = react.useState([]);
|
|
354
|
+
const [loading, setLoading] = react.useState(true);
|
|
355
|
+
react.useEffect(() => {
|
|
356
|
+
const value2 = selected.length > maximumItems || selected.length < minimumItems ? [] : selected;
|
|
357
|
+
onChange({ target: { name, value: index.formatToStrapiField(value2, currentLocale) } });
|
|
358
|
+
}, [selected]);
|
|
359
|
+
const hint = react.useMemo(() => {
|
|
360
|
+
const minLabel = minimumItems > 0 ? `${translate("input.hint.min")} ${minimumItems} ${minimumItems > 1 ? translate("input.hint.entries") : translate("input.hint.entry")}` : "";
|
|
361
|
+
const maxLabel = maximumItems > 0 ? `${translate("input.hint.max")} ${maximumItems} ${maximumItems > 1 ? translate("input.hint.entries") : translate("input.hint.entry")}` : "";
|
|
362
|
+
return `
|
|
363
|
+
${minLabel ? `${minLabel}` : ""}
|
|
364
|
+
${minLabel && maxLabel ? ", " : ""}
|
|
365
|
+
${maxLabel}
|
|
366
|
+
${minLabel || maxLabel ? translate("input.hint.separator") : ""}
|
|
367
|
+
${selected.length} ${translate("input.hint.selected")}
|
|
368
|
+
`;
|
|
369
|
+
}, [selected, maximumItems, minimumItems, translate]);
|
|
370
|
+
const inputError = react.useMemo(() => {
|
|
371
|
+
if (!error) return "";
|
|
372
|
+
if (selected.length < minimumItems)
|
|
373
|
+
return `${error} - ${translate("input.error.min")} ${minimumItems} ${translate("input.error.required")}`;
|
|
374
|
+
if (selected.length > maximumItems)
|
|
375
|
+
return `${error} - ${translate("input.error.max")} ${maximumItems} ${translate("input.error.required")}`;
|
|
376
|
+
return error;
|
|
377
|
+
}, [error, maximumItems, minimumItems, selected, translate]);
|
|
378
|
+
const { loading: searchLoading, results } = useSearchedEntries(
|
|
379
|
+
keyword,
|
|
380
|
+
attribute.options.contentTypes,
|
|
381
|
+
currentLocale
|
|
382
|
+
);
|
|
383
|
+
react.useEffect(() => {
|
|
384
|
+
async function validateContent() {
|
|
385
|
+
if (!value) {
|
|
386
|
+
setLoading(false);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
const entries = JSON.parse(value);
|
|
390
|
+
const result = await index.validateCurrentRelations(entries);
|
|
391
|
+
setSelected(result);
|
|
392
|
+
setLoading(false);
|
|
393
|
+
}
|
|
394
|
+
validateContent();
|
|
395
|
+
}, []);
|
|
396
|
+
const onAddEntry = (entry) => {
|
|
397
|
+
const alreadyDefined = selected.some(
|
|
398
|
+
(selectedEntry) => selectedEntry.uid === entry.uid && selectedEntry.item.id === entry.item.id
|
|
399
|
+
);
|
|
400
|
+
if (alreadyDefined) return;
|
|
401
|
+
setSelected([...selected, entry]);
|
|
402
|
+
};
|
|
403
|
+
const onDeleteEntry = (entry) => {
|
|
404
|
+
const newSelected = selected.filter(
|
|
405
|
+
(selectedEntry) => !(selectedEntry.uid === entry.uid && selectedEntry.item.id === entry.item.id)
|
|
406
|
+
);
|
|
407
|
+
setSelected(newSelected);
|
|
408
|
+
};
|
|
409
|
+
const onEntriesSorted = (entries) => {
|
|
410
|
+
setSelected(entries);
|
|
411
|
+
};
|
|
412
|
+
if (loading) return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, {});
|
|
413
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
414
|
+
designSystem.Field.Root,
|
|
415
|
+
{
|
|
416
|
+
name,
|
|
417
|
+
id: name,
|
|
418
|
+
error,
|
|
419
|
+
hint: description,
|
|
420
|
+
required,
|
|
421
|
+
children: [
|
|
422
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { action: labelAction, children: label }),
|
|
423
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
424
|
+
designSystem.TextInput,
|
|
425
|
+
{
|
|
426
|
+
placeholder: translate("input.placeholder"),
|
|
427
|
+
required,
|
|
428
|
+
hint,
|
|
429
|
+
error: inputError,
|
|
430
|
+
value: keyword,
|
|
431
|
+
onChange: (e) => setKeyword(e.target.value)
|
|
432
|
+
}
|
|
433
|
+
),
|
|
434
|
+
searchLoading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, {}) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
435
|
+
InputContentSuggestions,
|
|
436
|
+
{
|
|
437
|
+
uniqueId: Date.now(),
|
|
438
|
+
suggestions: results,
|
|
439
|
+
selected,
|
|
440
|
+
onAddEntry,
|
|
441
|
+
onDeleteEntry,
|
|
442
|
+
onEntriesSorted,
|
|
443
|
+
maximum: maximumItems
|
|
444
|
+
}
|
|
445
|
+
)
|
|
446
|
+
]
|
|
447
|
+
}
|
|
448
|
+
);
|
|
449
|
+
};
|
|
450
|
+
const Index = (props) => {
|
|
451
|
+
const { locale } = reactIntl.useIntl();
|
|
452
|
+
const theme = react.useMemo(() => ({}), []);
|
|
453
|
+
const attribute = react.useMemo(() => {
|
|
454
|
+
if (!props.attribute) return props.attribute;
|
|
455
|
+
if (!props.attribute.options) return props.attribute;
|
|
456
|
+
if (!props.attribute.options.contentTypes) return props.attribute;
|
|
457
|
+
const contentTypes = Object.keys(
|
|
458
|
+
props.attribute.options.contentTypes
|
|
459
|
+
).filter((key) => props.attribute.options.contentTypes[key]);
|
|
460
|
+
return {
|
|
461
|
+
...props.attribute,
|
|
462
|
+
options: {
|
|
463
|
+
...props.attribute.options,
|
|
464
|
+
contentTypes: contentTypes.join(",")
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
}, [props.attribute]);
|
|
468
|
+
return /* @__PURE__ */ jsxRuntime.jsx(designSystem.DesignSystemProvider, { theme, children: /* @__PURE__ */ jsxRuntime.jsx(MainInput, { ...props, attribute }) });
|
|
469
|
+
};
|
|
470
|
+
exports.default = Index;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const jsxRuntime = require("react/jsx-runtime");
|
|
3
3
|
const icons = require("@strapi/icons");
|
|
4
4
|
const admin = require("@strapi/strapi/admin");
|
|
5
|
-
const
|
|
5
|
+
const react = require("react");
|
|
6
6
|
const designSystem = require("@strapi/design-system");
|
|
7
7
|
const reactIntl = require("react-intl");
|
|
8
8
|
const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
|
|
@@ -52,15 +52,22 @@ const fetchMatchingContent = async (keyword, contentTypes, locale) => {
|
|
|
52
52
|
total
|
|
53
53
|
};
|
|
54
54
|
};
|
|
55
|
-
const formatToStrapiField = (entries) => {
|
|
55
|
+
const formatToStrapiField = (entries, locale) => {
|
|
56
56
|
if (entries.length === 0) return "";
|
|
57
|
-
|
|
58
|
-
entries.map((entry) =>
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
const storedData = JSON.stringify(
|
|
58
|
+
entries.map((entry) => {
|
|
59
|
+
const obj = {
|
|
60
|
+
uid: entry.uid,
|
|
61
|
+
documentId: entry.item.documentId,
|
|
62
|
+
MRCT: true
|
|
63
|
+
};
|
|
64
|
+
if (locale) {
|
|
65
|
+
obj.locale = locale;
|
|
66
|
+
}
|
|
67
|
+
return obj;
|
|
68
|
+
}).filter(Boolean)
|
|
63
69
|
);
|
|
70
|
+
return storedData;
|
|
64
71
|
};
|
|
65
72
|
const validateCurrentRelations = async (entries) => {
|
|
66
73
|
const { post } = admin.getFetchClient();
|
|
@@ -109,23 +116,41 @@ const SidePanel = () => {
|
|
|
109
116
|
model: uid,
|
|
110
117
|
isSingleType
|
|
111
118
|
} = admin.unstable_useContentManagerContext();
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
119
|
+
const currentLocale = new URLSearchParams(location.search).get(
|
|
120
|
+
"plugins[i18n][locale]"
|
|
121
|
+
);
|
|
122
|
+
const [linkedContent, setLinkedContent] = react.useState([]);
|
|
123
|
+
const [loading, setLoading] = react.useState(true);
|
|
124
|
+
const [disabled, setDisabledFeature] = react.useState(false);
|
|
125
|
+
react.useEffect(() => {
|
|
115
126
|
const fetchRevertRelations = async () => {
|
|
116
127
|
const { post } = admin.getFetchClient();
|
|
117
128
|
const response = await post(`/${pluginId}/fetch-revert-relations`, {
|
|
118
129
|
documentId,
|
|
119
130
|
uid,
|
|
120
|
-
isSingleType
|
|
131
|
+
isSingleType,
|
|
132
|
+
locale: currentLocale
|
|
121
133
|
});
|
|
122
134
|
setLinkedContent(Array.isArray(response.data) ? response.data.filter(Boolean) : []);
|
|
123
135
|
setLoading(false);
|
|
124
136
|
};
|
|
125
|
-
|
|
137
|
+
const fetchConfiguration = async () => {
|
|
138
|
+
const { get } = admin.getFetchClient();
|
|
139
|
+
const response = await get(`/${pluginId}/get-configuration`);
|
|
140
|
+
if (response.data.disableRevertRelations) {
|
|
141
|
+
setDisabledFeature(true);
|
|
142
|
+
setLoading(false);
|
|
143
|
+
} else {
|
|
144
|
+
fetchRevertRelations();
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
fetchConfiguration();
|
|
126
148
|
}, []);
|
|
127
149
|
if (loading)
|
|
128
150
|
return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { width: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, {}) });
|
|
151
|
+
if (disabled) {
|
|
152
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", {});
|
|
153
|
+
}
|
|
129
154
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
130
155
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, {}),
|
|
131
156
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { width: "100%", marginTop: 4, children: [
|
|
@@ -165,7 +190,7 @@ const SidePanel = () => {
|
|
|
165
190
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
166
191
|
"a",
|
|
167
192
|
{
|
|
168
|
-
href: content.isSingleType ? `/admin/content-manager/single-types/${content.uid}` : `/admin/content-manager/collection-types/${content.uid}/${content.documentId}`,
|
|
193
|
+
href: content.isSingleType ? `/admin/content-manager/single-types/${content.uid}${currentLocale ? `?plugins[i18n][locale]=${currentLocale}` : ""}` : `/admin/content-manager/collection-types/${content.uid}/${content.documentId}${currentLocale ? `?plugins[i18n][locale]=${currentLocale}` : ""}`,
|
|
169
194
|
target: "_blank",
|
|
170
195
|
style: { textDecoration: "none" },
|
|
171
196
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -224,7 +249,7 @@ const index = {
|
|
|
224
249
|
components: {
|
|
225
250
|
Input: () => Promise.resolve().then(() => require(
|
|
226
251
|
/* webpackChunkName: "input-component" */
|
|
227
|
-
"./index-
|
|
252
|
+
"./index-Bu99PZdl.js"
|
|
228
253
|
))
|
|
229
254
|
},
|
|
230
255
|
inputSize: {
|