@qkix/strapi-plugin-ferry 0.1.1 → 0.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 +18 -0
- package/dist/_chunks/TransferPage-B0Le6wyG.mjs +373 -0
- package/dist/_chunks/TransferPage-DPZr7PFx.js +1 -0
- package/dist/_chunks/index-9EKjguHv.js +1 -0
- package/dist/_chunks/index-C3-QXVsq.mjs +207 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +4 -41
- package/dist/admin/src/api.d.ts +10 -0
- package/dist/admin/src/components/ExportScopeDialog.d.ts +21 -0
- package/dist/admin/src/components/exportActions.d.ts +35 -0
- package/dist/admin/src/index.d.ts +1 -0
- package/package.json +3 -3
- package/dist/_chunks/TransferPage-Bhp6vkZE.js +0 -1
- package/dist/_chunks/TransferPage-c8hdFQDQ.mjs +0 -398
package/README.md
CHANGED
|
@@ -62,6 +62,24 @@ export default {
|
|
|
62
62
|
|
|
63
63
|
Then open **Ferry** in the main menu.
|
|
64
64
|
|
|
65
|
+
## Exporting from the list view
|
|
66
|
+
|
|
67
|
+
<img src="https://raw.githubusercontent.com/qkix/strapi-plugins/main/packages/strapi-plugin-ferry/docs/list-view.png" alt="A Content Manager list view with two rows ticked: an Export action between Publish and Delete, and a second Export button beside the view settings" width="900" />
|
|
68
|
+
|
|
69
|
+
Ferry's own page carries whole content types. The Content Manager is where you
|
|
70
|
+
usually know which entries you actually want, so the export is there too, in the
|
|
71
|
+
two shapes that question comes in:
|
|
72
|
+
|
|
73
|
+
- **Tick some rows** and _Export_ appears beside _Delete_. This is the "a copy of
|
|
74
|
+
the twelve articles I just fixed" case.
|
|
75
|
+
- **Narrow the list with filters** and the _Export_ button next to the view
|
|
76
|
+
settings takes everything that matches - including what is on the pages you
|
|
77
|
+
cannot see, which is why ticking rows cannot answer it.
|
|
78
|
+
|
|
79
|
+
Either way you pick the format and the version, and the file downloads. Both
|
|
80
|
+
respect the `plugin::ferry.export` permission and appear only on the project's
|
|
81
|
+
own content types, so nothing offers an export the server would refuse.
|
|
82
|
+
|
|
65
83
|
## The dry run
|
|
66
84
|
|
|
67
85
|
Nothing is written until you have read a report. Upload a file, and Ferry
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import { jsxs as n, jsx as e } from "react/jsx-runtime";
|
|
2
|
+
import * as d from "react";
|
|
3
|
+
import { useFetchClient as j, Page as N, Layouts as $ } from "@strapi/admin/strapi-admin";
|
|
4
|
+
import { Flex as s, Typography as r, Field as c, SingleSelect as S, SingleSelectOption as f, Box as g, Checkbox as P, Button as D, Badge as F, Loader as U } from "@strapi/design-system";
|
|
5
|
+
import { Download as V, Upload as A } from "@strapi/icons";
|
|
6
|
+
import { D as J, r as T, s as Z, a as z, b as _ } from "./index-C3-QXVsq.mjs";
|
|
7
|
+
const H = ({ contentTypes: a }) => {
|
|
8
|
+
const { get: h, post: i } = j(), [u, v] = d.useState(a[0]?.uid ?? ""), [p, m] = d.useState(J), [o, C] = d.useState(null), [w, k] = d.useState(!1), [B, R] = d.useState([]), [I, E] = d.useState(null), [y, W] = d.useState(null);
|
|
9
|
+
d.useEffect(() => {
|
|
10
|
+
if (!u) return;
|
|
11
|
+
let t = !0;
|
|
12
|
+
return h(T.plan(u)).then(({ data: L }) => t && C(L)).catch(() => t && C(null)), () => {
|
|
13
|
+
t = !1;
|
|
14
|
+
};
|
|
15
|
+
}, [h, u]);
|
|
16
|
+
const l = (t) => m((L) => ({ ...L, ...t })), b = async () => {
|
|
17
|
+
k(!0), E(null), R([]), W(null);
|
|
18
|
+
try {
|
|
19
|
+
const { data: t } = await i(T.export, {
|
|
20
|
+
uid: u,
|
|
21
|
+
...p,
|
|
22
|
+
envelope: !0
|
|
23
|
+
});
|
|
24
|
+
Z(t.body, t.filename, t.mime), R(t.warnings ?? []), W(`${t.filename} (${t.count} document${t.count === 1 ? "" : "s"})`);
|
|
25
|
+
} catch (t) {
|
|
26
|
+
E(t?.response?.data?.error?.message ?? "The export failed.");
|
|
27
|
+
} finally {
|
|
28
|
+
k(!1);
|
|
29
|
+
}
|
|
30
|
+
}, x = o ? [
|
|
31
|
+
`${o.scalars.length} field(s)`,
|
|
32
|
+
p.relations && o.relations.length > 0 ? `${o.relations.length} relation(s)` : null,
|
|
33
|
+
o.components.length > 0 ? `${o.components.length} component(s)` : null,
|
|
34
|
+
o.dynamicZones.length > 0 ? `${o.dynamicZones.length} dynamic zone(s)` : null,
|
|
35
|
+
p.media && o.media.length > 0 ? `${o.media.length} media field(s)` : null
|
|
36
|
+
].filter(Boolean) : [];
|
|
37
|
+
return /* @__PURE__ */ n(s, { direction: "column", gap: 4, alignItems: "stretch", children: [
|
|
38
|
+
/* @__PURE__ */ e(r, { variant: "delta", children: "Export" }),
|
|
39
|
+
/* @__PURE__ */ n(c.Root, { name: "export-uid", children: [
|
|
40
|
+
/* @__PURE__ */ e(c.Label, { children: "Content type" }),
|
|
41
|
+
/* @__PURE__ */ e(S, { value: u, onChange: (t) => v(String(t)), children: a.map((t) => /* @__PURE__ */ e(f, { value: t.uid, children: t.label }, t.uid)) })
|
|
42
|
+
] }),
|
|
43
|
+
/* @__PURE__ */ n(s, { gap: 4, wrap: "wrap", alignItems: "flex-end", children: [
|
|
44
|
+
/* @__PURE__ */ e(g, { minWidth: "160px", children: /* @__PURE__ */ n(c.Root, { name: "export-format", children: [
|
|
45
|
+
/* @__PURE__ */ e(c.Label, { children: "Format" }),
|
|
46
|
+
/* @__PURE__ */ n(
|
|
47
|
+
S,
|
|
48
|
+
{
|
|
49
|
+
value: p.format,
|
|
50
|
+
onChange: (t) => l({ format: t }),
|
|
51
|
+
children: [
|
|
52
|
+
/* @__PURE__ */ e(f, { value: "json", children: "JSON - everything" }),
|
|
53
|
+
/* @__PURE__ */ e(f, { value: "csv", children: "CSV - flat fields" })
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
] }) }),
|
|
58
|
+
/* @__PURE__ */ e(g, { minWidth: "160px", children: /* @__PURE__ */ n(c.Root, { name: "export-status", children: [
|
|
59
|
+
/* @__PURE__ */ e(c.Label, { children: "Version" }),
|
|
60
|
+
/* @__PURE__ */ n(
|
|
61
|
+
S,
|
|
62
|
+
{
|
|
63
|
+
value: p.status,
|
|
64
|
+
onChange: (t) => l({ status: t }),
|
|
65
|
+
disabled: o ? !o.draftAndPublish : !1,
|
|
66
|
+
children: [
|
|
67
|
+
/* @__PURE__ */ e(f, { value: "draft", children: "Draft" }),
|
|
68
|
+
/* @__PURE__ */ e(f, { value: "published", children: "Published" })
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
] }) })
|
|
73
|
+
] }),
|
|
74
|
+
/* @__PURE__ */ n(s, { gap: 4, wrap: "wrap", children: [
|
|
75
|
+
/* @__PURE__ */ e(
|
|
76
|
+
P,
|
|
77
|
+
{
|
|
78
|
+
checked: p.relations,
|
|
79
|
+
onCheckedChange: (t) => l({ relations: !!t }),
|
|
80
|
+
children: "Relations"
|
|
81
|
+
}
|
|
82
|
+
),
|
|
83
|
+
/* @__PURE__ */ e(
|
|
84
|
+
P,
|
|
85
|
+
{
|
|
86
|
+
checked: p.media,
|
|
87
|
+
onCheckedChange: (t) => l({ media: !!t }),
|
|
88
|
+
children: "Media references"
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
] }),
|
|
92
|
+
x.length > 0 && /* @__PURE__ */ n(r, { variant: "pi", textColor: "neutral600", children: [
|
|
93
|
+
"Carries ",
|
|
94
|
+
x.join(", "),
|
|
95
|
+
".",
|
|
96
|
+
p.format === "csv" && (o?.components.length || o?.dynamicZones.length) ? " CSV leaves components and dynamic zones behind - use JSON to keep them." : ""
|
|
97
|
+
] }),
|
|
98
|
+
/* @__PURE__ */ e(g, { children: /* @__PURE__ */ e(D, { startIcon: /* @__PURE__ */ e(V, {}), onClick: b, loading: w, disabled: !u, children: "Export" }) }),
|
|
99
|
+
y && /* @__PURE__ */ n(r, { variant: "pi", textColor: "success600", children: [
|
|
100
|
+
"Saved ",
|
|
101
|
+
y,
|
|
102
|
+
"."
|
|
103
|
+
] }),
|
|
104
|
+
B.map((t, L) => /* @__PURE__ */ e(r, { variant: "pi", textColor: "warning700", children: t }, L)),
|
|
105
|
+
I && /* @__PURE__ */ e(r, { variant: "pi", textColor: "danger600", children: I })
|
|
106
|
+
] });
|
|
107
|
+
}, O = ({ label: a, value: h, tone: i }) => /* @__PURE__ */ n(s, { direction: "column", gap: 1, alignItems: "flex-start", children: [
|
|
108
|
+
/* @__PURE__ */ e(r, { variant: "beta", textColor: h > 0 ? i : "neutral500", children: h }),
|
|
109
|
+
/* @__PURE__ */ e(r, { variant: "pi", textColor: "neutral600", children: a })
|
|
110
|
+
] }), K = ({ report: a }) => {
|
|
111
|
+
const h = a.rows.filter((i) => i.outcome === "error");
|
|
112
|
+
return /* @__PURE__ */ n(s, { direction: "column", gap: 4, alignItems: "stretch", children: [
|
|
113
|
+
/* @__PURE__ */ n(
|
|
114
|
+
g,
|
|
115
|
+
{
|
|
116
|
+
padding: 4,
|
|
117
|
+
hasRadius: !0,
|
|
118
|
+
background: "neutral0",
|
|
119
|
+
borderColor: "neutral200",
|
|
120
|
+
borderWidth: "1px",
|
|
121
|
+
borderStyle: "solid",
|
|
122
|
+
children: [
|
|
123
|
+
/* @__PURE__ */ n(s, { justifyContent: "space-between", alignItems: "center", paddingBottom: 3, children: [
|
|
124
|
+
/* @__PURE__ */ e(r, { variant: "delta", children: a.applied ? "Imported" : "Dry run - nothing written yet" }),
|
|
125
|
+
/* @__PURE__ */ e(F, { children: z(a) })
|
|
126
|
+
] }),
|
|
127
|
+
/* @__PURE__ */ n(s, { gap: 8, wrap: "wrap", children: [
|
|
128
|
+
/* @__PURE__ */ e(O, { label: "in the file", value: a.total, tone: "neutral800" }),
|
|
129
|
+
/* @__PURE__ */ e(O, { label: "to create", value: a.created, tone: "success600" }),
|
|
130
|
+
/* @__PURE__ */ e(O, { label: "to update", value: a.updated, tone: "primary600" }),
|
|
131
|
+
/* @__PURE__ */ e(O, { label: "skipped", value: a.skipped, tone: "neutral600" }),
|
|
132
|
+
/* @__PURE__ */ e(O, { label: "failed", value: a.errored, tone: "danger600" })
|
|
133
|
+
] })
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
),
|
|
137
|
+
a.warnings.map((i, u) => /* @__PURE__ */ e(
|
|
138
|
+
g,
|
|
139
|
+
{
|
|
140
|
+
padding: 3,
|
|
141
|
+
hasRadius: !0,
|
|
142
|
+
background: "warning100",
|
|
143
|
+
borderColor: "warning200",
|
|
144
|
+
borderWidth: "1px",
|
|
145
|
+
borderStyle: "solid",
|
|
146
|
+
children: /* @__PURE__ */ e(r, { variant: "pi", textColor: "warning700", children: i })
|
|
147
|
+
},
|
|
148
|
+
u
|
|
149
|
+
)),
|
|
150
|
+
a.unresolved.length > 0 && /* @__PURE__ */ n(
|
|
151
|
+
g,
|
|
152
|
+
{
|
|
153
|
+
padding: 4,
|
|
154
|
+
hasRadius: !0,
|
|
155
|
+
background: "neutral0",
|
|
156
|
+
borderColor: "warning200",
|
|
157
|
+
borderWidth: "1px",
|
|
158
|
+
borderStyle: "solid",
|
|
159
|
+
children: [
|
|
160
|
+
/* @__PURE__ */ e(r, { variant: "delta", children: "Relations that point at nothing" }),
|
|
161
|
+
/* @__PURE__ */ e(g, { paddingTop: 1, paddingBottom: 3, children: /* @__PURE__ */ e(r, { variant: "pi", textColor: "neutral600", children: 'These targets are neither in the file nor in this environment. With "drop the link" the rows still import without them; with "refuse the import" nothing is written at all.' }) }),
|
|
162
|
+
/* @__PURE__ */ n(s, { direction: "column", gap: 2, alignItems: "stretch", children: [
|
|
163
|
+
a.unresolved.slice(0, 25).map((i, u) => /* @__PURE__ */ n(s, { gap: 3, alignItems: "baseline", wrap: "wrap", children: [
|
|
164
|
+
/* @__PURE__ */ n(F, { children: [
|
|
165
|
+
"row ",
|
|
166
|
+
i.row
|
|
167
|
+
] }),
|
|
168
|
+
/* @__PURE__ */ e(r, { variant: "omega", fontWeight: "bold", children: i.field }),
|
|
169
|
+
/* @__PURE__ */ e(r, { variant: "pi", textColor: "neutral600", children: i.target }),
|
|
170
|
+
/* @__PURE__ */ e(r, { variant: "pi", textColor: "danger600", children: i.keys.join(", ") })
|
|
171
|
+
] }, u)),
|
|
172
|
+
a.unresolved.length > 25 && /* @__PURE__ */ n(r, { variant: "pi", textColor: "neutral600", children: [
|
|
173
|
+
"and ",
|
|
174
|
+
a.unresolved.length - 25,
|
|
175
|
+
" more"
|
|
176
|
+
] })
|
|
177
|
+
] })
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
),
|
|
181
|
+
h.length > 0 && /* @__PURE__ */ n(
|
|
182
|
+
g,
|
|
183
|
+
{
|
|
184
|
+
padding: 4,
|
|
185
|
+
hasRadius: !0,
|
|
186
|
+
background: "neutral0",
|
|
187
|
+
borderColor: "danger200",
|
|
188
|
+
borderWidth: "1px",
|
|
189
|
+
borderStyle: "solid",
|
|
190
|
+
children: [
|
|
191
|
+
/* @__PURE__ */ e(r, { variant: "delta", textColor: "danger600", children: "Rows that failed" }),
|
|
192
|
+
/* @__PURE__ */ e(g, { paddingTop: 2, children: /* @__PURE__ */ e(s, { direction: "column", gap: 2, alignItems: "stretch", children: h.slice(0, 25).map((i) => /* @__PURE__ */ n(s, { gap: 3, alignItems: "baseline", wrap: "wrap", children: [
|
|
193
|
+
/* @__PURE__ */ n(F, { children: [
|
|
194
|
+
"row ",
|
|
195
|
+
i.row
|
|
196
|
+
] }),
|
|
197
|
+
/* @__PURE__ */ e(r, { variant: "pi", textColor: "neutral600", children: i.documentId ?? "no documentId" }),
|
|
198
|
+
/* @__PURE__ */ e(r, { variant: "pi", textColor: "danger600", children: i.message })
|
|
199
|
+
] }, i.row)) }) })
|
|
200
|
+
]
|
|
201
|
+
}
|
|
202
|
+
)
|
|
203
|
+
] });
|
|
204
|
+
}, X = ({ contentTypes: a }) => {
|
|
205
|
+
const { post: h } = j(), [i, u] = d.useState(a[0]?.uid ?? ""), [v, p] = d.useState(_), [m, o] = d.useState(null), [C, w] = d.useState(null), [k, B] = d.useState(!1), [R, I] = d.useState(null), E = d.useRef(null), y = (l) => {
|
|
206
|
+
p((b) => ({ ...b, ...l })), w(null);
|
|
207
|
+
}, W = async (l) => {
|
|
208
|
+
if (!m) return;
|
|
209
|
+
B(!0), I(null);
|
|
210
|
+
const b = new FormData();
|
|
211
|
+
b.append("file", m), b.append("uid", i), Object.entries(v).forEach(([x, t]) => b.append(x, String(t)));
|
|
212
|
+
try {
|
|
213
|
+
const { data: x } = await h(l ? T.apply : T.preview, b);
|
|
214
|
+
w(x);
|
|
215
|
+
} catch (x) {
|
|
216
|
+
I(x?.response?.data?.error?.message ?? "The file could not be read."), w(null);
|
|
217
|
+
} finally {
|
|
218
|
+
B(!1);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
return /* @__PURE__ */ n(s, { direction: "column", gap: 4, alignItems: "stretch", children: [
|
|
222
|
+
/* @__PURE__ */ e(r, { variant: "delta", children: "Import" }),
|
|
223
|
+
/* @__PURE__ */ n(c.Root, { name: "import-uid", children: [
|
|
224
|
+
/* @__PURE__ */ e(c.Label, { children: "Content type" }),
|
|
225
|
+
/* @__PURE__ */ e(
|
|
226
|
+
S,
|
|
227
|
+
{
|
|
228
|
+
value: i,
|
|
229
|
+
onChange: (l) => {
|
|
230
|
+
u(String(l)), w(null);
|
|
231
|
+
},
|
|
232
|
+
children: a.map((l) => /* @__PURE__ */ e(f, { value: l.uid, children: l.label }, l.uid))
|
|
233
|
+
}
|
|
234
|
+
)
|
|
235
|
+
] }),
|
|
236
|
+
/* @__PURE__ */ n(c.Root, { name: "import-file", children: [
|
|
237
|
+
/* @__PURE__ */ e(c.Label, { children: "File" }),
|
|
238
|
+
/* @__PURE__ */ n(s, { gap: 2, alignItems: "center", children: [
|
|
239
|
+
/* @__PURE__ */ e(D, { variant: "secondary", onClick: () => E.current?.click(), children: "Choose a file" }),
|
|
240
|
+
/* @__PURE__ */ e(r, { variant: "pi", textColor: m ? "neutral800" : "neutral500", children: m ? m.name : "JSON or CSV" })
|
|
241
|
+
] }),
|
|
242
|
+
/* @__PURE__ */ e(
|
|
243
|
+
"input",
|
|
244
|
+
{
|
|
245
|
+
ref: E,
|
|
246
|
+
type: "file",
|
|
247
|
+
accept: ".json,.csv,application/json,text/csv",
|
|
248
|
+
style: { display: "none" },
|
|
249
|
+
onChange: (l) => {
|
|
250
|
+
o(l.target.files?.[0] ?? null), w(null);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
)
|
|
254
|
+
] }),
|
|
255
|
+
/* @__PURE__ */ n(s, { gap: 4, wrap: "wrap", alignItems: "flex-end", children: [
|
|
256
|
+
/* @__PURE__ */ e(g, { minWidth: "200px", children: /* @__PURE__ */ n(c.Root, { name: "import-existing", children: [
|
|
257
|
+
/* @__PURE__ */ e(c.Label, { children: "When it is already there" }),
|
|
258
|
+
/* @__PURE__ */ n(
|
|
259
|
+
S,
|
|
260
|
+
{
|
|
261
|
+
value: v.onExisting,
|
|
262
|
+
onChange: (l) => y({ onExisting: l === "skip" ? "skip" : "update" }),
|
|
263
|
+
children: [
|
|
264
|
+
/* @__PURE__ */ e(f, { value: "update", children: "Update it" }),
|
|
265
|
+
/* @__PURE__ */ e(f, { value: "skip", children: "Leave it alone" })
|
|
266
|
+
]
|
|
267
|
+
}
|
|
268
|
+
)
|
|
269
|
+
] }) }),
|
|
270
|
+
/* @__PURE__ */ e(g, { minWidth: "240px", children: /* @__PURE__ */ n(c.Root, { name: "import-missing", children: [
|
|
271
|
+
/* @__PURE__ */ e(c.Label, { children: "When a relation points at nothing" }),
|
|
272
|
+
/* @__PURE__ */ n(
|
|
273
|
+
S,
|
|
274
|
+
{
|
|
275
|
+
value: v.onMissingRelation,
|
|
276
|
+
onChange: (l) => y({ onMissingRelation: l === "fail" ? "fail" : "skip" }),
|
|
277
|
+
children: [
|
|
278
|
+
/* @__PURE__ */ e(f, { value: "skip", children: "Drop the link, keep the row" }),
|
|
279
|
+
/* @__PURE__ */ e(f, { value: "fail", children: "Refuse the import" })
|
|
280
|
+
]
|
|
281
|
+
}
|
|
282
|
+
)
|
|
283
|
+
] }) }),
|
|
284
|
+
/* @__PURE__ */ e(g, { minWidth: "160px", children: /* @__PURE__ */ n(c.Root, { name: "import-status", children: [
|
|
285
|
+
/* @__PURE__ */ e(c.Label, { children: "Import as" }),
|
|
286
|
+
/* @__PURE__ */ n(
|
|
287
|
+
S,
|
|
288
|
+
{
|
|
289
|
+
value: v.status,
|
|
290
|
+
onChange: (l) => y({ status: l }),
|
|
291
|
+
children: [
|
|
292
|
+
/* @__PURE__ */ e(f, { value: "draft", children: "Draft" }),
|
|
293
|
+
/* @__PURE__ */ e(f, { value: "published", children: "Published" })
|
|
294
|
+
]
|
|
295
|
+
}
|
|
296
|
+
)
|
|
297
|
+
] }) })
|
|
298
|
+
] }),
|
|
299
|
+
/* @__PURE__ */ e(
|
|
300
|
+
P,
|
|
301
|
+
{
|
|
302
|
+
checked: v.continueOnError,
|
|
303
|
+
onCheckedChange: (l) => y({ continueOnError: !!l }),
|
|
304
|
+
children: "Keep going past a row that fails"
|
|
305
|
+
}
|
|
306
|
+
),
|
|
307
|
+
/* @__PURE__ */ e(r, { variant: "pi", textColor: "neutral600", children: v.continueOnError ? "Rows that fail are reported and the rest are committed." : "All or nothing: if any row fails, the whole import is rolled back and nothing changes." }),
|
|
308
|
+
/* @__PURE__ */ n(s, { gap: 2, children: [
|
|
309
|
+
/* @__PURE__ */ e(
|
|
310
|
+
D,
|
|
311
|
+
{
|
|
312
|
+
variant: "secondary",
|
|
313
|
+
startIcon: /* @__PURE__ */ e(A, {}),
|
|
314
|
+
onClick: () => W(!1),
|
|
315
|
+
loading: k,
|
|
316
|
+
disabled: !m || !i,
|
|
317
|
+
children: "Dry run"
|
|
318
|
+
}
|
|
319
|
+
),
|
|
320
|
+
/* @__PURE__ */ e(
|
|
321
|
+
D,
|
|
322
|
+
{
|
|
323
|
+
onClick: () => W(!0),
|
|
324
|
+
loading: k,
|
|
325
|
+
disabled: !m || !i || !C || C.applied,
|
|
326
|
+
children: "Import for real"
|
|
327
|
+
}
|
|
328
|
+
)
|
|
329
|
+
] }),
|
|
330
|
+
!C && m && /* @__PURE__ */ e(r, { variant: "pi", textColor: "neutral600", children: "Run the dry run first. Nothing is written until you have seen what it would do." }),
|
|
331
|
+
R && /* @__PURE__ */ e(r, { variant: "pi", textColor: "danger600", children: R }),
|
|
332
|
+
C && /* @__PURE__ */ e(K, { report: C })
|
|
333
|
+
] });
|
|
334
|
+
}, M = ({ children: a }) => /* @__PURE__ */ e(
|
|
335
|
+
g,
|
|
336
|
+
{
|
|
337
|
+
padding: 6,
|
|
338
|
+
hasRadius: !0,
|
|
339
|
+
background: "neutral0",
|
|
340
|
+
borderColor: "neutral200",
|
|
341
|
+
borderWidth: "1px",
|
|
342
|
+
borderStyle: "solid",
|
|
343
|
+
flex: "1",
|
|
344
|
+
minWidth: "380px",
|
|
345
|
+
children: a
|
|
346
|
+
}
|
|
347
|
+
), ne = () => {
|
|
348
|
+
const { get: a } = j(), [h, i] = d.useState([]), [u, v] = d.useState(!0), [p, m] = d.useState(null);
|
|
349
|
+
return d.useEffect(() => {
|
|
350
|
+
let o = !0;
|
|
351
|
+
return a(T.contentTypes).then(({ data: C }) => o && i(C.contentTypes)).catch(() => o && m("Could not read the list of content types.")).finally(() => o && v(!1)), () => {
|
|
352
|
+
o = !1;
|
|
353
|
+
};
|
|
354
|
+
}, [a]), /* @__PURE__ */ n(N.Main, { children: [
|
|
355
|
+
/* @__PURE__ */ e(
|
|
356
|
+
$.Header,
|
|
357
|
+
{
|
|
358
|
+
title: "Ferry",
|
|
359
|
+
subtitle: "Move content in and out as JSON or CSV, with relations and components intact"
|
|
360
|
+
}
|
|
361
|
+
),
|
|
362
|
+
/* @__PURE__ */ e($.Content, { children: u ? /* @__PURE__ */ e(s, { justifyContent: "center", padding: 10, children: /* @__PURE__ */ e(U, { small: !0, children: "Reading the schema" }) }) : p ? /* @__PURE__ */ e(r, { textColor: "danger600", children: p }) : h.length === 0 ? /* @__PURE__ */ n(s, { direction: "column", gap: 2, padding: 10, alignItems: "center", children: [
|
|
363
|
+
/* @__PURE__ */ e(r, { variant: "delta", children: "Nothing to carry yet" }),
|
|
364
|
+
/* @__PURE__ */ e(r, { textColor: "neutral600", children: "Create a content type, and it will show up here." })
|
|
365
|
+
] }) : /* @__PURE__ */ n(s, { gap: 6, alignItems: "flex-start", wrap: "wrap", children: [
|
|
366
|
+
/* @__PURE__ */ e(M, { children: /* @__PURE__ */ e(H, { contentTypes: h }) }),
|
|
367
|
+
/* @__PURE__ */ e(M, { children: /* @__PURE__ */ e(X, { contentTypes: h }) })
|
|
368
|
+
] }) })
|
|
369
|
+
] });
|
|
370
|
+
};
|
|
371
|
+
export {
|
|
372
|
+
ne as TransferPage
|
|
373
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),B=require("react"),f=require("@strapi/admin/strapi-admin"),n=require("@strapi/design-system"),R=require("@strapi/icons"),x=require("./index-9EKjguHv.js");function I(t){if(t&&t.__esModule)return t;const o=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t){for(const i in t)if(i!=="default"){const d=Object.getOwnPropertyDescriptor(t,i);Object.defineProperty(o,i,d.get?d:{enumerable:!0,get:()=>t[i]})}}return o.default=t,Object.freeze(o)}const s=I(B),E=({contentTypes:t})=>{const{get:o,post:i}=f.useFetchClient(),[d,p]=s.useState(t[0]?.uid??""),[c,h]=s.useState(x.DEFAULT_EXPORT),[a,u]=s.useState(null),[m,S]=s.useState(!1),[k,v]=s.useState([]),[b,C]=s.useState(null),[y,w]=s.useState(null);s.useEffect(()=>{if(!d)return;let l=!0;return o(x.routes.plan(d)).then(({data:F})=>l&&u(F)).catch(()=>l&&u(null)),()=>{l=!1}},[o,d]);const r=l=>h(F=>({...F,...l})),g=async()=>{S(!0),C(null),v([]),w(null);try{const{data:l}=await i(x.routes.export,{uid:d,...c,envelope:!0});x.saveFile(l.body,l.filename,l.mime),v(l.warnings??[]),w(`${l.filename} (${l.count} document${l.count===1?"":"s"})`)}catch(l){C(l?.response?.data?.error?.message??"The export failed.")}finally{S(!1)}},j=a?[`${a.scalars.length} field(s)`,c.relations&&a.relations.length>0?`${a.relations.length} relation(s)`:null,a.components.length>0?`${a.components.length} component(s)`:null,a.dynamicZones.length>0?`${a.dynamicZones.length} dynamic zone(s)`:null,c.media&&a.media.length>0?`${a.media.length} media field(s)`:null].filter(Boolean):[];return e.jsxs(n.Flex,{direction:"column",gap:4,alignItems:"stretch",children:[e.jsx(n.Typography,{variant:"delta",children:"Export"}),e.jsxs(n.Field.Root,{name:"export-uid",children:[e.jsx(n.Field.Label,{children:"Content type"}),e.jsx(n.SingleSelect,{value:d,onChange:l=>p(String(l)),children:t.map(l=>e.jsx(n.SingleSelectOption,{value:l.uid,children:l.label},l.uid))})]}),e.jsxs(n.Flex,{gap:4,wrap:"wrap",alignItems:"flex-end",children:[e.jsx(n.Box,{minWidth:"160px",children:e.jsxs(n.Field.Root,{name:"export-format",children:[e.jsx(n.Field.Label,{children:"Format"}),e.jsxs(n.SingleSelect,{value:c.format,onChange:l=>r({format:l}),children:[e.jsx(n.SingleSelectOption,{value:"json",children:"JSON - everything"}),e.jsx(n.SingleSelectOption,{value:"csv",children:"CSV - flat fields"})]})]})}),e.jsx(n.Box,{minWidth:"160px",children:e.jsxs(n.Field.Root,{name:"export-status",children:[e.jsx(n.Field.Label,{children:"Version"}),e.jsxs(n.SingleSelect,{value:c.status,onChange:l=>r({status:l}),disabled:a?!a.draftAndPublish:!1,children:[e.jsx(n.SingleSelectOption,{value:"draft",children:"Draft"}),e.jsx(n.SingleSelectOption,{value:"published",children:"Published"})]})]})})]}),e.jsxs(n.Flex,{gap:4,wrap:"wrap",children:[e.jsx(n.Checkbox,{checked:c.relations,onCheckedChange:l=>r({relations:!!l}),children:"Relations"}),e.jsx(n.Checkbox,{checked:c.media,onCheckedChange:l=>r({media:!!l}),children:"Media references"})]}),j.length>0&&e.jsxs(n.Typography,{variant:"pi",textColor:"neutral600",children:["Carries ",j.join(", "),".",c.format==="csv"&&(a?.components.length||a?.dynamicZones.length)?" CSV leaves components and dynamic zones behind - use JSON to keep them.":""]}),e.jsx(n.Box,{children:e.jsx(n.Button,{startIcon:e.jsx(R.Download,{}),onClick:g,loading:m,disabled:!d,children:"Export"})}),y&&e.jsxs(n.Typography,{variant:"pi",textColor:"success600",children:["Saved ",y,"."]}),k.map((l,F)=>e.jsx(n.Typography,{variant:"pi",textColor:"warning700",children:l},F)),b&&e.jsx(n.Typography,{variant:"pi",textColor:"danger600",children:b})]})},T=({label:t,value:o,tone:i})=>e.jsxs(n.Flex,{direction:"column",gap:1,alignItems:"flex-start",children:[e.jsx(n.Typography,{variant:"beta",textColor:o>0?i:"neutral500",children:o}),e.jsx(n.Typography,{variant:"pi",textColor:"neutral600",children:t})]}),W=({report:t})=>{const o=t.rows.filter(i=>i.outcome==="error");return e.jsxs(n.Flex,{direction:"column",gap:4,alignItems:"stretch",children:[e.jsxs(n.Box,{padding:4,hasRadius:!0,background:"neutral0",borderColor:"neutral200",borderWidth:"1px",borderStyle:"solid",children:[e.jsxs(n.Flex,{justifyContent:"space-between",alignItems:"center",paddingBottom:3,children:[e.jsx(n.Typography,{variant:"delta",children:t.applied?"Imported":"Dry run - nothing written yet"}),e.jsx(n.Badge,{children:x.summarise(t)})]}),e.jsxs(n.Flex,{gap:8,wrap:"wrap",children:[e.jsx(T,{label:"in the file",value:t.total,tone:"neutral800"}),e.jsx(T,{label:"to create",value:t.created,tone:"success600"}),e.jsx(T,{label:"to update",value:t.updated,tone:"primary600"}),e.jsx(T,{label:"skipped",value:t.skipped,tone:"neutral600"}),e.jsx(T,{label:"failed",value:t.errored,tone:"danger600"})]})]}),t.warnings.map((i,d)=>e.jsx(n.Box,{padding:3,hasRadius:!0,background:"warning100",borderColor:"warning200",borderWidth:"1px",borderStyle:"solid",children:e.jsx(n.Typography,{variant:"pi",textColor:"warning700",children:i})},d)),t.unresolved.length>0&&e.jsxs(n.Box,{padding:4,hasRadius:!0,background:"neutral0",borderColor:"warning200",borderWidth:"1px",borderStyle:"solid",children:[e.jsx(n.Typography,{variant:"delta",children:"Relations that point at nothing"}),e.jsx(n.Box,{paddingTop:1,paddingBottom:3,children:e.jsx(n.Typography,{variant:"pi",textColor:"neutral600",children:'These targets are neither in the file nor in this environment. With "drop the link" the rows still import without them; with "refuse the import" nothing is written at all.'})}),e.jsxs(n.Flex,{direction:"column",gap:2,alignItems:"stretch",children:[t.unresolved.slice(0,25).map((i,d)=>e.jsxs(n.Flex,{gap:3,alignItems:"baseline",wrap:"wrap",children:[e.jsxs(n.Badge,{children:["row ",i.row]}),e.jsx(n.Typography,{variant:"omega",fontWeight:"bold",children:i.field}),e.jsx(n.Typography,{variant:"pi",textColor:"neutral600",children:i.target}),e.jsx(n.Typography,{variant:"pi",textColor:"danger600",children:i.keys.join(", ")})]},d)),t.unresolved.length>25&&e.jsxs(n.Typography,{variant:"pi",textColor:"neutral600",children:["and ",t.unresolved.length-25," more"]})]})]}),o.length>0&&e.jsxs(n.Box,{padding:4,hasRadius:!0,background:"neutral0",borderColor:"danger200",borderWidth:"1px",borderStyle:"solid",children:[e.jsx(n.Typography,{variant:"delta",textColor:"danger600",children:"Rows that failed"}),e.jsx(n.Box,{paddingTop:2,children:e.jsx(n.Flex,{direction:"column",gap:2,alignItems:"stretch",children:o.slice(0,25).map(i=>e.jsxs(n.Flex,{gap:3,alignItems:"baseline",wrap:"wrap",children:[e.jsxs(n.Badge,{children:["row ",i.row]}),e.jsx(n.Typography,{variant:"pi",textColor:"neutral600",children:i.documentId??"no documentId"}),e.jsx(n.Typography,{variant:"pi",textColor:"danger600",children:i.message})]},i.row))})})]})]})},L=({contentTypes:t})=>{const{post:o}=f.useFetchClient(),[i,d]=s.useState(t[0]?.uid??""),[p,c]=s.useState(x.DEFAULT_IMPORT),[h,a]=s.useState(null),[u,m]=s.useState(null),[S,k]=s.useState(!1),[v,b]=s.useState(null),C=s.useRef(null),y=r=>{c(g=>({...g,...r})),m(null)},w=async r=>{if(!h)return;k(!0),b(null);const g=new FormData;g.append("file",h),g.append("uid",i),Object.entries(p).forEach(([j,l])=>g.append(j,String(l)));try{const{data:j}=await o(r?x.routes.apply:x.routes.preview,g);m(j)}catch(j){b(j?.response?.data?.error?.message??"The file could not be read."),m(null)}finally{k(!1)}};return e.jsxs(n.Flex,{direction:"column",gap:4,alignItems:"stretch",children:[e.jsx(n.Typography,{variant:"delta",children:"Import"}),e.jsxs(n.Field.Root,{name:"import-uid",children:[e.jsx(n.Field.Label,{children:"Content type"}),e.jsx(n.SingleSelect,{value:i,onChange:r=>{d(String(r)),m(null)},children:t.map(r=>e.jsx(n.SingleSelectOption,{value:r.uid,children:r.label},r.uid))})]}),e.jsxs(n.Field.Root,{name:"import-file",children:[e.jsx(n.Field.Label,{children:"File"}),e.jsxs(n.Flex,{gap:2,alignItems:"center",children:[e.jsx(n.Button,{variant:"secondary",onClick:()=>C.current?.click(),children:"Choose a file"}),e.jsx(n.Typography,{variant:"pi",textColor:h?"neutral800":"neutral500",children:h?h.name:"JSON or CSV"})]}),e.jsx("input",{ref:C,type:"file",accept:".json,.csv,application/json,text/csv",style:{display:"none"},onChange:r=>{a(r.target.files?.[0]??null),m(null)}})]}),e.jsxs(n.Flex,{gap:4,wrap:"wrap",alignItems:"flex-end",children:[e.jsx(n.Box,{minWidth:"200px",children:e.jsxs(n.Field.Root,{name:"import-existing",children:[e.jsx(n.Field.Label,{children:"When it is already there"}),e.jsxs(n.SingleSelect,{value:p.onExisting,onChange:r=>y({onExisting:r==="skip"?"skip":"update"}),children:[e.jsx(n.SingleSelectOption,{value:"update",children:"Update it"}),e.jsx(n.SingleSelectOption,{value:"skip",children:"Leave it alone"})]})]})}),e.jsx(n.Box,{minWidth:"240px",children:e.jsxs(n.Field.Root,{name:"import-missing",children:[e.jsx(n.Field.Label,{children:"When a relation points at nothing"}),e.jsxs(n.SingleSelect,{value:p.onMissingRelation,onChange:r=>y({onMissingRelation:r==="fail"?"fail":"skip"}),children:[e.jsx(n.SingleSelectOption,{value:"skip",children:"Drop the link, keep the row"}),e.jsx(n.SingleSelectOption,{value:"fail",children:"Refuse the import"})]})]})}),e.jsx(n.Box,{minWidth:"160px",children:e.jsxs(n.Field.Root,{name:"import-status",children:[e.jsx(n.Field.Label,{children:"Import as"}),e.jsxs(n.SingleSelect,{value:p.status,onChange:r=>y({status:r}),children:[e.jsx(n.SingleSelectOption,{value:"draft",children:"Draft"}),e.jsx(n.SingleSelectOption,{value:"published",children:"Published"})]})]})})]}),e.jsx(n.Checkbox,{checked:p.continueOnError,onCheckedChange:r=>y({continueOnError:!!r}),children:"Keep going past a row that fails"}),e.jsx(n.Typography,{variant:"pi",textColor:"neutral600",children:p.continueOnError?"Rows that fail are reported and the rest are committed.":"All or nothing: if any row fails, the whole import is rolled back and nothing changes."}),e.jsxs(n.Flex,{gap:2,children:[e.jsx(n.Button,{variant:"secondary",startIcon:e.jsx(R.Upload,{}),onClick:()=>w(!1),loading:S,disabled:!h||!i,children:"Dry run"}),e.jsx(n.Button,{onClick:()=>w(!0),loading:S,disabled:!h||!i||!u||u.applied,children:"Import for real"})]}),!u&&h&&e.jsx(n.Typography,{variant:"pi",textColor:"neutral600",children:"Run the dry run first. Nothing is written until you have seen what it would do."}),v&&e.jsx(n.Typography,{variant:"pi",textColor:"danger600",children:v}),u&&e.jsx(W,{report:u})]})},O=({children:t})=>e.jsx(n.Box,{padding:6,hasRadius:!0,background:"neutral0",borderColor:"neutral200",borderWidth:"1px",borderStyle:"solid",flex:"1",minWidth:"380px",children:t}),P=()=>{const{get:t}=f.useFetchClient(),[o,i]=s.useState([]),[d,p]=s.useState(!0),[c,h]=s.useState(null);return s.useEffect(()=>{let a=!0;return t(x.routes.contentTypes).then(({data:u})=>a&&i(u.contentTypes)).catch(()=>a&&h("Could not read the list of content types.")).finally(()=>a&&p(!1)),()=>{a=!1}},[t]),e.jsxs(f.Page.Main,{children:[e.jsx(f.Layouts.Header,{title:"Ferry",subtitle:"Move content in and out as JSON or CSV, with relations and components intact"}),e.jsx(f.Layouts.Content,{children:d?e.jsx(n.Flex,{justifyContent:"center",padding:10,children:e.jsx(n.Loader,{small:!0,children:"Reading the schema"})}):c?e.jsx(n.Typography,{textColor:"danger600",children:c}):o.length===0?e.jsxs(n.Flex,{direction:"column",gap:2,padding:10,alignItems:"center",children:[e.jsx(n.Typography,{variant:"delta",children:"Nothing to carry yet"}),e.jsx(n.Typography,{textColor:"neutral600",children:"Create a content type, and it will show up here."})]}):e.jsxs(n.Flex,{gap:6,alignItems:"flex-start",wrap:"wrap",children:[e.jsx(O,{children:e.jsx(E,{contentTypes:o})}),e.jsx(O,{children:e.jsx(L,{contentTypes:o})})]})})]})};exports.TransferPage=P;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const y=require("@strapi/icons"),t=require("react/jsx-runtime"),F=require("react"),g=require("@strapi/admin/strapi-admin"),r=require("@strapi/design-system");function k(e){if(e&&e.__esModule)return e;const n=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const o in e)if(o!=="default"){const s=Object.getOwnPropertyDescriptor(e,o);Object.defineProperty(n,o,s.get?s:{enumerable:!0,get:()=>e[o]})}}return n.default=e,Object.freeze(n)}const p=k(F),$=(e,n,o)=>{const s=e[n];return s?typeof s=="function"?s():Promise.resolve(s):new Promise((i,a)=>{(typeof queueMicrotask=="function"?queueMicrotask:setTimeout)(a.bind(null,new Error("Unknown variable dynamic import: "+n+(n.split("/").length!==o?". Note that variables only represent file names one level deep.":""))))})},c="ferry",S={contentTypes:`/${c}/content-types`,plan:e=>`/${c}/plan/${e}`,export:`/${c}/export`,preview:`/${c}/import/preview`,apply:`/${c}/import`},h={format:"json",status:"draft",relations:!0,media:!0},M={status:"draft",onExisting:"update",onMissingRelation:"skip",publish:!0,continueOnError:!1},E=(e,n,o)=>{const s=URL.createObjectURL(new Blob([e],{type:o})),i=document.createElement("a");i.href=s,i.download=n,document.body.appendChild(i),i.click(),i.remove(),URL.revokeObjectURL(s)},I=e=>[e.created>0?`${e.created} to create`:null,e.updated>0?`${e.updated} to update`:null,e.skipped>0?`${e.skipped} skipped`:null,e.errored>0?`${e.errored} failed`:null].filter(Boolean).join(", ")||"nothing to do",O=({scope:e,request:n,uid:o,onClose:s})=>{const{post:i}=g.useFetchClient(),[a,d]=p.useState(h.format),[u,x]=p.useState(h.status),[m,j]=p.useState(!1),[b,v]=p.useState(null),[f,w]=p.useState([]),P=async()=>{j(!0),v(null),w([]);try{const{data:l}=await i(S.export,{uid:o,...h,...n,format:a,status:u,envelope:!0});if(E(l.body,l.filename,l.mime),l.warnings?.length){w(l.warnings);return}s()}catch(l){v(l?.response?.data?.error?.message??"The export failed.")}finally{j(!1)}};return t.jsxs(t.Fragment,{children:[t.jsx(r.Modal.Body,{children:t.jsxs(r.Flex,{direction:"column",alignItems:"stretch",gap:4,children:[t.jsxs(r.Typography,{textColor:"neutral700",children:["Exporting ",e,"."]}),t.jsxs(r.Field.Root,{name:"ferry-format",children:[t.jsx(r.Field.Label,{children:"Format"}),t.jsxs(r.SingleSelect,{value:a,onChange:l=>d(String(l)),disabled:m,children:[t.jsx(r.SingleSelectOption,{value:"json",children:"JSON"}),t.jsx(r.SingleSelectOption,{value:"csv",children:"CSV"})]})]}),t.jsxs(r.Field.Root,{name:"ferry-status",children:[t.jsx(r.Field.Label,{children:"Version"}),t.jsxs(r.SingleSelect,{value:u,onChange:l=>x(String(l)),disabled:m,children:[t.jsx(r.SingleSelectOption,{value:"draft",children:"Draft"}),t.jsx(r.SingleSelectOption,{value:"published",children:"Published"})]})]}),f.length>0?t.jsxs(r.Flex,{direction:"column",alignItems:"flex-start",gap:1,children:[t.jsx(r.Typography,{variant:"pi",textColor:"warning600",fontWeight:"bold",children:"The file was saved, with warnings:"}),f.map(l=>t.jsx(r.Typography,{variant:"pi",textColor:"warning600",children:l},l))]}):null,b?t.jsx(r.Typography,{textColor:"danger600",children:b}):null]})}),t.jsxs(r.Modal.Footer,{children:[t.jsx(r.Button,{variant:"tertiary",onClick:s,disabled:m,children:f.length>0?"Close":"Cancel"}),t.jsx(r.Button,{onClick:P,loading:m,children:"Export"})]})]})},C=e=>e.startsWith("api::"),R=[{action:`plugin::${c}.export`,subject:null}],T=e=>e.plugins?.i18n?.locale,L=({documents:e,model:n})=>{const{allowedActions:o,isLoading:s}=g.useRBAC({export:R}),[{query:i}]=g.useQueryParams();if(s||!o.canExport||!C(n))return null;const a=e.map(u=>u.documentId).filter(Boolean);if(a.length===0)return null;const d=a.length;return{label:"Export",icon:t.jsx(y.Download,{}),dialog:{type:"modal",title:"Export",content:({onClose:u})=>t.jsx(O,{uid:n,scope:`${d} selected ${d===1?"entry":"entries"}`,request:{documentIds:a,locale:T(i)},onClose:u})}}},_=()=>{const{allowedActions:e,isLoading:n}=g.useRBAC({export:R}),[{query:o}]=g.useQueryParams(),[s,i]=p.useState(!1),a=window.location.pathname.match(/\/content-manager\/collection-types\/([^/?#]+)/),d=a?decodeURIComponent(a[1]):null;if(n||!e.canExport||!d||!C(d))return null;const u=o.filters,x=!!(u&&Object.keys(u).length>0);return t.jsxs(t.Fragment,{children:[t.jsx(r.Button,{variant:"tertiary",startIcon:t.jsx(y.Download,{}),onClick:()=>i(!0),children:"Export"}),s?t.jsx(r.Modal.Root,{open:!0,onOpenChange:()=>i(!1),children:t.jsxs(r.Modal.Content,{children:[t.jsx(r.Modal.Header,{children:t.jsx(r.Modal.Title,{children:"Export"})}),t.jsx(O,{uid:d,scope:x?"everything matching this view":"every entry of this content type",request:{filters:u,sort:o.sort,locale:T(o)},onClose:()=>i(!1)})]})}):null]})},A={register(e){e.registerPlugin({id:c,name:c,isReady:!0}),e.addMenuLink({to:`/plugins/${c}`,icon:y.ArrowsHorizontal,intlLabel:{id:`${c}.menu.label`,defaultMessage:"Ferry"},Component:async()=>{const{TransferPage:n}=await Promise.resolve().then(()=>require("./TransferPage-DPZr7PFx.js"));return{default:n}},permissions:[{action:`plugin::${c}.export`,subject:null}]})},bootstrap(e){const n=e.getPlugin("content-manager");n.apis.addBulkAction(o=>{const s=o.findIndex(a=>a.type==="delete"),i=s===-1?o.length:s;return[...o.slice(0,i),L,...o.slice(i)]}),n.injectComponent("listView","actions",{name:`${c}-export-view`,Component:_})},async registerTrads({locales:e}){return Promise.all(e.map(async n=>{try{const{default:o}=await $(Object.assign({}),`./translations/${n}.json`,3);return{data:o,locale:n}}catch{return{data:{},locale:n}}}))}};exports.DEFAULT_EXPORT=h;exports.DEFAULT_IMPORT=M;exports.PLUGIN_ID=c;exports.index=A;exports.routes=S;exports.saveFile=E;exports.summarise=I;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { Download as O, ArrowsHorizontal as M } from "@strapi/icons";
|
|
2
|
+
import { jsxs as c, Fragment as P, jsx as n } from "react/jsx-runtime";
|
|
3
|
+
import * as p from "react";
|
|
4
|
+
import { useFetchClient as U, useRBAC as j, useQueryParams as I } from "@strapi/admin/strapi-admin";
|
|
5
|
+
import { Modal as m, Flex as k, Typography as g, Field as f, SingleSelect as R, SingleSelectOption as y, Button as w } from "@strapi/design-system";
|
|
6
|
+
const D = (e, t, o) => {
|
|
7
|
+
const r = e[t];
|
|
8
|
+
return r ? typeof r == "function" ? r() : Promise.resolve(r) : new Promise((a, l) => {
|
|
9
|
+
(typeof queueMicrotask == "function" ? queueMicrotask : setTimeout)(
|
|
10
|
+
l.bind(
|
|
11
|
+
null,
|
|
12
|
+
new Error(
|
|
13
|
+
"Unknown variable dynamic import: " + t + (t.split("/").length !== o ? ". Note that variables only represent file names one level deep." : "")
|
|
14
|
+
)
|
|
15
|
+
)
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
}, s = "ferry", _ = {
|
|
19
|
+
contentTypes: `/${s}/content-types`,
|
|
20
|
+
plan: (e) => `/${s}/plan/${e}`,
|
|
21
|
+
export: `/${s}/export`,
|
|
22
|
+
preview: `/${s}/import/preview`,
|
|
23
|
+
apply: `/${s}/import`
|
|
24
|
+
}, v = {
|
|
25
|
+
format: "json",
|
|
26
|
+
status: "draft",
|
|
27
|
+
relations: !0,
|
|
28
|
+
media: !0
|
|
29
|
+
}, G = {
|
|
30
|
+
status: "draft",
|
|
31
|
+
onExisting: "update",
|
|
32
|
+
onMissingRelation: "skip",
|
|
33
|
+
publish: !0,
|
|
34
|
+
continueOnError: !1
|
|
35
|
+
}, q = (e, t, o) => {
|
|
36
|
+
const r = URL.createObjectURL(new Blob([e], { type: o })), a = document.createElement("a");
|
|
37
|
+
a.href = r, a.download = t, document.body.appendChild(a), a.click(), a.remove(), URL.revokeObjectURL(r);
|
|
38
|
+
}, J = (e) => [
|
|
39
|
+
e.created > 0 ? `${e.created} to create` : null,
|
|
40
|
+
e.updated > 0 ? `${e.updated} to update` : null,
|
|
41
|
+
e.skipped > 0 ? `${e.skipped} skipped` : null,
|
|
42
|
+
e.errored > 0 ? `${e.errored} failed` : null
|
|
43
|
+
].filter(Boolean).join(", ") || "nothing to do", L = ({
|
|
44
|
+
/** What is being exported, in words, e.g. "3 selected entries". */
|
|
45
|
+
scope: e,
|
|
46
|
+
/** The scope as request keys - `documentIds`, or `filters` and `locale`. */
|
|
47
|
+
request: t,
|
|
48
|
+
uid: o,
|
|
49
|
+
onClose: r
|
|
50
|
+
}) => {
|
|
51
|
+
const { post: a } = U(), [l, d] = p.useState(v.format), [u, x] = p.useState(v.status), [h, C] = p.useState(!1), [E, S] = p.useState(null), [b, $] = p.useState([]), A = async () => {
|
|
52
|
+
C(!0), S(null), $([]);
|
|
53
|
+
try {
|
|
54
|
+
const { data: i } = await a(_.export, {
|
|
55
|
+
uid: o,
|
|
56
|
+
...v,
|
|
57
|
+
...t,
|
|
58
|
+
format: l,
|
|
59
|
+
status: u,
|
|
60
|
+
envelope: !0
|
|
61
|
+
});
|
|
62
|
+
if (q(i.body, i.filename, i.mime), i.warnings?.length) {
|
|
63
|
+
$(i.warnings);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
r();
|
|
67
|
+
} catch (i) {
|
|
68
|
+
S(i?.response?.data?.error?.message ?? "The export failed.");
|
|
69
|
+
} finally {
|
|
70
|
+
C(!1);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
return /* @__PURE__ */ c(P, { children: [
|
|
74
|
+
/* @__PURE__ */ n(m.Body, { children: /* @__PURE__ */ c(k, { direction: "column", alignItems: "stretch", gap: 4, children: [
|
|
75
|
+
/* @__PURE__ */ c(g, { textColor: "neutral700", children: [
|
|
76
|
+
"Exporting ",
|
|
77
|
+
e,
|
|
78
|
+
"."
|
|
79
|
+
] }),
|
|
80
|
+
/* @__PURE__ */ c(f.Root, { name: "ferry-format", children: [
|
|
81
|
+
/* @__PURE__ */ n(f.Label, { children: "Format" }),
|
|
82
|
+
/* @__PURE__ */ c(
|
|
83
|
+
R,
|
|
84
|
+
{
|
|
85
|
+
value: l,
|
|
86
|
+
onChange: (i) => d(String(i)),
|
|
87
|
+
disabled: h,
|
|
88
|
+
children: [
|
|
89
|
+
/* @__PURE__ */ n(y, { value: "json", children: "JSON" }),
|
|
90
|
+
/* @__PURE__ */ n(y, { value: "csv", children: "CSV" })
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
] }),
|
|
95
|
+
/* @__PURE__ */ c(f.Root, { name: "ferry-status", children: [
|
|
96
|
+
/* @__PURE__ */ n(f.Label, { children: "Version" }),
|
|
97
|
+
/* @__PURE__ */ c(
|
|
98
|
+
R,
|
|
99
|
+
{
|
|
100
|
+
value: u,
|
|
101
|
+
onChange: (i) => x(String(i)),
|
|
102
|
+
disabled: h,
|
|
103
|
+
children: [
|
|
104
|
+
/* @__PURE__ */ n(y, { value: "draft", children: "Draft" }),
|
|
105
|
+
/* @__PURE__ */ n(y, { value: "published", children: "Published" })
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
] }),
|
|
110
|
+
b.length > 0 ? /* @__PURE__ */ c(k, { direction: "column", alignItems: "flex-start", gap: 1, children: [
|
|
111
|
+
/* @__PURE__ */ n(g, { variant: "pi", textColor: "warning600", fontWeight: "bold", children: "The file was saved, with warnings:" }),
|
|
112
|
+
b.map((i) => /* @__PURE__ */ n(g, { variant: "pi", textColor: "warning600", children: i }, i))
|
|
113
|
+
] }) : null,
|
|
114
|
+
E ? /* @__PURE__ */ n(g, { textColor: "danger600", children: E }) : null
|
|
115
|
+
] }) }),
|
|
116
|
+
/* @__PURE__ */ c(m.Footer, { children: [
|
|
117
|
+
/* @__PURE__ */ n(w, { variant: "tertiary", onClick: r, disabled: h, children: b.length > 0 ? "Close" : "Cancel" }),
|
|
118
|
+
/* @__PURE__ */ n(w, { onClick: A, loading: h, children: "Export" })
|
|
119
|
+
] })
|
|
120
|
+
] });
|
|
121
|
+
}, T = (e) => e.startsWith("api::"), F = [{ action: `plugin::${s}.export`, subject: null }], B = (e) => e.plugins?.i18n?.locale, N = ({ documents: e, model: t }) => {
|
|
122
|
+
const { allowedActions: o, isLoading: r } = j({ export: F }), [{ query: a }] = I();
|
|
123
|
+
if (r || !o.canExport || !T(t)) return null;
|
|
124
|
+
const l = e.map((u) => u.documentId).filter(Boolean);
|
|
125
|
+
if (l.length === 0) return null;
|
|
126
|
+
const d = l.length;
|
|
127
|
+
return {
|
|
128
|
+
label: "Export",
|
|
129
|
+
icon: /* @__PURE__ */ n(O, {}),
|
|
130
|
+
dialog: {
|
|
131
|
+
type: "modal",
|
|
132
|
+
title: "Export",
|
|
133
|
+
content: ({ onClose: u }) => /* @__PURE__ */ n(
|
|
134
|
+
L,
|
|
135
|
+
{
|
|
136
|
+
uid: t,
|
|
137
|
+
scope: `${d} selected ${d === 1 ? "entry" : "entries"}`,
|
|
138
|
+
request: { documentIds: l, locale: B(a) },
|
|
139
|
+
onClose: u
|
|
140
|
+
}
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}, V = () => {
|
|
145
|
+
const { allowedActions: e, isLoading: t } = j({ export: F }), [{ query: o }] = I(), [r, a] = p.useState(!1), l = window.location.pathname.match(/\/content-manager\/collection-types\/([^/?#]+)/), d = l ? decodeURIComponent(l[1]) : null;
|
|
146
|
+
if (t || !e.canExport || !d || !T(d)) return null;
|
|
147
|
+
const u = o.filters, x = !!(u && Object.keys(u).length > 0);
|
|
148
|
+
return /* @__PURE__ */ c(P, { children: [
|
|
149
|
+
/* @__PURE__ */ n(w, { variant: "tertiary", startIcon: /* @__PURE__ */ n(O, {}), onClick: () => a(!0), children: "Export" }),
|
|
150
|
+
r ? /* @__PURE__ */ n(m.Root, { open: !0, onOpenChange: () => a(!1), children: /* @__PURE__ */ c(m.Content, { children: [
|
|
151
|
+
/* @__PURE__ */ n(m.Header, { children: /* @__PURE__ */ n(m.Title, { children: "Export" }) }),
|
|
152
|
+
/* @__PURE__ */ n(
|
|
153
|
+
L,
|
|
154
|
+
{
|
|
155
|
+
uid: d,
|
|
156
|
+
scope: x ? "everything matching this view" : "every entry of this content type",
|
|
157
|
+
request: { filters: u, sort: o.sort, locale: B(o) },
|
|
158
|
+
onClose: () => a(!1)
|
|
159
|
+
}
|
|
160
|
+
)
|
|
161
|
+
] }) }) : null
|
|
162
|
+
] });
|
|
163
|
+
}, Q = {
|
|
164
|
+
register(e) {
|
|
165
|
+
e.registerPlugin({ id: s, name: s, isReady: !0 }), e.addMenuLink({
|
|
166
|
+
to: `/plugins/${s}`,
|
|
167
|
+
icon: M,
|
|
168
|
+
intlLabel: { id: `${s}.menu.label`, defaultMessage: "Ferry" },
|
|
169
|
+
Component: async () => {
|
|
170
|
+
const { TransferPage: t } = await import("./TransferPage-B0Le6wyG.mjs");
|
|
171
|
+
return { default: t };
|
|
172
|
+
},
|
|
173
|
+
permissions: [{ action: `plugin::${s}.export`, subject: null }]
|
|
174
|
+
});
|
|
175
|
+
},
|
|
176
|
+
bootstrap(e) {
|
|
177
|
+
const t = e.getPlugin("content-manager");
|
|
178
|
+
t.apis.addBulkAction((o) => {
|
|
179
|
+
const r = o.findIndex((l) => l.type === "delete"), a = r === -1 ? o.length : r;
|
|
180
|
+
return [...o.slice(0, a), N, ...o.slice(a)];
|
|
181
|
+
}), t.injectComponent("listView", "actions", {
|
|
182
|
+
name: `${s}-export-view`,
|
|
183
|
+
Component: V
|
|
184
|
+
});
|
|
185
|
+
},
|
|
186
|
+
async registerTrads({ locales: e }) {
|
|
187
|
+
return Promise.all(
|
|
188
|
+
e.map(async (t) => {
|
|
189
|
+
try {
|
|
190
|
+
const { default: o } = await D(/* @__PURE__ */ Object.assign({}), `./translations/${t}.json`, 3);
|
|
191
|
+
return { data: o, locale: t };
|
|
192
|
+
} catch {
|
|
193
|
+
return { data: {}, locale: t };
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
export {
|
|
200
|
+
v as D,
|
|
201
|
+
s as P,
|
|
202
|
+
J as a,
|
|
203
|
+
G as b,
|
|
204
|
+
Q as i,
|
|
205
|
+
_ as r,
|
|
206
|
+
q as s
|
|
207
|
+
};
|
package/dist/admin/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("../_chunks/index-9EKjguHv.js");require("@strapi/icons");exports.PLUGIN_ID=e.PLUGIN_ID;exports.default=e.index;
|
package/dist/admin/index.mjs
CHANGED
|
@@ -1,43 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
const t = n[e];
|
|
4
|
-
return t ? typeof t == "function" ? t() : Promise.resolve(t) : new Promise((u, a) => {
|
|
5
|
-
(typeof queueMicrotask == "function" ? queueMicrotask : setTimeout)(
|
|
6
|
-
a.bind(
|
|
7
|
-
null,
|
|
8
|
-
new Error(
|
|
9
|
-
"Unknown variable dynamic import: " + e + (e.split("/").length !== i ? ". Note that variables only represent file names one level deep." : "")
|
|
10
|
-
)
|
|
11
|
-
)
|
|
12
|
-
);
|
|
13
|
-
});
|
|
14
|
-
}, r = "ferry", c = {
|
|
15
|
-
register(n) {
|
|
16
|
-
n.registerPlugin({ id: r, name: r, isReady: !0 }), n.addMenuLink({
|
|
17
|
-
to: `/plugins/${r}`,
|
|
18
|
-
icon: s,
|
|
19
|
-
intlLabel: { id: `${r}.menu.label`, defaultMessage: "Ferry" },
|
|
20
|
-
Component: async () => {
|
|
21
|
-
const { TransferPage: e } = await import("../_chunks/TransferPage-c8hdFQDQ.mjs");
|
|
22
|
-
return { default: e };
|
|
23
|
-
},
|
|
24
|
-
permissions: [{ action: `plugin::${r}.export`, subject: null }]
|
|
25
|
-
});
|
|
26
|
-
},
|
|
27
|
-
async registerTrads({ locales: n }) {
|
|
28
|
-
return Promise.all(
|
|
29
|
-
n.map(async (e) => {
|
|
30
|
-
try {
|
|
31
|
-
const { default: i } = await o(/* @__PURE__ */ Object.assign({}), `./translations/${e}.json`, 3);
|
|
32
|
-
return { data: i, locale: e };
|
|
33
|
-
} catch {
|
|
34
|
-
return { data: {}, locale: e };
|
|
35
|
-
}
|
|
36
|
-
})
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
};
|
|
1
|
+
import { P as t, i as e } from "../_chunks/index-C3-QXVsq.mjs";
|
|
2
|
+
import "@strapi/icons";
|
|
40
3
|
export {
|
|
41
|
-
|
|
42
|
-
|
|
4
|
+
t as PLUGIN_ID,
|
|
5
|
+
e as default
|
|
43
6
|
};
|
package/dist/admin/src/api.d.ts
CHANGED
|
@@ -75,7 +75,17 @@ export interface ExportRequest {
|
|
|
75
75
|
status: Status;
|
|
76
76
|
relations: boolean;
|
|
77
77
|
media: boolean;
|
|
78
|
+
/** Export exactly these documents - the Content Manager's selected rows. */
|
|
78
79
|
documentIds?: string[];
|
|
80
|
+
/**
|
|
81
|
+
* Strapi filters, passed through untouched - the Content Manager's current
|
|
82
|
+
* view. Ignored when `documentIds` is set, which the server resolves by
|
|
83
|
+
* overwriting `filters.documentId`.
|
|
84
|
+
*/
|
|
85
|
+
filters?: Record<string, unknown>;
|
|
86
|
+
/** Which locale to take, when the content type has more than one. */
|
|
87
|
+
locale?: string;
|
|
88
|
+
sort?: string;
|
|
79
89
|
}
|
|
80
90
|
export interface ImportRequest {
|
|
81
91
|
uid: string;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type ExportRequest } from '../api';
|
|
3
|
+
/**
|
|
4
|
+
* The export dialog the Content Manager opens, for either scope.
|
|
5
|
+
*
|
|
6
|
+
* One component for "the rows I ticked" and "everything in this view", because
|
|
7
|
+
* the two differ only in which keys of the request are filled in. What they
|
|
8
|
+
* must not differ in is the answer to "what am I about to get" - a dialog that
|
|
9
|
+
* asks the same questions but means something different each time is worse
|
|
10
|
+
* than two dialogs.
|
|
11
|
+
*
|
|
12
|
+
* Only format and status are offered. The full page keeps the rest: relations
|
|
13
|
+
* and media are defaults nobody changes per export, and a list view is not the
|
|
14
|
+
* place to relearn them.
|
|
15
|
+
*/
|
|
16
|
+
export declare const ExportScopeDialog: ({ scope, request, uid, onClose, }: {
|
|
17
|
+
scope: string;
|
|
18
|
+
request: Partial<ExportRequest>;
|
|
19
|
+
uid: string;
|
|
20
|
+
onClose: () => void;
|
|
21
|
+
}) => React.JSX.Element;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Export the ticked rows.
|
|
4
|
+
*
|
|
5
|
+
* A bulk action, so it appears only once something is selected, next to Delete
|
|
6
|
+
* and Publish. That is the shape of the request it answers: "a copy of the
|
|
7
|
+
* twelve articles I just fixed".
|
|
8
|
+
*/
|
|
9
|
+
export declare const ExportBulkAction: ({ documents, model }: {
|
|
10
|
+
documents: any[];
|
|
11
|
+
model: string;
|
|
12
|
+
}) => {
|
|
13
|
+
label: string;
|
|
14
|
+
icon: React.JSX.Element;
|
|
15
|
+
dialog: {
|
|
16
|
+
type: "modal";
|
|
17
|
+
title: string;
|
|
18
|
+
content: ({ onClose }: {
|
|
19
|
+
onClose: () => void;
|
|
20
|
+
}) => React.JSX.Element;
|
|
21
|
+
};
|
|
22
|
+
} | null;
|
|
23
|
+
/**
|
|
24
|
+
* Export everything the current filters match.
|
|
25
|
+
*
|
|
26
|
+
* The other half of the same job, and it cannot be the same control: a bulk
|
|
27
|
+
* action exists only while rows are ticked, and ticking is per page. Narrowing
|
|
28
|
+
* a list to three hundred entries and wanting all of them is the case this
|
|
29
|
+
* covers, and there is no way to tick them.
|
|
30
|
+
*
|
|
31
|
+
* Lives in the `listView.actions` injection zone, beside the view settings -
|
|
32
|
+
* always visible, because the filters it follows always exist, even when they
|
|
33
|
+
* are "no filters at all".
|
|
34
|
+
*/
|
|
35
|
+
export declare const ExportViewAction: () => React.JSX.Element | null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qkix/strapi-plugin-ferry",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Import and export Strapi v5 content as JSON or CSV - relations, components and dynamic zones included, with a dry run before anything is written",
|
|
5
5
|
"strapi": {
|
|
6
6
|
"kind": "plugin",
|
|
@@ -57,9 +57,9 @@
|
|
|
57
57
|
"styled-components": "^6.1.17",
|
|
58
58
|
"typescript": "^5.8.3",
|
|
59
59
|
"vitest": "^3.2.4",
|
|
60
|
+
"@qkix/eslint-config": "0.0.0",
|
|
60
61
|
"@qkix/strapi-test-harness": "0.0.0",
|
|
61
|
-
"@qkix/tsconfig": "0.0.0"
|
|
62
|
-
"@qkix/eslint-config": "0.0.0"
|
|
62
|
+
"@qkix/tsconfig": "0.0.0"
|
|
63
63
|
},
|
|
64
64
|
"peerDependencies": {
|
|
65
65
|
"@strapi/admin": "^5.0.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),B=require("react"),y=require("@strapi/admin/strapi-admin"),n=require("@strapi/design-system"),O=require("@strapi/icons"),F=require("../admin/index.js");function L(t){if(t&&t.__esModule)return t;const s=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t){for(const i in t)if(i!=="default"){const o=Object.getOwnPropertyDescriptor(t,i);Object.defineProperty(s,i,o.get?o:{enumerable:!0,get:()=>t[i]})}}return s.default=t,Object.freeze(s)}const d=L(B),k={contentTypes:`/${F.PLUGIN_ID}/content-types`,plan:t=>`/${F.PLUGIN_ID}/plan/${t}`,export:`/${F.PLUGIN_ID}/export`,preview:`/${F.PLUGIN_ID}/import/preview`,apply:`/${F.PLUGIN_ID}/import`},E={format:"json",status:"draft",relations:!0,media:!0},P={status:"draft",onExisting:"update",onMissingRelation:"skip",publish:!0,continueOnError:!1},$=(t,s,i)=>{const o=URL.createObjectURL(new Blob([t],{type:i})),c=document.createElement("a");c.href=o,c.download=s,document.body.appendChild(c),c.click(),c.remove(),URL.revokeObjectURL(o)},D=t=>[t.created>0?`${t.created} to create`:null,t.updated>0?`${t.updated} to update`:null,t.skipped>0?`${t.skipped} skipped`:null,t.errored>0?`${t.errored} failed`:null].filter(Boolean).join(", ")||"nothing to do",W=({contentTypes:t})=>{const{get:s,post:i}=y.useFetchClient(),[o,c]=d.useState(t[0]?.uid??""),[h,p]=d.useState(E),[r,u]=d.useState(null),[j,f]=d.useState(!1),[R,S]=d.useState([]),[v,b]=d.useState(null),[m,C]=d.useState(null);d.useEffect(()=>{if(!o)return;let l=!0;return s(k.plan(o)).then(({data:w})=>l&&u(w)).catch(()=>l&&u(null)),()=>{l=!1}},[s,o]);const a=l=>p(w=>({...w,...l})),x=async()=>{f(!0),b(null),S([]),C(null);try{const{data:l}=await i(k.export,{uid:o,...h,envelope:!0});$(l.body,l.filename,l.mime),S(l.warnings??[]),C(`${l.filename} (${l.count} document${l.count===1?"":"s"})`)}catch(l){b(l?.response?.data?.error?.message??"The export failed.")}finally{f(!1)}},g=r?[`${r.scalars.length} field(s)`,h.relations&&r.relations.length>0?`${r.relations.length} relation(s)`:null,r.components.length>0?`${r.components.length} component(s)`:null,r.dynamicZones.length>0?`${r.dynamicZones.length} dynamic zone(s)`:null,h.media&&r.media.length>0?`${r.media.length} media field(s)`:null].filter(Boolean):[];return e.jsxs(n.Flex,{direction:"column",gap:4,alignItems:"stretch",children:[e.jsx(n.Typography,{variant:"delta",children:"Export"}),e.jsxs(n.Field.Root,{name:"export-uid",children:[e.jsx(n.Field.Label,{children:"Content type"}),e.jsx(n.SingleSelect,{value:o,onChange:l=>c(String(l)),children:t.map(l=>e.jsx(n.SingleSelectOption,{value:l.uid,children:l.label},l.uid))})]}),e.jsxs(n.Flex,{gap:4,wrap:"wrap",alignItems:"flex-end",children:[e.jsx(n.Box,{minWidth:"160px",children:e.jsxs(n.Field.Root,{name:"export-format",children:[e.jsx(n.Field.Label,{children:"Format"}),e.jsxs(n.SingleSelect,{value:h.format,onChange:l=>a({format:l}),children:[e.jsx(n.SingleSelectOption,{value:"json",children:"JSON - everything"}),e.jsx(n.SingleSelectOption,{value:"csv",children:"CSV - flat fields"})]})]})}),e.jsx(n.Box,{minWidth:"160px",children:e.jsxs(n.Field.Root,{name:"export-status",children:[e.jsx(n.Field.Label,{children:"Version"}),e.jsxs(n.SingleSelect,{value:h.status,onChange:l=>a({status:l}),disabled:r?!r.draftAndPublish:!1,children:[e.jsx(n.SingleSelectOption,{value:"draft",children:"Draft"}),e.jsx(n.SingleSelectOption,{value:"published",children:"Published"})]})]})})]}),e.jsxs(n.Flex,{gap:4,wrap:"wrap",children:[e.jsx(n.Checkbox,{checked:h.relations,onCheckedChange:l=>a({relations:!!l}),children:"Relations"}),e.jsx(n.Checkbox,{checked:h.media,onCheckedChange:l=>a({media:!!l}),children:"Media references"})]}),g.length>0&&e.jsxs(n.Typography,{variant:"pi",textColor:"neutral600",children:["Carries ",g.join(", "),".",h.format==="csv"&&(r?.components.length||r?.dynamicZones.length)?" CSV leaves components and dynamic zones behind - use JSON to keep them.":""]}),e.jsx(n.Box,{children:e.jsx(n.Button,{startIcon:e.jsx(O.Download,{}),onClick:x,loading:j,disabled:!o,children:"Export"})}),m&&e.jsxs(n.Typography,{variant:"pi",textColor:"success600",children:["Saved ",m,"."]}),R.map((l,w)=>e.jsx(n.Typography,{variant:"pi",textColor:"warning700",children:l},w)),v&&e.jsx(n.Typography,{variant:"pi",textColor:"danger600",children:v})]})},T=({label:t,value:s,tone:i})=>e.jsxs(n.Flex,{direction:"column",gap:1,alignItems:"flex-start",children:[e.jsx(n.Typography,{variant:"beta",textColor:s>0?i:"neutral500",children:s}),e.jsx(n.Typography,{variant:"pi",textColor:"neutral600",children:t})]}),U=({report:t})=>{const s=t.rows.filter(i=>i.outcome==="error");return e.jsxs(n.Flex,{direction:"column",gap:4,alignItems:"stretch",children:[e.jsxs(n.Box,{padding:4,hasRadius:!0,background:"neutral0",borderColor:"neutral200",borderWidth:"1px",borderStyle:"solid",children:[e.jsxs(n.Flex,{justifyContent:"space-between",alignItems:"center",paddingBottom:3,children:[e.jsx(n.Typography,{variant:"delta",children:t.applied?"Imported":"Dry run - nothing written yet"}),e.jsx(n.Badge,{children:D(t)})]}),e.jsxs(n.Flex,{gap:8,wrap:"wrap",children:[e.jsx(T,{label:"in the file",value:t.total,tone:"neutral800"}),e.jsx(T,{label:"to create",value:t.created,tone:"success600"}),e.jsx(T,{label:"to update",value:t.updated,tone:"primary600"}),e.jsx(T,{label:"skipped",value:t.skipped,tone:"neutral600"}),e.jsx(T,{label:"failed",value:t.errored,tone:"danger600"})]})]}),t.warnings.map((i,o)=>e.jsx(n.Box,{padding:3,hasRadius:!0,background:"warning100",borderColor:"warning200",borderWidth:"1px",borderStyle:"solid",children:e.jsx(n.Typography,{variant:"pi",textColor:"warning700",children:i})},o)),t.unresolved.length>0&&e.jsxs(n.Box,{padding:4,hasRadius:!0,background:"neutral0",borderColor:"warning200",borderWidth:"1px",borderStyle:"solid",children:[e.jsx(n.Typography,{variant:"delta",children:"Relations that point at nothing"}),e.jsx(n.Box,{paddingTop:1,paddingBottom:3,children:e.jsx(n.Typography,{variant:"pi",textColor:"neutral600",children:'These targets are neither in the file nor in this environment. With "drop the link" the rows still import without them; with "refuse the import" nothing is written at all.'})}),e.jsxs(n.Flex,{direction:"column",gap:2,alignItems:"stretch",children:[t.unresolved.slice(0,25).map((i,o)=>e.jsxs(n.Flex,{gap:3,alignItems:"baseline",wrap:"wrap",children:[e.jsxs(n.Badge,{children:["row ",i.row]}),e.jsx(n.Typography,{variant:"omega",fontWeight:"bold",children:i.field}),e.jsx(n.Typography,{variant:"pi",textColor:"neutral600",children:i.target}),e.jsx(n.Typography,{variant:"pi",textColor:"danger600",children:i.keys.join(", ")})]},o)),t.unresolved.length>25&&e.jsxs(n.Typography,{variant:"pi",textColor:"neutral600",children:["and ",t.unresolved.length-25," more"]})]})]}),s.length>0&&e.jsxs(n.Box,{padding:4,hasRadius:!0,background:"neutral0",borderColor:"danger200",borderWidth:"1px",borderStyle:"solid",children:[e.jsx(n.Typography,{variant:"delta",textColor:"danger600",children:"Rows that failed"}),e.jsx(n.Box,{paddingTop:2,children:e.jsx(n.Flex,{direction:"column",gap:2,alignItems:"stretch",children:s.slice(0,25).map(i=>e.jsxs(n.Flex,{gap:3,alignItems:"baseline",wrap:"wrap",children:[e.jsxs(n.Badge,{children:["row ",i.row]}),e.jsx(n.Typography,{variant:"pi",textColor:"neutral600",children:i.documentId??"no documentId"}),e.jsx(n.Typography,{variant:"pi",textColor:"danger600",children:i.message})]},i.row))})})]})]})},N=({contentTypes:t})=>{const{post:s}=y.useFetchClient(),[i,o]=d.useState(t[0]?.uid??""),[c,h]=d.useState(P),[p,r]=d.useState(null),[u,j]=d.useState(null),[f,R]=d.useState(!1),[S,v]=d.useState(null),b=d.useRef(null),m=a=>{h(x=>({...x,...a})),j(null)},C=async a=>{if(!p)return;R(!0),v(null);const x=new FormData;x.append("file",p),x.append("uid",i),Object.entries(c).forEach(([g,l])=>x.append(g,String(l)));try{const{data:g}=await s(a?k.apply:k.preview,x);j(g)}catch(g){v(g?.response?.data?.error?.message??"The file could not be read."),j(null)}finally{R(!1)}};return e.jsxs(n.Flex,{direction:"column",gap:4,alignItems:"stretch",children:[e.jsx(n.Typography,{variant:"delta",children:"Import"}),e.jsxs(n.Field.Root,{name:"import-uid",children:[e.jsx(n.Field.Label,{children:"Content type"}),e.jsx(n.SingleSelect,{value:i,onChange:a=>{o(String(a)),j(null)},children:t.map(a=>e.jsx(n.SingleSelectOption,{value:a.uid,children:a.label},a.uid))})]}),e.jsxs(n.Field.Root,{name:"import-file",children:[e.jsx(n.Field.Label,{children:"File"}),e.jsxs(n.Flex,{gap:2,alignItems:"center",children:[e.jsx(n.Button,{variant:"secondary",onClick:()=>b.current?.click(),children:"Choose a file"}),e.jsx(n.Typography,{variant:"pi",textColor:p?"neutral800":"neutral500",children:p?p.name:"JSON or CSV"})]}),e.jsx("input",{ref:b,type:"file",accept:".json,.csv,application/json,text/csv",style:{display:"none"},onChange:a=>{r(a.target.files?.[0]??null),j(null)}})]}),e.jsxs(n.Flex,{gap:4,wrap:"wrap",alignItems:"flex-end",children:[e.jsx(n.Box,{minWidth:"200px",children:e.jsxs(n.Field.Root,{name:"import-existing",children:[e.jsx(n.Field.Label,{children:"When it is already there"}),e.jsxs(n.SingleSelect,{value:c.onExisting,onChange:a=>m({onExisting:a==="skip"?"skip":"update"}),children:[e.jsx(n.SingleSelectOption,{value:"update",children:"Update it"}),e.jsx(n.SingleSelectOption,{value:"skip",children:"Leave it alone"})]})]})}),e.jsx(n.Box,{minWidth:"240px",children:e.jsxs(n.Field.Root,{name:"import-missing",children:[e.jsx(n.Field.Label,{children:"When a relation points at nothing"}),e.jsxs(n.SingleSelect,{value:c.onMissingRelation,onChange:a=>m({onMissingRelation:a==="fail"?"fail":"skip"}),children:[e.jsx(n.SingleSelectOption,{value:"skip",children:"Drop the link, keep the row"}),e.jsx(n.SingleSelectOption,{value:"fail",children:"Refuse the import"})]})]})}),e.jsx(n.Box,{minWidth:"160px",children:e.jsxs(n.Field.Root,{name:"import-status",children:[e.jsx(n.Field.Label,{children:"Import as"}),e.jsxs(n.SingleSelect,{value:c.status,onChange:a=>m({status:a}),children:[e.jsx(n.SingleSelectOption,{value:"draft",children:"Draft"}),e.jsx(n.SingleSelectOption,{value:"published",children:"Published"})]})]})})]}),e.jsx(n.Checkbox,{checked:c.continueOnError,onCheckedChange:a=>m({continueOnError:!!a}),children:"Keep going past a row that fails"}),e.jsx(n.Typography,{variant:"pi",textColor:"neutral600",children:c.continueOnError?"Rows that fail are reported and the rest are committed.":"All or nothing: if any row fails, the whole import is rolled back and nothing changes."}),e.jsxs(n.Flex,{gap:2,children:[e.jsx(n.Button,{variant:"secondary",startIcon:e.jsx(O.Upload,{}),onClick:()=>C(!1),loading:f,disabled:!p||!i,children:"Dry run"}),e.jsx(n.Button,{onClick:()=>C(!0),loading:f,disabled:!p||!i||!u||u.applied,children:"Import for real"})]}),!u&&p&&e.jsx(n.Typography,{variant:"pi",textColor:"neutral600",children:"Run the dry run first. Nothing is written until you have seen what it would do."}),S&&e.jsx(n.Typography,{variant:"pi",textColor:"danger600",children:S}),u&&e.jsx(U,{report:u})]})},I=({children:t})=>e.jsx(n.Box,{padding:6,hasRadius:!0,background:"neutral0",borderColor:"neutral200",borderWidth:"1px",borderStyle:"solid",flex:"1",minWidth:"380px",children:t}),_=()=>{const{get:t}=y.useFetchClient(),[s,i]=d.useState([]),[o,c]=d.useState(!0),[h,p]=d.useState(null);return d.useEffect(()=>{let r=!0;return t(k.contentTypes).then(({data:u})=>r&&i(u.contentTypes)).catch(()=>r&&p("Could not read the list of content types.")).finally(()=>r&&c(!1)),()=>{r=!1}},[t]),e.jsxs(y.Page.Main,{children:[e.jsx(y.Layouts.Header,{title:"Ferry",subtitle:"Move content in and out as JSON or CSV, with relations and components intact"}),e.jsx(y.Layouts.Content,{children:o?e.jsx(n.Flex,{justifyContent:"center",padding:10,children:e.jsx(n.Loader,{small:!0,children:"Reading the schema"})}):h?e.jsx(n.Typography,{textColor:"danger600",children:h}):s.length===0?e.jsxs(n.Flex,{direction:"column",gap:2,padding:10,alignItems:"center",children:[e.jsx(n.Typography,{variant:"delta",children:"Nothing to carry yet"}),e.jsx(n.Typography,{textColor:"neutral600",children:"Create a content type, and it will show up here."})]}):e.jsxs(n.Flex,{gap:6,alignItems:"flex-start",wrap:"wrap",children:[e.jsx(I,{children:e.jsx(W,{contentTypes:s})}),e.jsx(I,{children:e.jsx(N,{contentTypes:s})})]})})]})};exports.TransferPage=_;
|
|
@@ -1,398 +0,0 @@
|
|
|
1
|
-
import { jsxs as t, jsx as e } from "react/jsx-runtime";
|
|
2
|
-
import * as d from "react";
|
|
3
|
-
import { useFetchClient as F, Page as N, Layouts as U } from "@strapi/admin/strapi-admin";
|
|
4
|
-
import { Flex as s, Typography as i, Field as p, SingleSelect as k, SingleSelectOption as v, Box as m, Checkbox as D, Button as B, Badge as P, Loader as V } from "@strapi/design-system";
|
|
5
|
-
import { Download as A, Upload as J } from "@strapi/icons";
|
|
6
|
-
import { PLUGIN_ID as O } from "../admin/index.mjs";
|
|
7
|
-
const j = {
|
|
8
|
-
contentTypes: `/${O}/content-types`,
|
|
9
|
-
plan: (n) => `/${O}/plan/${n}`,
|
|
10
|
-
export: `/${O}/export`,
|
|
11
|
-
preview: `/${O}/import/preview`,
|
|
12
|
-
apply: `/${O}/import`
|
|
13
|
-
}, Z = {
|
|
14
|
-
format: "json",
|
|
15
|
-
status: "draft",
|
|
16
|
-
relations: !0,
|
|
17
|
-
media: !0
|
|
18
|
-
}, _ = {
|
|
19
|
-
status: "draft",
|
|
20
|
-
onExisting: "update",
|
|
21
|
-
onMissingRelation: "skip",
|
|
22
|
-
publish: !0,
|
|
23
|
-
continueOnError: !1
|
|
24
|
-
}, z = (n, h, r) => {
|
|
25
|
-
const c = URL.createObjectURL(new Blob([n], { type: r })), u = document.createElement("a");
|
|
26
|
-
u.href = c, u.download = h, document.body.appendChild(u), u.click(), u.remove(), URL.revokeObjectURL(c);
|
|
27
|
-
}, G = (n) => [
|
|
28
|
-
n.created > 0 ? `${n.created} to create` : null,
|
|
29
|
-
n.updated > 0 ? `${n.updated} to update` : null,
|
|
30
|
-
n.skipped > 0 ? `${n.skipped} skipped` : null,
|
|
31
|
-
n.errored > 0 ? `${n.errored} failed` : null
|
|
32
|
-
].filter(Boolean).join(", ") || "nothing to do", H = ({ contentTypes: n }) => {
|
|
33
|
-
const { get: h, post: r } = F(), [c, u] = d.useState(n[0]?.uid ?? ""), [g, f] = d.useState(Z), [o, b] = d.useState(null), [w, S] = d.useState(!1), [T, R] = d.useState([]), [I, E] = d.useState(null), [y, L] = d.useState(null);
|
|
34
|
-
d.useEffect(() => {
|
|
35
|
-
if (!c) return;
|
|
36
|
-
let a = !0;
|
|
37
|
-
return h(j.plan(c)).then(({ data: $ }) => a && b($)).catch(() => a && b(null)), () => {
|
|
38
|
-
a = !1;
|
|
39
|
-
};
|
|
40
|
-
}, [h, c]);
|
|
41
|
-
const l = (a) => f(($) => ({ ...$, ...a })), C = async () => {
|
|
42
|
-
S(!0), E(null), R([]), L(null);
|
|
43
|
-
try {
|
|
44
|
-
const { data: a } = await r(j.export, {
|
|
45
|
-
uid: c,
|
|
46
|
-
...g,
|
|
47
|
-
envelope: !0
|
|
48
|
-
});
|
|
49
|
-
z(a.body, a.filename, a.mime), R(a.warnings ?? []), L(`${a.filename} (${a.count} document${a.count === 1 ? "" : "s"})`);
|
|
50
|
-
} catch (a) {
|
|
51
|
-
E(a?.response?.data?.error?.message ?? "The export failed.");
|
|
52
|
-
} finally {
|
|
53
|
-
S(!1);
|
|
54
|
-
}
|
|
55
|
-
}, x = o ? [
|
|
56
|
-
`${o.scalars.length} field(s)`,
|
|
57
|
-
g.relations && o.relations.length > 0 ? `${o.relations.length} relation(s)` : null,
|
|
58
|
-
o.components.length > 0 ? `${o.components.length} component(s)` : null,
|
|
59
|
-
o.dynamicZones.length > 0 ? `${o.dynamicZones.length} dynamic zone(s)` : null,
|
|
60
|
-
g.media && o.media.length > 0 ? `${o.media.length} media field(s)` : null
|
|
61
|
-
].filter(Boolean) : [];
|
|
62
|
-
return /* @__PURE__ */ t(s, { direction: "column", gap: 4, alignItems: "stretch", children: [
|
|
63
|
-
/* @__PURE__ */ e(i, { variant: "delta", children: "Export" }),
|
|
64
|
-
/* @__PURE__ */ t(p.Root, { name: "export-uid", children: [
|
|
65
|
-
/* @__PURE__ */ e(p.Label, { children: "Content type" }),
|
|
66
|
-
/* @__PURE__ */ e(k, { value: c, onChange: (a) => u(String(a)), children: n.map((a) => /* @__PURE__ */ e(v, { value: a.uid, children: a.label }, a.uid)) })
|
|
67
|
-
] }),
|
|
68
|
-
/* @__PURE__ */ t(s, { gap: 4, wrap: "wrap", alignItems: "flex-end", children: [
|
|
69
|
-
/* @__PURE__ */ e(m, { minWidth: "160px", children: /* @__PURE__ */ t(p.Root, { name: "export-format", children: [
|
|
70
|
-
/* @__PURE__ */ e(p.Label, { children: "Format" }),
|
|
71
|
-
/* @__PURE__ */ t(
|
|
72
|
-
k,
|
|
73
|
-
{
|
|
74
|
-
value: g.format,
|
|
75
|
-
onChange: (a) => l({ format: a }),
|
|
76
|
-
children: [
|
|
77
|
-
/* @__PURE__ */ e(v, { value: "json", children: "JSON - everything" }),
|
|
78
|
-
/* @__PURE__ */ e(v, { value: "csv", children: "CSV - flat fields" })
|
|
79
|
-
]
|
|
80
|
-
}
|
|
81
|
-
)
|
|
82
|
-
] }) }),
|
|
83
|
-
/* @__PURE__ */ e(m, { minWidth: "160px", children: /* @__PURE__ */ t(p.Root, { name: "export-status", children: [
|
|
84
|
-
/* @__PURE__ */ e(p.Label, { children: "Version" }),
|
|
85
|
-
/* @__PURE__ */ t(
|
|
86
|
-
k,
|
|
87
|
-
{
|
|
88
|
-
value: g.status,
|
|
89
|
-
onChange: (a) => l({ status: a }),
|
|
90
|
-
disabled: o ? !o.draftAndPublish : !1,
|
|
91
|
-
children: [
|
|
92
|
-
/* @__PURE__ */ e(v, { value: "draft", children: "Draft" }),
|
|
93
|
-
/* @__PURE__ */ e(v, { value: "published", children: "Published" })
|
|
94
|
-
]
|
|
95
|
-
}
|
|
96
|
-
)
|
|
97
|
-
] }) })
|
|
98
|
-
] }),
|
|
99
|
-
/* @__PURE__ */ t(s, { gap: 4, wrap: "wrap", children: [
|
|
100
|
-
/* @__PURE__ */ e(
|
|
101
|
-
D,
|
|
102
|
-
{
|
|
103
|
-
checked: g.relations,
|
|
104
|
-
onCheckedChange: (a) => l({ relations: !!a }),
|
|
105
|
-
children: "Relations"
|
|
106
|
-
}
|
|
107
|
-
),
|
|
108
|
-
/* @__PURE__ */ e(
|
|
109
|
-
D,
|
|
110
|
-
{
|
|
111
|
-
checked: g.media,
|
|
112
|
-
onCheckedChange: (a) => l({ media: !!a }),
|
|
113
|
-
children: "Media references"
|
|
114
|
-
}
|
|
115
|
-
)
|
|
116
|
-
] }),
|
|
117
|
-
x.length > 0 && /* @__PURE__ */ t(i, { variant: "pi", textColor: "neutral600", children: [
|
|
118
|
-
"Carries ",
|
|
119
|
-
x.join(", "),
|
|
120
|
-
".",
|
|
121
|
-
g.format === "csv" && (o?.components.length || o?.dynamicZones.length) ? " CSV leaves components and dynamic zones behind - use JSON to keep them." : ""
|
|
122
|
-
] }),
|
|
123
|
-
/* @__PURE__ */ e(m, { children: /* @__PURE__ */ e(B, { startIcon: /* @__PURE__ */ e(A, {}), onClick: C, loading: w, disabled: !c, children: "Export" }) }),
|
|
124
|
-
y && /* @__PURE__ */ t(i, { variant: "pi", textColor: "success600", children: [
|
|
125
|
-
"Saved ",
|
|
126
|
-
y,
|
|
127
|
-
"."
|
|
128
|
-
] }),
|
|
129
|
-
T.map((a, $) => /* @__PURE__ */ e(i, { variant: "pi", textColor: "warning700", children: a }, $)),
|
|
130
|
-
I && /* @__PURE__ */ e(i, { variant: "pi", textColor: "danger600", children: I })
|
|
131
|
-
] });
|
|
132
|
-
}, W = ({ label: n, value: h, tone: r }) => /* @__PURE__ */ t(s, { direction: "column", gap: 1, alignItems: "flex-start", children: [
|
|
133
|
-
/* @__PURE__ */ e(i, { variant: "beta", textColor: h > 0 ? r : "neutral500", children: h }),
|
|
134
|
-
/* @__PURE__ */ e(i, { variant: "pi", textColor: "neutral600", children: n })
|
|
135
|
-
] }), K = ({ report: n }) => {
|
|
136
|
-
const h = n.rows.filter((r) => r.outcome === "error");
|
|
137
|
-
return /* @__PURE__ */ t(s, { direction: "column", gap: 4, alignItems: "stretch", children: [
|
|
138
|
-
/* @__PURE__ */ t(
|
|
139
|
-
m,
|
|
140
|
-
{
|
|
141
|
-
padding: 4,
|
|
142
|
-
hasRadius: !0,
|
|
143
|
-
background: "neutral0",
|
|
144
|
-
borderColor: "neutral200",
|
|
145
|
-
borderWidth: "1px",
|
|
146
|
-
borderStyle: "solid",
|
|
147
|
-
children: [
|
|
148
|
-
/* @__PURE__ */ t(s, { justifyContent: "space-between", alignItems: "center", paddingBottom: 3, children: [
|
|
149
|
-
/* @__PURE__ */ e(i, { variant: "delta", children: n.applied ? "Imported" : "Dry run - nothing written yet" }),
|
|
150
|
-
/* @__PURE__ */ e(P, { children: G(n) })
|
|
151
|
-
] }),
|
|
152
|
-
/* @__PURE__ */ t(s, { gap: 8, wrap: "wrap", children: [
|
|
153
|
-
/* @__PURE__ */ e(W, { label: "in the file", value: n.total, tone: "neutral800" }),
|
|
154
|
-
/* @__PURE__ */ e(W, { label: "to create", value: n.created, tone: "success600" }),
|
|
155
|
-
/* @__PURE__ */ e(W, { label: "to update", value: n.updated, tone: "primary600" }),
|
|
156
|
-
/* @__PURE__ */ e(W, { label: "skipped", value: n.skipped, tone: "neutral600" }),
|
|
157
|
-
/* @__PURE__ */ e(W, { label: "failed", value: n.errored, tone: "danger600" })
|
|
158
|
-
] })
|
|
159
|
-
]
|
|
160
|
-
}
|
|
161
|
-
),
|
|
162
|
-
n.warnings.map((r, c) => /* @__PURE__ */ e(
|
|
163
|
-
m,
|
|
164
|
-
{
|
|
165
|
-
padding: 3,
|
|
166
|
-
hasRadius: !0,
|
|
167
|
-
background: "warning100",
|
|
168
|
-
borderColor: "warning200",
|
|
169
|
-
borderWidth: "1px",
|
|
170
|
-
borderStyle: "solid",
|
|
171
|
-
children: /* @__PURE__ */ e(i, { variant: "pi", textColor: "warning700", children: r })
|
|
172
|
-
},
|
|
173
|
-
c
|
|
174
|
-
)),
|
|
175
|
-
n.unresolved.length > 0 && /* @__PURE__ */ t(
|
|
176
|
-
m,
|
|
177
|
-
{
|
|
178
|
-
padding: 4,
|
|
179
|
-
hasRadius: !0,
|
|
180
|
-
background: "neutral0",
|
|
181
|
-
borderColor: "warning200",
|
|
182
|
-
borderWidth: "1px",
|
|
183
|
-
borderStyle: "solid",
|
|
184
|
-
children: [
|
|
185
|
-
/* @__PURE__ */ e(i, { variant: "delta", children: "Relations that point at nothing" }),
|
|
186
|
-
/* @__PURE__ */ e(m, { paddingTop: 1, paddingBottom: 3, children: /* @__PURE__ */ e(i, { variant: "pi", textColor: "neutral600", children: 'These targets are neither in the file nor in this environment. With "drop the link" the rows still import without them; with "refuse the import" nothing is written at all.' }) }),
|
|
187
|
-
/* @__PURE__ */ t(s, { direction: "column", gap: 2, alignItems: "stretch", children: [
|
|
188
|
-
n.unresolved.slice(0, 25).map((r, c) => /* @__PURE__ */ t(s, { gap: 3, alignItems: "baseline", wrap: "wrap", children: [
|
|
189
|
-
/* @__PURE__ */ t(P, { children: [
|
|
190
|
-
"row ",
|
|
191
|
-
r.row
|
|
192
|
-
] }),
|
|
193
|
-
/* @__PURE__ */ e(i, { variant: "omega", fontWeight: "bold", children: r.field }),
|
|
194
|
-
/* @__PURE__ */ e(i, { variant: "pi", textColor: "neutral600", children: r.target }),
|
|
195
|
-
/* @__PURE__ */ e(i, { variant: "pi", textColor: "danger600", children: r.keys.join(", ") })
|
|
196
|
-
] }, c)),
|
|
197
|
-
n.unresolved.length > 25 && /* @__PURE__ */ t(i, { variant: "pi", textColor: "neutral600", children: [
|
|
198
|
-
"and ",
|
|
199
|
-
n.unresolved.length - 25,
|
|
200
|
-
" more"
|
|
201
|
-
] })
|
|
202
|
-
] })
|
|
203
|
-
]
|
|
204
|
-
}
|
|
205
|
-
),
|
|
206
|
-
h.length > 0 && /* @__PURE__ */ t(
|
|
207
|
-
m,
|
|
208
|
-
{
|
|
209
|
-
padding: 4,
|
|
210
|
-
hasRadius: !0,
|
|
211
|
-
background: "neutral0",
|
|
212
|
-
borderColor: "danger200",
|
|
213
|
-
borderWidth: "1px",
|
|
214
|
-
borderStyle: "solid",
|
|
215
|
-
children: [
|
|
216
|
-
/* @__PURE__ */ e(i, { variant: "delta", textColor: "danger600", children: "Rows that failed" }),
|
|
217
|
-
/* @__PURE__ */ e(m, { paddingTop: 2, children: /* @__PURE__ */ e(s, { direction: "column", gap: 2, alignItems: "stretch", children: h.slice(0, 25).map((r) => /* @__PURE__ */ t(s, { gap: 3, alignItems: "baseline", wrap: "wrap", children: [
|
|
218
|
-
/* @__PURE__ */ t(P, { children: [
|
|
219
|
-
"row ",
|
|
220
|
-
r.row
|
|
221
|
-
] }),
|
|
222
|
-
/* @__PURE__ */ e(i, { variant: "pi", textColor: "neutral600", children: r.documentId ?? "no documentId" }),
|
|
223
|
-
/* @__PURE__ */ e(i, { variant: "pi", textColor: "danger600", children: r.message })
|
|
224
|
-
] }, r.row)) }) })
|
|
225
|
-
]
|
|
226
|
-
}
|
|
227
|
-
)
|
|
228
|
-
] });
|
|
229
|
-
}, X = ({ contentTypes: n }) => {
|
|
230
|
-
const { post: h } = F(), [r, c] = d.useState(n[0]?.uid ?? ""), [u, g] = d.useState(_), [f, o] = d.useState(null), [b, w] = d.useState(null), [S, T] = d.useState(!1), [R, I] = d.useState(null), E = d.useRef(null), y = (l) => {
|
|
231
|
-
g((C) => ({ ...C, ...l })), w(null);
|
|
232
|
-
}, L = async (l) => {
|
|
233
|
-
if (!f) return;
|
|
234
|
-
T(!0), I(null);
|
|
235
|
-
const C = new FormData();
|
|
236
|
-
C.append("file", f), C.append("uid", r), Object.entries(u).forEach(([x, a]) => C.append(x, String(a)));
|
|
237
|
-
try {
|
|
238
|
-
const { data: x } = await h(l ? j.apply : j.preview, C);
|
|
239
|
-
w(x);
|
|
240
|
-
} catch (x) {
|
|
241
|
-
I(x?.response?.data?.error?.message ?? "The file could not be read."), w(null);
|
|
242
|
-
} finally {
|
|
243
|
-
T(!1);
|
|
244
|
-
}
|
|
245
|
-
};
|
|
246
|
-
return /* @__PURE__ */ t(s, { direction: "column", gap: 4, alignItems: "stretch", children: [
|
|
247
|
-
/* @__PURE__ */ e(i, { variant: "delta", children: "Import" }),
|
|
248
|
-
/* @__PURE__ */ t(p.Root, { name: "import-uid", children: [
|
|
249
|
-
/* @__PURE__ */ e(p.Label, { children: "Content type" }),
|
|
250
|
-
/* @__PURE__ */ e(
|
|
251
|
-
k,
|
|
252
|
-
{
|
|
253
|
-
value: r,
|
|
254
|
-
onChange: (l) => {
|
|
255
|
-
c(String(l)), w(null);
|
|
256
|
-
},
|
|
257
|
-
children: n.map((l) => /* @__PURE__ */ e(v, { value: l.uid, children: l.label }, l.uid))
|
|
258
|
-
}
|
|
259
|
-
)
|
|
260
|
-
] }),
|
|
261
|
-
/* @__PURE__ */ t(p.Root, { name: "import-file", children: [
|
|
262
|
-
/* @__PURE__ */ e(p.Label, { children: "File" }),
|
|
263
|
-
/* @__PURE__ */ t(s, { gap: 2, alignItems: "center", children: [
|
|
264
|
-
/* @__PURE__ */ e(B, { variant: "secondary", onClick: () => E.current?.click(), children: "Choose a file" }),
|
|
265
|
-
/* @__PURE__ */ e(i, { variant: "pi", textColor: f ? "neutral800" : "neutral500", children: f ? f.name : "JSON or CSV" })
|
|
266
|
-
] }),
|
|
267
|
-
/* @__PURE__ */ e(
|
|
268
|
-
"input",
|
|
269
|
-
{
|
|
270
|
-
ref: E,
|
|
271
|
-
type: "file",
|
|
272
|
-
accept: ".json,.csv,application/json,text/csv",
|
|
273
|
-
style: { display: "none" },
|
|
274
|
-
onChange: (l) => {
|
|
275
|
-
o(l.target.files?.[0] ?? null), w(null);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
)
|
|
279
|
-
] }),
|
|
280
|
-
/* @__PURE__ */ t(s, { gap: 4, wrap: "wrap", alignItems: "flex-end", children: [
|
|
281
|
-
/* @__PURE__ */ e(m, { minWidth: "200px", children: /* @__PURE__ */ t(p.Root, { name: "import-existing", children: [
|
|
282
|
-
/* @__PURE__ */ e(p.Label, { children: "When it is already there" }),
|
|
283
|
-
/* @__PURE__ */ t(
|
|
284
|
-
k,
|
|
285
|
-
{
|
|
286
|
-
value: u.onExisting,
|
|
287
|
-
onChange: (l) => y({ onExisting: l === "skip" ? "skip" : "update" }),
|
|
288
|
-
children: [
|
|
289
|
-
/* @__PURE__ */ e(v, { value: "update", children: "Update it" }),
|
|
290
|
-
/* @__PURE__ */ e(v, { value: "skip", children: "Leave it alone" })
|
|
291
|
-
]
|
|
292
|
-
}
|
|
293
|
-
)
|
|
294
|
-
] }) }),
|
|
295
|
-
/* @__PURE__ */ e(m, { minWidth: "240px", children: /* @__PURE__ */ t(p.Root, { name: "import-missing", children: [
|
|
296
|
-
/* @__PURE__ */ e(p.Label, { children: "When a relation points at nothing" }),
|
|
297
|
-
/* @__PURE__ */ t(
|
|
298
|
-
k,
|
|
299
|
-
{
|
|
300
|
-
value: u.onMissingRelation,
|
|
301
|
-
onChange: (l) => y({ onMissingRelation: l === "fail" ? "fail" : "skip" }),
|
|
302
|
-
children: [
|
|
303
|
-
/* @__PURE__ */ e(v, { value: "skip", children: "Drop the link, keep the row" }),
|
|
304
|
-
/* @__PURE__ */ e(v, { value: "fail", children: "Refuse the import" })
|
|
305
|
-
]
|
|
306
|
-
}
|
|
307
|
-
)
|
|
308
|
-
] }) }),
|
|
309
|
-
/* @__PURE__ */ e(m, { minWidth: "160px", children: /* @__PURE__ */ t(p.Root, { name: "import-status", children: [
|
|
310
|
-
/* @__PURE__ */ e(p.Label, { children: "Import as" }),
|
|
311
|
-
/* @__PURE__ */ t(
|
|
312
|
-
k,
|
|
313
|
-
{
|
|
314
|
-
value: u.status,
|
|
315
|
-
onChange: (l) => y({ status: l }),
|
|
316
|
-
children: [
|
|
317
|
-
/* @__PURE__ */ e(v, { value: "draft", children: "Draft" }),
|
|
318
|
-
/* @__PURE__ */ e(v, { value: "published", children: "Published" })
|
|
319
|
-
]
|
|
320
|
-
}
|
|
321
|
-
)
|
|
322
|
-
] }) })
|
|
323
|
-
] }),
|
|
324
|
-
/* @__PURE__ */ e(
|
|
325
|
-
D,
|
|
326
|
-
{
|
|
327
|
-
checked: u.continueOnError,
|
|
328
|
-
onCheckedChange: (l) => y({ continueOnError: !!l }),
|
|
329
|
-
children: "Keep going past a row that fails"
|
|
330
|
-
}
|
|
331
|
-
),
|
|
332
|
-
/* @__PURE__ */ e(i, { variant: "pi", textColor: "neutral600", children: u.continueOnError ? "Rows that fail are reported and the rest are committed." : "All or nothing: if any row fails, the whole import is rolled back and nothing changes." }),
|
|
333
|
-
/* @__PURE__ */ t(s, { gap: 2, children: [
|
|
334
|
-
/* @__PURE__ */ e(
|
|
335
|
-
B,
|
|
336
|
-
{
|
|
337
|
-
variant: "secondary",
|
|
338
|
-
startIcon: /* @__PURE__ */ e(J, {}),
|
|
339
|
-
onClick: () => L(!1),
|
|
340
|
-
loading: S,
|
|
341
|
-
disabled: !f || !r,
|
|
342
|
-
children: "Dry run"
|
|
343
|
-
}
|
|
344
|
-
),
|
|
345
|
-
/* @__PURE__ */ e(
|
|
346
|
-
B,
|
|
347
|
-
{
|
|
348
|
-
onClick: () => L(!0),
|
|
349
|
-
loading: S,
|
|
350
|
-
disabled: !f || !r || !b || b.applied,
|
|
351
|
-
children: "Import for real"
|
|
352
|
-
}
|
|
353
|
-
)
|
|
354
|
-
] }),
|
|
355
|
-
!b && f && /* @__PURE__ */ e(i, { variant: "pi", textColor: "neutral600", children: "Run the dry run first. Nothing is written until you have seen what it would do." }),
|
|
356
|
-
R && /* @__PURE__ */ e(i, { variant: "pi", textColor: "danger600", children: R }),
|
|
357
|
-
b && /* @__PURE__ */ e(K, { report: b })
|
|
358
|
-
] });
|
|
359
|
-
}, M = ({ children: n }) => /* @__PURE__ */ e(
|
|
360
|
-
m,
|
|
361
|
-
{
|
|
362
|
-
padding: 6,
|
|
363
|
-
hasRadius: !0,
|
|
364
|
-
background: "neutral0",
|
|
365
|
-
borderColor: "neutral200",
|
|
366
|
-
borderWidth: "1px",
|
|
367
|
-
borderStyle: "solid",
|
|
368
|
-
flex: "1",
|
|
369
|
-
minWidth: "380px",
|
|
370
|
-
children: n
|
|
371
|
-
}
|
|
372
|
-
), te = () => {
|
|
373
|
-
const { get: n } = F(), [h, r] = d.useState([]), [c, u] = d.useState(!0), [g, f] = d.useState(null);
|
|
374
|
-
return d.useEffect(() => {
|
|
375
|
-
let o = !0;
|
|
376
|
-
return n(j.contentTypes).then(({ data: b }) => o && r(b.contentTypes)).catch(() => o && f("Could not read the list of content types.")).finally(() => o && u(!1)), () => {
|
|
377
|
-
o = !1;
|
|
378
|
-
};
|
|
379
|
-
}, [n]), /* @__PURE__ */ t(N.Main, { children: [
|
|
380
|
-
/* @__PURE__ */ e(
|
|
381
|
-
U.Header,
|
|
382
|
-
{
|
|
383
|
-
title: "Ferry",
|
|
384
|
-
subtitle: "Move content in and out as JSON or CSV, with relations and components intact"
|
|
385
|
-
}
|
|
386
|
-
),
|
|
387
|
-
/* @__PURE__ */ e(U.Content, { children: c ? /* @__PURE__ */ e(s, { justifyContent: "center", padding: 10, children: /* @__PURE__ */ e(V, { small: !0, children: "Reading the schema" }) }) : g ? /* @__PURE__ */ e(i, { textColor: "danger600", children: g }) : h.length === 0 ? /* @__PURE__ */ t(s, { direction: "column", gap: 2, padding: 10, alignItems: "center", children: [
|
|
388
|
-
/* @__PURE__ */ e(i, { variant: "delta", children: "Nothing to carry yet" }),
|
|
389
|
-
/* @__PURE__ */ e(i, { textColor: "neutral600", children: "Create a content type, and it will show up here." })
|
|
390
|
-
] }) : /* @__PURE__ */ t(s, { gap: 6, alignItems: "flex-start", wrap: "wrap", children: [
|
|
391
|
-
/* @__PURE__ */ e(M, { children: /* @__PURE__ */ e(H, { contentTypes: h }) }),
|
|
392
|
-
/* @__PURE__ */ e(M, { children: /* @__PURE__ */ e(X, { contentTypes: h }) })
|
|
393
|
-
] }) })
|
|
394
|
-
] });
|
|
395
|
-
};
|
|
396
|
-
export {
|
|
397
|
-
te as TransferPage
|
|
398
|
-
};
|