@commercetools-demo/puck-content-manager 0.5.2 → 0.6.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/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +747 -468
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +807 -460
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -19
package/dist/index.mjs
CHANGED
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
// src/ContentManager.tsx
|
|
2
2
|
import { useState } from "react";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
3
|
+
import {
|
|
4
|
+
PuckApiProvider,
|
|
5
|
+
usePuckContents,
|
|
6
|
+
usePuckTemplates
|
|
7
|
+
} from "@commercetools-demo/puck-api";
|
|
8
|
+
import {
|
|
9
|
+
Badge,
|
|
10
|
+
Button,
|
|
11
|
+
Card,
|
|
12
|
+
DataTable,
|
|
13
|
+
Dialog,
|
|
14
|
+
FormField,
|
|
15
|
+
Icon,
|
|
16
|
+
IconButton,
|
|
17
|
+
LoadingSpinner,
|
|
18
|
+
Select,
|
|
19
|
+
Stack,
|
|
20
|
+
Text,
|
|
21
|
+
TextInput
|
|
22
|
+
} from "@commercetools/nimbus";
|
|
23
|
+
import { Add, Close, Delete, Edit } from "@commercetools/nimbus-icons";
|
|
16
24
|
|
|
17
25
|
// src/EnsureIntlProvider.tsx
|
|
18
26
|
import { useContext } from "react";
|
|
@@ -38,25 +46,44 @@ var EnsureIntlProvider = ({ children }) => {
|
|
|
38
46
|
);
|
|
39
47
|
};
|
|
40
48
|
|
|
49
|
+
// src/EnsureNimbusProvider.tsx
|
|
50
|
+
import { ChakraProvider, createSystem } from "@chakra-ui/react/styled-system";
|
|
51
|
+
import {
|
|
52
|
+
NimbusI18nProvider,
|
|
53
|
+
_RegionProvider,
|
|
54
|
+
system as nimbusSystem
|
|
55
|
+
} from "@commercetools/nimbus";
|
|
56
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
57
|
+
var SCOPED_SYSTEM_KEY = /* @__PURE__ */ Symbol.for(
|
|
58
|
+
"@commercetools-demo/puck:nimbus-scoped-system"
|
|
59
|
+
);
|
|
60
|
+
var globalScope = globalThis;
|
|
61
|
+
var scopedSystem = globalScope[SCOPED_SYSTEM_KEY] ?? (globalScope[SCOPED_SYSTEM_KEY] = createSystem({
|
|
62
|
+
// `_config` is the fully-merged Nimbus + Chakra config used to build the
|
|
63
|
+
// stock system; reusing it keeps all Nimbus theming intact.
|
|
64
|
+
...nimbusSystem._config,
|
|
65
|
+
preflight: false,
|
|
66
|
+
globalCss: {}
|
|
67
|
+
}));
|
|
68
|
+
var EnsureNimbusProvider = ({
|
|
69
|
+
locale = "en",
|
|
70
|
+
children
|
|
71
|
+
}) => /* @__PURE__ */ jsx2(ChakraProvider, { value: scopedSystem, children: /* @__PURE__ */ jsx2(NimbusI18nProvider, { locale, children: /* @__PURE__ */ jsx2(_RegionProvider, { children }) }) });
|
|
72
|
+
|
|
41
73
|
// src/ContentManager.tsx
|
|
42
|
-
import { jsx as
|
|
43
|
-
var columns = [
|
|
44
|
-
{ key: "name", label: "Name" },
|
|
45
|
-
{ key: "contentType", label: "Content Type" },
|
|
46
|
-
{ key: "status", label: "Status" },
|
|
47
|
-
{ key: "updatedAt", label: "Updated" },
|
|
48
|
-
{ key: "actions", label: "Actions", shouldIgnoreRowClick: true }
|
|
49
|
-
];
|
|
74
|
+
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
50
75
|
var ContentList = ({ defaultContentType, onEdit }) => {
|
|
51
|
-
const { contents, loading, error,
|
|
52
|
-
const
|
|
76
|
+
const { contents, loading, error, createContent, deleteContent } = usePuckContents(defaultContentType);
|
|
77
|
+
const { templates } = usePuckTemplates("content");
|
|
78
|
+
const [search, setSearch] = useState("");
|
|
53
79
|
const [showCreate, setShowCreate] = useState(false);
|
|
54
80
|
const [createName, setCreateName] = useState("");
|
|
55
81
|
const [createType, setCreateType] = useState(defaultContentType ?? "");
|
|
82
|
+
const [templateKey, setTemplateKey] = useState("");
|
|
56
83
|
const [createError, setCreateError] = useState(null);
|
|
57
84
|
const [creating, setCreating] = useState(false);
|
|
58
85
|
const [deleting, setDeleting] = useState(null);
|
|
59
|
-
const
|
|
86
|
+
const [pendingDelete, setPendingDelete] = useState(null);
|
|
60
87
|
const handleCreate = async () => {
|
|
61
88
|
setCreateError(null);
|
|
62
89
|
if (!createName.trim()) {
|
|
@@ -69,168 +96,231 @@ var ContentList = ({ defaultContentType, onEdit }) => {
|
|
|
69
96
|
}
|
|
70
97
|
setCreating(true);
|
|
71
98
|
try {
|
|
99
|
+
const template = templateKey ? templates.find((t) => t.key === templateKey) : void 0;
|
|
72
100
|
const input = {
|
|
73
101
|
name: createName.trim(),
|
|
74
102
|
contentType: createType.trim(),
|
|
75
|
-
data: { content: [], root: { props: {} } }
|
|
103
|
+
data: template?.value.puckData ?? { content: [], root: { props: {} } }
|
|
76
104
|
};
|
|
77
105
|
await createContent(input);
|
|
78
106
|
setShowCreate(false);
|
|
79
107
|
setCreateName("");
|
|
80
108
|
setCreateType(defaultContentType ?? "");
|
|
109
|
+
setTemplateKey("");
|
|
81
110
|
} catch (err) {
|
|
82
111
|
setCreateError(err.message);
|
|
83
112
|
} finally {
|
|
84
113
|
setCreating(false);
|
|
85
114
|
}
|
|
86
115
|
};
|
|
87
|
-
const handleDelete = async (
|
|
88
|
-
|
|
89
|
-
setDeleting(key);
|
|
116
|
+
const handleDelete = async (item) => {
|
|
117
|
+
setDeleting(item.key);
|
|
90
118
|
try {
|
|
91
|
-
await deleteContent(key);
|
|
119
|
+
await deleteContent(item.key);
|
|
120
|
+
setPendingDelete(null);
|
|
92
121
|
} finally {
|
|
93
122
|
setDeleting(null);
|
|
94
123
|
}
|
|
95
124
|
};
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
125
|
+
const term = search.trim().toLowerCase();
|
|
126
|
+
const filteredContents = term ? contents.filter(
|
|
127
|
+
(c) => c.value.name.toLowerCase().includes(term) || c.value.contentType.toLowerCase().includes(term)
|
|
128
|
+
) : contents;
|
|
129
|
+
const rows = filteredContents.map((c) => ({ ...c, id: c.key }));
|
|
130
|
+
const columns = [
|
|
131
|
+
{
|
|
132
|
+
id: "name",
|
|
133
|
+
header: "Name",
|
|
134
|
+
accessor: (row) => row.value.name,
|
|
135
|
+
render: ({ row }) => /* @__PURE__ */ jsx3(Text, { fontWeight: "bold", children: row.value.name })
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: "contentType",
|
|
139
|
+
header: "Content Type",
|
|
140
|
+
accessor: (row) => row.value.contentType,
|
|
141
|
+
render: ({ row }) => /* @__PURE__ */ jsx3(
|
|
142
|
+
"code",
|
|
102
143
|
{
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
144
|
+
style: {
|
|
145
|
+
background: "#f4f4f4",
|
|
146
|
+
padding: "2px 6px",
|
|
147
|
+
borderRadius: "4px",
|
|
148
|
+
fontSize: "11px",
|
|
149
|
+
fontFamily: "monospace"
|
|
150
|
+
},
|
|
151
|
+
children: row.value.contentType
|
|
106
152
|
}
|
|
107
153
|
)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
/* @__PURE__ */ jsxs(Spacings.Inline, { scale: "s", children: [
|
|
139
|
-
/* @__PURE__ */ jsx2(
|
|
140
|
-
PrimaryButton,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
id: "status",
|
|
157
|
+
header: "Status",
|
|
158
|
+
accessor: () => "",
|
|
159
|
+
isSortable: false,
|
|
160
|
+
render: ({ row }) => {
|
|
161
|
+
const hasDraft = !!row.states.draft;
|
|
162
|
+
const hasPublished = !!row.states.published;
|
|
163
|
+
return /* @__PURE__ */ jsxs(Stack, { direction: "row", gap: "100", wrap: "wrap", children: [
|
|
164
|
+
hasDraft && /* @__PURE__ */ jsx3(Badge, { colorPalette: "warning", size: "xs", children: "Draft" }),
|
|
165
|
+
hasPublished && /* @__PURE__ */ jsx3(Badge, { colorPalette: "positive", size: "xs", children: "Published" }),
|
|
166
|
+
!hasDraft && !hasPublished && /* @__PURE__ */ jsx3(Badge, { colorPalette: "neutral", size: "xs", children: "No state" })
|
|
167
|
+
] });
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
id: "updatedAt",
|
|
172
|
+
header: "Updated",
|
|
173
|
+
accessor: (row) => row.value.updatedAt,
|
|
174
|
+
render: ({ row }) => /* @__PURE__ */ jsx3(Text, { color: "neutral.11", children: new Date(row.value.updatedAt).toLocaleDateString() })
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
id: "actions",
|
|
178
|
+
header: "Actions",
|
|
179
|
+
accessor: () => "",
|
|
180
|
+
isSortable: false,
|
|
181
|
+
render: ({ row }) => /* @__PURE__ */ jsxs(Stack, { direction: "row", gap: "100", alignItems: "center", children: [
|
|
182
|
+
/* @__PURE__ */ jsx3(
|
|
183
|
+
IconButton,
|
|
141
184
|
{
|
|
142
|
-
label:
|
|
143
|
-
|
|
144
|
-
|
|
185
|
+
"aria-label": `Edit ${row.value.name}`,
|
|
186
|
+
variant: "ghost",
|
|
187
|
+
size: "xs",
|
|
188
|
+
onPress: () => onEdit(row),
|
|
189
|
+
children: /* @__PURE__ */ jsx3(Edit, {})
|
|
145
190
|
}
|
|
146
191
|
),
|
|
147
|
-
/* @__PURE__ */
|
|
192
|
+
/* @__PURE__ */ jsx3(
|
|
193
|
+
IconButton,
|
|
194
|
+
{
|
|
195
|
+
"aria-label": `Delete ${row.value.name}`,
|
|
196
|
+
variant: "ghost",
|
|
197
|
+
colorPalette: "critical",
|
|
198
|
+
size: "xs",
|
|
199
|
+
isDisabled: deleting === row.key,
|
|
200
|
+
onPress: () => setPendingDelete(row),
|
|
201
|
+
children: /* @__PURE__ */ jsx3(Delete, {})
|
|
202
|
+
}
|
|
203
|
+
)
|
|
148
204
|
] })
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
205
|
+
}
|
|
206
|
+
];
|
|
207
|
+
return /* @__PURE__ */ jsxs("div", { style: { maxWidth: "1200px", margin: "0 auto", padding: "32px 24px" }, children: [
|
|
208
|
+
/* @__PURE__ */ jsxs(Stack, { direction: "column", gap: "600", children: [
|
|
209
|
+
/* @__PURE__ */ jsxs(Stack, { direction: "row", justifyContent: "space-between", alignItems: "center", children: [
|
|
210
|
+
/* @__PURE__ */ jsx3(Text, { as: "h1", fontSize: "2xl", fontWeight: "700", children: "Content Items" }),
|
|
211
|
+
/* @__PURE__ */ jsxs(Button, { variant: "solid", onPress: () => setShowCreate((v) => !v), children: [
|
|
212
|
+
/* @__PURE__ */ jsx3(Icon, { as: Add }),
|
|
213
|
+
" New Content"
|
|
214
|
+
] })
|
|
215
|
+
] }),
|
|
216
|
+
showCreate && /* @__PURE__ */ jsx3(Card.Root, { variant: "outlined", children: /* @__PURE__ */ jsx3(Card.Body, { children: /* @__PURE__ */ jsxs(Stack, { direction: "column", gap: "400", children: [
|
|
217
|
+
/* @__PURE__ */ jsx3(Text, { as: "h4", fontSize: "xl", fontWeight: "700", children: "Create Content Item" }),
|
|
218
|
+
createError && /* @__PURE__ */ jsx3(Text, { color: "critical.11", children: createError }),
|
|
219
|
+
/* @__PURE__ */ jsxs(Stack, { direction: "row", gap: "400", children: [
|
|
220
|
+
/* @__PURE__ */ jsx3("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsxs(FormField.Root, { children: [
|
|
221
|
+
/* @__PURE__ */ jsx3(FormField.Label, { children: "Name" }),
|
|
222
|
+
/* @__PURE__ */ jsx3(FormField.Input, { children: /* @__PURE__ */ jsx3(
|
|
223
|
+
TextInput,
|
|
224
|
+
{
|
|
225
|
+
value: createName,
|
|
226
|
+
onChange: (v) => setCreateName(v),
|
|
227
|
+
placeholder: "e.g. Homepage Hero"
|
|
228
|
+
}
|
|
229
|
+
) })
|
|
230
|
+
] }) }),
|
|
231
|
+
/* @__PURE__ */ jsx3("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsxs(FormField.Root, { children: [
|
|
232
|
+
/* @__PURE__ */ jsx3(FormField.Label, { children: "Content Type" }),
|
|
233
|
+
/* @__PURE__ */ jsx3(FormField.Input, { children: /* @__PURE__ */ jsx3(
|
|
234
|
+
TextInput,
|
|
235
|
+
{
|
|
236
|
+
value: createType,
|
|
237
|
+
onChange: (v) => setCreateType(v),
|
|
238
|
+
placeholder: "e.g. hero, banner"
|
|
239
|
+
}
|
|
240
|
+
) })
|
|
241
|
+
] }) })
|
|
242
|
+
] }),
|
|
243
|
+
/* @__PURE__ */ jsxs(FormField.Root, { children: [
|
|
244
|
+
/* @__PURE__ */ jsx3(FormField.Label, { children: "Template" }),
|
|
245
|
+
/* @__PURE__ */ jsx3(FormField.Input, { children: /* @__PURE__ */ jsx3(
|
|
246
|
+
Select.Root,
|
|
247
|
+
{
|
|
248
|
+
"aria-label": "Template",
|
|
249
|
+
selectedKey: templateKey || "empty",
|
|
250
|
+
onSelectionChange: (key) => setTemplateKey(key == null || key === "empty" ? "" : String(key)),
|
|
251
|
+
children: /* @__PURE__ */ jsxs(Select.Options, { children: [
|
|
252
|
+
/* @__PURE__ */ jsx3(Select.Option, { id: "empty", children: "Empty" }),
|
|
253
|
+
templates.map((t) => /* @__PURE__ */ jsx3(Select.Option, { id: t.key, children: t.value.name }, t.key))
|
|
254
|
+
] })
|
|
255
|
+
}
|
|
256
|
+
) })
|
|
257
|
+
] }),
|
|
258
|
+
/* @__PURE__ */ jsxs(Stack, { direction: "row", gap: "200", children: [
|
|
259
|
+
/* @__PURE__ */ jsx3(Button, { variant: "solid", onPress: () => void handleCreate(), isDisabled: creating, children: creating ? "Creating\u2026" : "Create" }),
|
|
260
|
+
/* @__PURE__ */ jsx3(Button, { variant: "outline", onPress: () => setShowCreate(false), children: "Cancel" })
|
|
261
|
+
] })
|
|
262
|
+
] }) }) }),
|
|
263
|
+
/* @__PURE__ */ jsx3("div", { style: { maxWidth: 360 }, children: /* @__PURE__ */ jsx3(
|
|
152
264
|
TextInput,
|
|
153
265
|
{
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
266
|
+
"aria-label": "Search content",
|
|
267
|
+
placeholder: "Search by name or content type\u2026",
|
|
268
|
+
value: search,
|
|
269
|
+
onChange: (v) => setSearch(v),
|
|
270
|
+
width: "100%",
|
|
271
|
+
trailingElement: search !== "" ? /* @__PURE__ */ jsx3(
|
|
272
|
+
IconButton,
|
|
273
|
+
{
|
|
274
|
+
"aria-label": "Clear search",
|
|
275
|
+
variant: "ghost",
|
|
276
|
+
colorPalette: "neutral",
|
|
277
|
+
size: "2xs",
|
|
278
|
+
onPress: () => setSearch(""),
|
|
279
|
+
children: /* @__PURE__ */ jsx3(Close, {})
|
|
280
|
+
}
|
|
281
|
+
) : void 0
|
|
157
282
|
}
|
|
158
283
|
) }),
|
|
159
|
-
/* @__PURE__ */
|
|
160
|
-
|
|
161
|
-
{
|
|
162
|
-
label: "Filter",
|
|
163
|
-
iconLeft: /* @__PURE__ */ jsx2(SearchIcon, {}),
|
|
164
|
-
onClick: handleFilter
|
|
165
|
-
}
|
|
166
|
-
),
|
|
167
|
-
/* @__PURE__ */ jsx2(
|
|
168
|
-
FlatButton,
|
|
169
|
-
{
|
|
170
|
-
label: "Clear",
|
|
171
|
-
onClick: () => {
|
|
172
|
-
setFilterType("");
|
|
173
|
-
void fetchContents(void 0);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
),
|
|
177
|
-
/* @__PURE__ */ jsx2(FlatButton, { label: "Refresh", onClick: () => void refresh() })
|
|
284
|
+
error && /* @__PURE__ */ jsx3(Text, { color: "critical.11", children: error }),
|
|
285
|
+
loading ? /* @__PURE__ */ jsx3("div", { style: { display: "flex", justifyContent: "center", padding: "48px" }, children: /* @__PURE__ */ jsx3(LoadingSpinner, {}) }) : contents.length === 0 ? /* @__PURE__ */ jsx3(Stack, { direction: "column", gap: "400", alignItems: "center", children: /* @__PURE__ */ jsx3(Text, { color: "neutral.11", children: "No content items found." }) }) : /* @__PURE__ */ jsx3(DataTable, { columns, rows, "aria-label": "Content items" })
|
|
178
286
|
] }),
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
DataTable,
|
|
287
|
+
/* @__PURE__ */ jsx3(
|
|
288
|
+
Dialog.Root,
|
|
182
289
|
{
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
case "actions":
|
|
215
|
-
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
|
|
216
|
-
/* @__PURE__ */ jsx2(PrimaryButton, { label: "Edit", size: "20", onClick: () => onEdit(row) }),
|
|
217
|
-
/* @__PURE__ */ jsx2(
|
|
218
|
-
FlatButton,
|
|
219
|
-
{
|
|
220
|
-
tone: "critical",
|
|
221
|
-
label: deleting === row.key ? "\u2026" : "Delete",
|
|
222
|
-
isDisabled: deleting === row.key,
|
|
223
|
-
onClick: () => void handleDelete(row.key)
|
|
224
|
-
}
|
|
225
|
-
)
|
|
226
|
-
] });
|
|
227
|
-
default:
|
|
228
|
-
return null;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
290
|
+
isOpen: pendingDelete !== null,
|
|
291
|
+
onOpenChange: (open) => {
|
|
292
|
+
if (!open) setPendingDelete(null);
|
|
293
|
+
},
|
|
294
|
+
children: /* @__PURE__ */ jsxs(Dialog.Content, { children: [
|
|
295
|
+
/* @__PURE__ */ jsxs(Dialog.Header, { children: [
|
|
296
|
+
/* @__PURE__ */ jsx3(Dialog.Title, { children: "Delete content item?" }),
|
|
297
|
+
/* @__PURE__ */ jsx3(Dialog.CloseTrigger, {})
|
|
298
|
+
] }),
|
|
299
|
+
/* @__PURE__ */ jsx3(Dialog.Body, { children: /* @__PURE__ */ jsxs(Text, { children: [
|
|
300
|
+
"Are you sure you want to delete",
|
|
301
|
+
" ",
|
|
302
|
+
/* @__PURE__ */ jsx3(Text, { as: "span", fontWeight: "700", children: pendingDelete?.value.name }),
|
|
303
|
+
" ",
|
|
304
|
+
"and all its versions? This cannot be undone."
|
|
305
|
+
] }) }),
|
|
306
|
+
/* @__PURE__ */ jsxs(Dialog.Footer, { children: [
|
|
307
|
+
/* @__PURE__ */ jsx3(Button, { slot: "close", variant: "outline", isDisabled: deleting !== null, children: "Cancel" }),
|
|
308
|
+
/* @__PURE__ */ jsx3(
|
|
309
|
+
Button,
|
|
310
|
+
{
|
|
311
|
+
colorPalette: "critical",
|
|
312
|
+
isDisabled: deleting !== null,
|
|
313
|
+
onPress: () => {
|
|
314
|
+
if (pendingDelete) void handleDelete(pendingDelete);
|
|
315
|
+
},
|
|
316
|
+
children: deleting !== null ? "Deleting\u2026" : "Delete"
|
|
317
|
+
}
|
|
318
|
+
)
|
|
319
|
+
] })
|
|
320
|
+
] })
|
|
231
321
|
}
|
|
232
322
|
)
|
|
233
|
-
] })
|
|
323
|
+
] });
|
|
234
324
|
};
|
|
235
325
|
var ContentManagerList = ({
|
|
236
326
|
baseURL,
|
|
@@ -239,27 +329,35 @@ var ContentManagerList = ({
|
|
|
239
329
|
jwtToken,
|
|
240
330
|
defaultContentType,
|
|
241
331
|
onEdit
|
|
242
|
-
}) => /* @__PURE__ */
|
|
332
|
+
}) => /* @__PURE__ */ jsx3(EnsureNimbusProvider, { children: /* @__PURE__ */ jsx3(EnsureIntlProvider, { children: /* @__PURE__ */ jsx3(
|
|
243
333
|
PuckApiProvider,
|
|
244
334
|
{
|
|
245
335
|
baseURL,
|
|
246
336
|
projectKey,
|
|
247
337
|
businessUnitKey,
|
|
248
338
|
jwtToken,
|
|
249
|
-
children: /* @__PURE__ */
|
|
339
|
+
children: /* @__PURE__ */ jsx3(ContentList, { defaultContentType, onEdit })
|
|
250
340
|
}
|
|
251
|
-
) });
|
|
341
|
+
) }) });
|
|
252
342
|
|
|
253
343
|
// src/ContentEditor.tsx
|
|
254
344
|
import { useCallback, useMemo, useRef, useState as useState2 } from "react";
|
|
255
345
|
import { Puck } from "@measured/puck";
|
|
256
346
|
import "@measured/puck/puck.css";
|
|
257
|
-
import {
|
|
347
|
+
import {
|
|
348
|
+
PuckApiProvider as PuckApiProvider2,
|
|
349
|
+
usePuckContent,
|
|
350
|
+
usePuckTemplates as usePuckTemplates2
|
|
351
|
+
} from "@commercetools-demo/puck-api";
|
|
258
352
|
import {
|
|
259
353
|
ComponentSearchProvider,
|
|
260
354
|
ComponentsPanel,
|
|
261
355
|
ComponentItemFilter,
|
|
262
|
-
|
|
356
|
+
CreateTemplateDialog,
|
|
357
|
+
EditorToolbar,
|
|
358
|
+
nimbusFieldTypes,
|
|
359
|
+
stripPuckDataToTemplate,
|
|
360
|
+
useDirtyState
|
|
263
361
|
} from "@commercetools-demo/puck-editor";
|
|
264
362
|
import {
|
|
265
363
|
VersionHistoryProvider,
|
|
@@ -269,10 +367,8 @@ import {
|
|
|
269
367
|
useVersionHistoryPanel,
|
|
270
368
|
useVersionDiff
|
|
271
369
|
} from "@commercetools-demo/puck-version-history";
|
|
272
|
-
import LoadingSpinner2 from "@commercetools
|
|
273
|
-
import
|
|
274
|
-
import Spacings2 from "@commercetools-uikit/spacings";
|
|
275
|
-
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
370
|
+
import { LoadingSpinner as LoadingSpinner2, Stack as Stack2, Text as Text2 } from "@commercetools/nimbus";
|
|
371
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
276
372
|
var ContentEditorInner = ({
|
|
277
373
|
contentKey,
|
|
278
374
|
config,
|
|
@@ -292,9 +388,12 @@ var ContentEditorInner = ({
|
|
|
292
388
|
revertToPublished,
|
|
293
389
|
loadVersions
|
|
294
390
|
} = usePuckContent(contentKey);
|
|
391
|
+
const { createTemplate } = usePuckTemplates2("content");
|
|
295
392
|
const latestDataRef = useRef(null);
|
|
296
|
-
const [hasUnsavedChanges, setHasUnsavedChanges] = useState2(false);
|
|
297
393
|
const [isApplyingVersion, setIsApplyingVersion] = useState2(false);
|
|
394
|
+
const [reloadNonce, setReloadNonce] = useState2(0);
|
|
395
|
+
const [templateDialogOpen, setTemplateDialogOpen] = useState2(false);
|
|
396
|
+
const [templateSaving, setTemplateSaving] = useState2(false);
|
|
298
397
|
const currentData = states.draft?.data ?? content?.data ?? {
|
|
299
398
|
content: [],
|
|
300
399
|
root: { props: {} }
|
|
@@ -310,39 +409,44 @@ var ContentEditorInner = ({
|
|
|
310
409
|
);
|
|
311
410
|
const isPreviewingRef = useRef(false);
|
|
312
411
|
isPreviewingRef.current = versionHistory.isPreviewingHistory;
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
412
|
+
const canvasKey = `${contentKey}:${versionHistory.selectedVersionId ?? "current"}:${reloadNonce}`;
|
|
413
|
+
const { isDirty: hasUnsavedChanges, markChange, markSaved } = useDirtyState(canvasKey);
|
|
414
|
+
const handleChange = useCallback(
|
|
415
|
+
(data) => {
|
|
416
|
+
if (isPreviewingRef.current) return;
|
|
417
|
+
latestDataRef.current = data;
|
|
418
|
+
markChange(data);
|
|
419
|
+
},
|
|
420
|
+
[markChange]
|
|
421
|
+
);
|
|
318
422
|
const handleSave = useCallback(async () => {
|
|
319
423
|
const data = latestDataRef.current;
|
|
320
424
|
if (!data) return;
|
|
321
425
|
try {
|
|
322
426
|
await saveDraft(data);
|
|
323
|
-
|
|
427
|
+
markSaved(data);
|
|
324
428
|
onSave?.(data);
|
|
325
429
|
} catch (err) {
|
|
326
430
|
onError?.(err);
|
|
327
431
|
}
|
|
328
|
-
}, [saveDraft, onSave, onError]);
|
|
432
|
+
}, [saveDraft, onSave, onError, markSaved]);
|
|
329
433
|
const handlePublish = useCallback(
|
|
330
434
|
async (data) => {
|
|
331
435
|
try {
|
|
332
436
|
await saveDraft(data);
|
|
333
|
-
|
|
437
|
+
markSaved(data);
|
|
334
438
|
await publish(false);
|
|
335
439
|
onPublish?.(data);
|
|
336
440
|
} catch (err) {
|
|
337
441
|
onError?.(err);
|
|
338
442
|
}
|
|
339
443
|
},
|
|
340
|
-
[saveDraft, publish, onPublish, onError]
|
|
444
|
+
[saveDraft, publish, onPublish, onError, markSaved]
|
|
341
445
|
);
|
|
342
446
|
const handleRevert = useCallback(async () => {
|
|
343
447
|
try {
|
|
344
448
|
await revertToPublished();
|
|
345
|
-
|
|
449
|
+
setReloadNonce((n) => n + 1);
|
|
346
450
|
} catch (err) {
|
|
347
451
|
onError?.(err);
|
|
348
452
|
}
|
|
@@ -353,7 +457,7 @@ var ContentEditorInner = ({
|
|
|
353
457
|
setIsApplyingVersion(true);
|
|
354
458
|
try {
|
|
355
459
|
await saveDraft(versionData);
|
|
356
|
-
|
|
460
|
+
markSaved(versionData);
|
|
357
461
|
onSave?.(versionData);
|
|
358
462
|
versionHistory.clearSelection();
|
|
359
463
|
} catch (err) {
|
|
@@ -361,7 +465,7 @@ var ContentEditorInner = ({
|
|
|
361
465
|
} finally {
|
|
362
466
|
setIsApplyingVersion(false);
|
|
363
467
|
}
|
|
364
|
-
}, [versionHistory, saveDraft, onSave, onError]);
|
|
468
|
+
}, [versionHistory, saveDraft, onSave, onError, markSaved]);
|
|
365
469
|
const contentConfig = useMemo(() => {
|
|
366
470
|
const otherRootFields = Object.fromEntries(
|
|
367
471
|
Object.entries(config.root?.fields ?? {}).filter(([k]) => k !== "title")
|
|
@@ -383,22 +487,38 @@ var ContentEditorInner = ({
|
|
|
383
487
|
}
|
|
384
488
|
};
|
|
385
489
|
}, [config]);
|
|
490
|
+
const handleCreateTemplate = useCallback(
|
|
491
|
+
async (name, withoutData) => {
|
|
492
|
+
const source = latestDataRef.current ?? currentData;
|
|
493
|
+
const puckData = withoutData ? stripPuckDataToTemplate(source, contentConfig) : source;
|
|
494
|
+
setTemplateSaving(true);
|
|
495
|
+
try {
|
|
496
|
+
await createTemplate({ name, kind: "content", puckData });
|
|
497
|
+
setTemplateDialogOpen(false);
|
|
498
|
+
} catch (err) {
|
|
499
|
+
onError?.(err);
|
|
500
|
+
} finally {
|
|
501
|
+
setTemplateSaving(false);
|
|
502
|
+
}
|
|
503
|
+
},
|
|
504
|
+
[createTemplate, currentData, contentConfig, onError]
|
|
505
|
+
);
|
|
386
506
|
if (loading) {
|
|
387
|
-
return /* @__PURE__ */
|
|
388
|
-
/* @__PURE__ */
|
|
389
|
-
/* @__PURE__ */
|
|
507
|
+
return /* @__PURE__ */ jsx4("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", height: "100vh" }, children: /* @__PURE__ */ jsxs2(Stack2, { direction: "column", gap: "400", alignItems: "center", children: [
|
|
508
|
+
/* @__PURE__ */ jsx4(LoadingSpinner2, {}),
|
|
509
|
+
/* @__PURE__ */ jsx4(Text2, { color: "neutral.11", children: "Loading editor\u2026" })
|
|
390
510
|
] }) });
|
|
391
511
|
}
|
|
392
512
|
if (error) {
|
|
393
|
-
return /* @__PURE__ */
|
|
394
|
-
/* @__PURE__ */
|
|
513
|
+
return /* @__PURE__ */ jsx4("div", { style: { padding: "32px" }, children: /* @__PURE__ */ jsxs2(Text2, { color: "critical.11", children: [
|
|
514
|
+
/* @__PURE__ */ jsx4("strong", { children: "Error loading content:" }),
|
|
395
515
|
" ",
|
|
396
516
|
error
|
|
397
517
|
] }) });
|
|
398
518
|
}
|
|
399
519
|
const activeData = versionHistory.previewData ?? currentData;
|
|
400
520
|
const toolbarStates = states;
|
|
401
|
-
return /* @__PURE__ */
|
|
521
|
+
return /* @__PURE__ */ jsx4(
|
|
402
522
|
VersionHistoryProvider,
|
|
403
523
|
{
|
|
404
524
|
diff,
|
|
@@ -411,44 +531,70 @@ var ContentEditorInner = ({
|
|
|
411
531
|
onApply: () => void handleApplyVersion(),
|
|
412
532
|
onDiscard: versionHistory.clearSelection,
|
|
413
533
|
onLoadVersions: versionHistory.openPanel,
|
|
414
|
-
children: /* @__PURE__ */
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
534
|
+
children: /* @__PURE__ */ jsxs2(ComponentSearchProvider, { children: [
|
|
535
|
+
/* @__PURE__ */ jsx4(
|
|
536
|
+
Puck,
|
|
537
|
+
{
|
|
538
|
+
config: contentConfig,
|
|
539
|
+
data: activeData,
|
|
540
|
+
onChange: handleChange,
|
|
541
|
+
onPublish: handlePublish,
|
|
542
|
+
overrides: {
|
|
543
|
+
fieldTypes: nimbusFieldTypes,
|
|
544
|
+
header: () => versionHistory.isPreviewingHistory ? /* @__PURE__ */ jsxs2(
|
|
545
|
+
Stack2,
|
|
546
|
+
{
|
|
547
|
+
gridArea: "header",
|
|
548
|
+
direction: "row",
|
|
549
|
+
gap: "200",
|
|
550
|
+
alignItems: "center",
|
|
551
|
+
justifyContent: "flex-end",
|
|
552
|
+
padding: "200",
|
|
553
|
+
children: [
|
|
554
|
+
/* @__PURE__ */ jsx4(
|
|
555
|
+
VersionPreviewBanner,
|
|
556
|
+
{
|
|
557
|
+
timestamp: versionHistory.selectedVersion.timestamp,
|
|
558
|
+
onApply: () => void handleApplyVersion(),
|
|
559
|
+
onDiscard: versionHistory.clearSelection,
|
|
560
|
+
isApplying: isApplyingVersion
|
|
561
|
+
}
|
|
562
|
+
),
|
|
563
|
+
/* @__PURE__ */ jsx4(VersionHistoryButton, { disabled: isApplyingVersion })
|
|
564
|
+
]
|
|
565
|
+
}
|
|
566
|
+
) : /* @__PURE__ */ jsx4(
|
|
567
|
+
EditorToolbar,
|
|
425
568
|
{
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
569
|
+
title: content?.name ?? "Content",
|
|
570
|
+
saving,
|
|
571
|
+
isDirty: hasUnsavedChanges,
|
|
572
|
+
states: toolbarStates,
|
|
573
|
+
onSave: () => void handleSave(),
|
|
574
|
+
onPublish: () => void handlePublish(activeData),
|
|
575
|
+
onRevert: () => void handleRevert(),
|
|
576
|
+
onCreateTemplate: () => setTemplateDialogOpen(true),
|
|
577
|
+
createTemplateLabel: "Create a template from this content",
|
|
578
|
+
showPublishButton: true
|
|
430
579
|
}
|
|
431
580
|
),
|
|
432
|
-
/* @__PURE__ */
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
componentItem: ({ children, name }) => /* @__PURE__ */ jsx3(ComponentItemFilter, { name, children }),
|
|
447
|
-
fields: ({ children, isLoading }) => /* @__PURE__ */ jsx3(VersionAwareFieldsPanel, { isLoading, children })
|
|
581
|
+
components: ({ children }) => /* @__PURE__ */ jsx4(ComponentsPanel, { children }),
|
|
582
|
+
componentItem: ({ children, name }) => /* @__PURE__ */ jsx4(ComponentItemFilter, { name, children }),
|
|
583
|
+
fields: ({ children, isLoading }) => /* @__PURE__ */ jsx4(VersionAwareFieldsPanel, { isLoading, children })
|
|
584
|
+
}
|
|
585
|
+
},
|
|
586
|
+
`${versionHistory.selectedVersionId ?? "current"}:${reloadNonce}`
|
|
587
|
+
),
|
|
588
|
+
/* @__PURE__ */ jsx4(
|
|
589
|
+
CreateTemplateDialog,
|
|
590
|
+
{
|
|
591
|
+
isOpen: templateDialogOpen,
|
|
592
|
+
onOpenChange: (open) => setTemplateDialogOpen(open),
|
|
593
|
+
onConfirm: handleCreateTemplate,
|
|
594
|
+
saving: templateSaving
|
|
448
595
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
) })
|
|
596
|
+
)
|
|
597
|
+
] })
|
|
452
598
|
}
|
|
453
599
|
);
|
|
454
600
|
};
|
|
@@ -457,19 +603,21 @@ var ContentEditor = ({
|
|
|
457
603
|
projectKey,
|
|
458
604
|
businessUnitKey,
|
|
459
605
|
jwtToken,
|
|
606
|
+
locale,
|
|
460
607
|
contentKey,
|
|
461
608
|
config,
|
|
462
609
|
onPublish,
|
|
463
610
|
onSave,
|
|
464
611
|
onError
|
|
465
|
-
}) => /* @__PURE__ */
|
|
612
|
+
}) => /* @__PURE__ */ jsx4(EnsureNimbusProvider, { locale, children: /* @__PURE__ */ jsx4(EnsureIntlProvider, { children: /* @__PURE__ */ jsx4(
|
|
466
613
|
PuckApiProvider2,
|
|
467
614
|
{
|
|
468
615
|
baseURL,
|
|
469
616
|
projectKey,
|
|
470
617
|
businessUnitKey,
|
|
471
618
|
jwtToken,
|
|
472
|
-
|
|
619
|
+
locale,
|
|
620
|
+
children: /* @__PURE__ */ jsx4(
|
|
473
621
|
ContentEditorInner,
|
|
474
622
|
{
|
|
475
623
|
contentKey,
|
|
@@ -480,7 +628,7 @@ var ContentEditor = ({
|
|
|
480
628
|
}
|
|
481
629
|
)
|
|
482
630
|
}
|
|
483
|
-
) });
|
|
631
|
+
) }) });
|
|
484
632
|
|
|
485
633
|
// src/ContentManagerRouter.tsx
|
|
486
634
|
import { useState as useState3, useCallback as useCallback2, useMemo as useMemo2, useRef as useRef2 } from "react";
|
|
@@ -498,9 +646,16 @@ import {
|
|
|
498
646
|
ComponentSearchProvider as ComponentSearchProvider2,
|
|
499
647
|
ComponentsPanel as ComponentsPanel2,
|
|
500
648
|
ComponentItemFilter as ComponentItemFilter2,
|
|
649
|
+
CreateTemplateDialog as CreateTemplateDialog2,
|
|
501
650
|
defaultPuckConfig,
|
|
502
|
-
EditorToolbar as EditorToolbar2
|
|
651
|
+
EditorToolbar as EditorToolbar2,
|
|
652
|
+
nimbusFieldTypes as nimbusFieldTypes2,
|
|
653
|
+
PropertiesResizer,
|
|
654
|
+
stripPuckDataToTemplate as stripPuckDataToTemplate2,
|
|
655
|
+
UnsavedChangesDialog,
|
|
656
|
+
useDirtyState as useDirtyState2
|
|
503
657
|
} from "@commercetools-demo/puck-editor";
|
|
658
|
+
import { PuckRenderer } from "@commercetools-demo/puck-renderer";
|
|
504
659
|
import {
|
|
505
660
|
VersionHistoryProvider as VersionHistoryProvider2,
|
|
506
661
|
VersionHistoryButton as VersionHistoryButton2,
|
|
@@ -512,21 +667,33 @@ import {
|
|
|
512
667
|
import {
|
|
513
668
|
PuckApiProvider as PuckApiProvider3,
|
|
514
669
|
usePuckContents as usePuckContents2,
|
|
515
|
-
usePuckContent as usePuckContent2
|
|
670
|
+
usePuckContent as usePuckContent2,
|
|
671
|
+
usePuckTemplates as usePuckTemplates3
|
|
516
672
|
} from "@commercetools-demo/puck-api";
|
|
517
|
-
import
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
673
|
+
import {
|
|
674
|
+
Badge as Badge2,
|
|
675
|
+
Button as Button2,
|
|
676
|
+
Card as Card2,
|
|
677
|
+
DataTable as DataTable2,
|
|
678
|
+
Dialog as Dialog2,
|
|
679
|
+
FormField as FormField2,
|
|
680
|
+
Icon as Icon2,
|
|
681
|
+
IconButton as IconButton2,
|
|
682
|
+
LoadingSpinner as LoadingSpinner3,
|
|
683
|
+
Select as Select2,
|
|
684
|
+
Stack as Stack3,
|
|
685
|
+
Text as Text3,
|
|
686
|
+
TextInput as TextInput2
|
|
687
|
+
} from "@commercetools/nimbus";
|
|
688
|
+
import {
|
|
689
|
+
Add as Add2,
|
|
690
|
+
ChevronLeft,
|
|
691
|
+
Close as Close2,
|
|
692
|
+
Delete as Delete2,
|
|
693
|
+
Edit as Edit2,
|
|
694
|
+
Visibility
|
|
695
|
+
} from "@commercetools/nimbus-icons";
|
|
696
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
530
697
|
var DEFAULT_CONFIG = {
|
|
531
698
|
...defaultPuckConfig,
|
|
532
699
|
components: { ...defaultPuckConfig.components }
|
|
@@ -539,28 +706,23 @@ var NAV_BAR_STYLE = {
|
|
|
539
706
|
gap: "12px",
|
|
540
707
|
padding: "8px 16px",
|
|
541
708
|
background: "var(--color-surface, #fff)",
|
|
542
|
-
borderBottom: "1px solid var(--color-neutral-90)",
|
|
709
|
+
borderBottom: "1px solid var(--color-neutral-90, #e0e0e0)",
|
|
543
710
|
zIndex: 200,
|
|
544
711
|
flexShrink: 0
|
|
545
712
|
};
|
|
546
|
-
var COLUMNS = [
|
|
547
|
-
{ key: "name", label: "Name" },
|
|
548
|
-
{ key: "contentType", label: "Content Type" },
|
|
549
|
-
{ key: "status", label: "Status" },
|
|
550
|
-
{ key: "updatedAt", label: "Updated" },
|
|
551
|
-
{ key: "actions", label: "Actions", shouldIgnoreRowClick: true }
|
|
552
|
-
];
|
|
553
713
|
var ContentListRoute = ({ defaultContentType, backButton }) => {
|
|
554
714
|
const history = useHistory();
|
|
555
|
-
const { contents, loading, error,
|
|
556
|
-
const
|
|
715
|
+
const { contents, loading, error, createContent, deleteContent } = usePuckContents2(defaultContentType);
|
|
716
|
+
const { templates } = usePuckTemplates3("content");
|
|
717
|
+
const [search, setSearch] = useState3("");
|
|
557
718
|
const [showCreate, setShowCreate] = useState3(false);
|
|
558
719
|
const [createName, setCreateName] = useState3("");
|
|
559
720
|
const [createType, setCreateType] = useState3(defaultContentType ?? "");
|
|
721
|
+
const [templateKey, setTemplateKey] = useState3("");
|
|
560
722
|
const [createError, setCreateError] = useState3(null);
|
|
561
723
|
const [creating, setCreating] = useState3(false);
|
|
562
724
|
const [deleting, setDeleting] = useState3(null);
|
|
563
|
-
const
|
|
725
|
+
const [pendingDelete, setPendingDelete] = useState3(null);
|
|
564
726
|
const handleCreate = async () => {
|
|
565
727
|
setCreateError(null);
|
|
566
728
|
if (!createName.trim()) {
|
|
@@ -573,15 +735,17 @@ var ContentListRoute = ({ defaultContentType, backButton }) => {
|
|
|
573
735
|
}
|
|
574
736
|
setCreating(true);
|
|
575
737
|
try {
|
|
738
|
+
const template = templateKey ? templates.find((t) => t.key === templateKey) : void 0;
|
|
576
739
|
const input = {
|
|
577
740
|
name: createName.trim(),
|
|
578
741
|
contentType: createType.trim(),
|
|
579
|
-
data: { content: [], root: { props: {} } }
|
|
742
|
+
data: template?.value.puckData ?? { content: [], root: { props: {} } }
|
|
580
743
|
};
|
|
581
744
|
const created = await createContent(input);
|
|
582
745
|
setShowCreate(false);
|
|
583
746
|
setCreateName("");
|
|
584
747
|
setCreateType(defaultContentType ?? "");
|
|
748
|
+
setTemplateKey("");
|
|
585
749
|
history.push(`/${created.key}`, { contentName: created.value.name });
|
|
586
750
|
} catch (err) {
|
|
587
751
|
setCreateError(err.message);
|
|
@@ -589,163 +753,227 @@ var ContentListRoute = ({ defaultContentType, backButton }) => {
|
|
|
589
753
|
setCreating(false);
|
|
590
754
|
}
|
|
591
755
|
};
|
|
592
|
-
const handleDelete = async (
|
|
593
|
-
|
|
594
|
-
setDeleting(key);
|
|
756
|
+
const handleDelete = async (item) => {
|
|
757
|
+
setDeleting(item.key);
|
|
595
758
|
try {
|
|
596
|
-
await deleteContent(key);
|
|
759
|
+
await deleteContent(item.key);
|
|
760
|
+
setPendingDelete(null);
|
|
597
761
|
} finally {
|
|
598
762
|
setDeleting(null);
|
|
599
763
|
}
|
|
600
764
|
};
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
765
|
+
const term = search.trim().toLowerCase();
|
|
766
|
+
const filteredContents = term ? contents.filter(
|
|
767
|
+
(c) => c.value.name.toLowerCase().includes(term) || c.value.contentType.toLowerCase().includes(term)
|
|
768
|
+
) : contents;
|
|
769
|
+
const rows = filteredContents.map((c) => ({ ...c, id: c.key }));
|
|
770
|
+
const columns = [
|
|
771
|
+
{
|
|
772
|
+
id: "name",
|
|
773
|
+
header: "Name",
|
|
774
|
+
accessor: (row) => row.value.name,
|
|
775
|
+
render: ({ row }) => /* @__PURE__ */ jsx5(Text3, { fontWeight: "bold", children: row.value.name })
|
|
776
|
+
},
|
|
777
|
+
{
|
|
778
|
+
id: "contentType",
|
|
779
|
+
header: "Content Type",
|
|
780
|
+
accessor: (row) => row.value.contentType,
|
|
781
|
+
render: ({ row }) => /* @__PURE__ */ jsx5(
|
|
782
|
+
"code",
|
|
610
783
|
{
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
784
|
+
style: {
|
|
785
|
+
background: "#f4f4f4",
|
|
786
|
+
padding: "2px 6px",
|
|
787
|
+
borderRadius: "4px",
|
|
788
|
+
fontSize: "11px",
|
|
789
|
+
fontFamily: "monospace"
|
|
790
|
+
},
|
|
791
|
+
children: row.value.contentType
|
|
614
792
|
}
|
|
615
793
|
)
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
id: "status",
|
|
797
|
+
header: "Status",
|
|
798
|
+
accessor: () => "",
|
|
799
|
+
isSortable: false,
|
|
800
|
+
render: ({ row }) => {
|
|
801
|
+
const hasDraft = !!row.states.draft;
|
|
802
|
+
const hasPublished = !!row.states.published;
|
|
803
|
+
return /* @__PURE__ */ jsxs3(Stack3, { direction: "row", gap: "100", wrap: "wrap", children: [
|
|
804
|
+
hasDraft && /* @__PURE__ */ jsx5(Badge2, { colorPalette: "warning", size: "xs", children: "Draft" }),
|
|
805
|
+
hasPublished && /* @__PURE__ */ jsx5(Badge2, { colorPalette: "positive", size: "xs", children: "Published" }),
|
|
806
|
+
!hasDraft && !hasPublished && /* @__PURE__ */ jsx5(Badge2, { colorPalette: "neutral", size: "xs", children: "No state" })
|
|
807
|
+
] });
|
|
808
|
+
}
|
|
809
|
+
},
|
|
810
|
+
{
|
|
811
|
+
id: "updatedAt",
|
|
812
|
+
header: "Updated",
|
|
813
|
+
accessor: (row) => row.value.updatedAt,
|
|
814
|
+
render: ({ row }) => /* @__PURE__ */ jsx5(Text3, { color: "neutral.11", children: new Date(row.value.updatedAt).toLocaleDateString() })
|
|
815
|
+
},
|
|
816
|
+
{
|
|
817
|
+
id: "actions",
|
|
818
|
+
header: "Actions",
|
|
819
|
+
accessor: () => "",
|
|
820
|
+
isSortable: false,
|
|
821
|
+
render: ({ row }) => /* @__PURE__ */ jsxs3(Stack3, { direction: "row", gap: "100", alignItems: "center", children: [
|
|
822
|
+
/* @__PURE__ */ jsx5(
|
|
823
|
+
IconButton2,
|
|
824
|
+
{
|
|
825
|
+
"aria-label": `Edit ${row.value.name}`,
|
|
826
|
+
variant: "ghost",
|
|
827
|
+
size: "xs",
|
|
828
|
+
onPress: () => history.push(`/${row.key}`, { contentName: row.value.name }),
|
|
829
|
+
children: /* @__PURE__ */ jsx5(Edit2, {})
|
|
830
|
+
}
|
|
831
|
+
),
|
|
832
|
+
/* @__PURE__ */ jsx5(
|
|
833
|
+
IconButton2,
|
|
649
834
|
{
|
|
650
|
-
label:
|
|
651
|
-
|
|
652
|
-
|
|
835
|
+
"aria-label": `Preview ${row.value.name}`,
|
|
836
|
+
variant: "ghost",
|
|
837
|
+
size: "xs",
|
|
838
|
+
onPress: () => history.push(`/${row.key}/preview`, { contentName: row.value.name }),
|
|
839
|
+
children: /* @__PURE__ */ jsx5(Visibility, {})
|
|
653
840
|
}
|
|
654
841
|
),
|
|
655
|
-
/* @__PURE__ */
|
|
842
|
+
/* @__PURE__ */ jsx5(
|
|
843
|
+
IconButton2,
|
|
844
|
+
{
|
|
845
|
+
"aria-label": `Delete ${row.value.name}`,
|
|
846
|
+
variant: "ghost",
|
|
847
|
+
colorPalette: "critical",
|
|
848
|
+
size: "xs",
|
|
849
|
+
isDisabled: deleting === row.key,
|
|
850
|
+
onPress: () => setPendingDelete(row),
|
|
851
|
+
children: /* @__PURE__ */ jsx5(Delete2, {})
|
|
852
|
+
}
|
|
853
|
+
)
|
|
656
854
|
] })
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
855
|
+
}
|
|
856
|
+
];
|
|
857
|
+
return /* @__PURE__ */ jsxs3("div", { style: { maxWidth: "1200px", margin: "0 auto", padding: "32px 24px" }, children: [
|
|
858
|
+
/* @__PURE__ */ jsxs3(Stack3, { direction: "column", gap: "600", children: [
|
|
859
|
+
/* @__PURE__ */ jsxs3(Stack3, { direction: "row", justifyContent: "space-between", alignItems: "center", children: [
|
|
860
|
+
/* @__PURE__ */ jsxs3(Stack3, { direction: "row", gap: "400", alignItems: "center", children: [
|
|
861
|
+
backButton,
|
|
862
|
+
/* @__PURE__ */ jsx5(Text3, { as: "h1", fontSize: "2xl", fontWeight: "700", children: "Content Items" })
|
|
863
|
+
] }),
|
|
864
|
+
/* @__PURE__ */ jsxs3(Button2, { variant: "solid", onPress: () => setShowCreate((v) => !v), children: [
|
|
865
|
+
/* @__PURE__ */ jsx5(Icon2, { as: Add2 }),
|
|
866
|
+
" New Content"
|
|
867
|
+
] })
|
|
868
|
+
] }),
|
|
869
|
+
showCreate && /* @__PURE__ */ jsx5(Card2.Root, { variant: "outlined", children: /* @__PURE__ */ jsx5(Card2.Body, { children: /* @__PURE__ */ jsxs3(Stack3, { direction: "column", gap: "400", children: [
|
|
870
|
+
/* @__PURE__ */ jsx5(Text3, { as: "h4", fontSize: "xl", fontWeight: "700", children: "Create Content Item" }),
|
|
871
|
+
createError && /* @__PURE__ */ jsx5(Text3, { color: "critical.11", children: createError }),
|
|
872
|
+
/* @__PURE__ */ jsxs3(Stack3, { direction: "row", gap: "400", children: [
|
|
873
|
+
/* @__PURE__ */ jsx5("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsxs3(FormField2.Root, { children: [
|
|
874
|
+
/* @__PURE__ */ jsx5(FormField2.Label, { children: "Name" }),
|
|
875
|
+
/* @__PURE__ */ jsx5(FormField2.Input, { children: /* @__PURE__ */ jsx5(
|
|
876
|
+
TextInput2,
|
|
877
|
+
{
|
|
878
|
+
value: createName,
|
|
879
|
+
onChange: (v) => setCreateName(v),
|
|
880
|
+
placeholder: "e.g. Homepage Hero"
|
|
881
|
+
}
|
|
882
|
+
) })
|
|
883
|
+
] }) }),
|
|
884
|
+
/* @__PURE__ */ jsx5("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsxs3(FormField2.Root, { children: [
|
|
885
|
+
/* @__PURE__ */ jsx5(FormField2.Label, { children: "Content Type" }),
|
|
886
|
+
/* @__PURE__ */ jsx5(FormField2.Input, { children: /* @__PURE__ */ jsx5(
|
|
887
|
+
TextInput2,
|
|
888
|
+
{
|
|
889
|
+
value: createType,
|
|
890
|
+
onChange: (v) => setCreateType(v),
|
|
891
|
+
placeholder: "e.g. hero, banner"
|
|
892
|
+
}
|
|
893
|
+
) })
|
|
894
|
+
] }) })
|
|
895
|
+
] }),
|
|
896
|
+
/* @__PURE__ */ jsxs3(FormField2.Root, { children: [
|
|
897
|
+
/* @__PURE__ */ jsx5(FormField2.Label, { children: "Template" }),
|
|
898
|
+
/* @__PURE__ */ jsx5(FormField2.Input, { children: /* @__PURE__ */ jsx5(
|
|
899
|
+
Select2.Root,
|
|
900
|
+
{
|
|
901
|
+
"aria-label": "Template",
|
|
902
|
+
selectedKey: templateKey || "empty",
|
|
903
|
+
onSelectionChange: (key) => setTemplateKey(key == null || key === "empty" ? "" : String(key)),
|
|
904
|
+
children: /* @__PURE__ */ jsxs3(Select2.Options, { children: [
|
|
905
|
+
/* @__PURE__ */ jsx5(Select2.Option, { id: "empty", children: "Empty" }),
|
|
906
|
+
templates.map((t) => /* @__PURE__ */ jsx5(Select2.Option, { id: t.key, children: t.value.name }, t.key))
|
|
907
|
+
] })
|
|
908
|
+
}
|
|
909
|
+
) })
|
|
910
|
+
] }),
|
|
911
|
+
/* @__PURE__ */ jsxs3(Stack3, { direction: "row", gap: "200", children: [
|
|
912
|
+
/* @__PURE__ */ jsx5(Button2, { variant: "solid", onPress: () => void handleCreate(), isDisabled: creating, children: creating ? "Creating\u2026" : "Create" }),
|
|
913
|
+
/* @__PURE__ */ jsx5(Button2, { variant: "outline", onPress: () => setShowCreate(false), children: "Cancel" })
|
|
914
|
+
] })
|
|
915
|
+
] }) }) }),
|
|
916
|
+
/* @__PURE__ */ jsx5("div", { style: { maxWidth: 360 }, children: /* @__PURE__ */ jsx5(
|
|
660
917
|
TextInput2,
|
|
661
918
|
{
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
919
|
+
"aria-label": "Search content",
|
|
920
|
+
placeholder: "Search by name or content type\u2026",
|
|
921
|
+
value: search,
|
|
922
|
+
onChange: (v) => setSearch(v),
|
|
923
|
+
width: "100%",
|
|
924
|
+
trailingElement: search !== "" ? /* @__PURE__ */ jsx5(
|
|
925
|
+
IconButton2,
|
|
926
|
+
{
|
|
927
|
+
"aria-label": "Clear search",
|
|
928
|
+
variant: "ghost",
|
|
929
|
+
colorPalette: "neutral",
|
|
930
|
+
size: "2xs",
|
|
931
|
+
onPress: () => setSearch(""),
|
|
932
|
+
children: /* @__PURE__ */ jsx5(Close2, {})
|
|
933
|
+
}
|
|
934
|
+
) : void 0
|
|
665
935
|
}
|
|
666
936
|
) }),
|
|
667
|
-
/* @__PURE__ */
|
|
668
|
-
|
|
669
|
-
{
|
|
670
|
-
label: "Filter",
|
|
671
|
-
iconLeft: /* @__PURE__ */ jsx4(SearchIcon2, {}),
|
|
672
|
-
onClick: handleFilter
|
|
673
|
-
}
|
|
674
|
-
),
|
|
675
|
-
/* @__PURE__ */ jsx4(
|
|
676
|
-
FlatButton2,
|
|
677
|
-
{
|
|
678
|
-
label: "Clear",
|
|
679
|
-
onClick: () => {
|
|
680
|
-
setFilterType("");
|
|
681
|
-
void fetchContents(void 0);
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
),
|
|
685
|
-
/* @__PURE__ */ jsx4(FlatButton2, { label: "Refresh", onClick: () => void refresh() })
|
|
937
|
+
error && /* @__PURE__ */ jsx5(Text3, { color: "critical.11", children: error }),
|
|
938
|
+
loading ? /* @__PURE__ */ jsx5("div", { style: { display: "flex", justifyContent: "center", padding: "48px" }, children: /* @__PURE__ */ jsx5(LoadingSpinner3, {}) }) : contents.length === 0 ? /* @__PURE__ */ jsx5(Stack3, { direction: "column", gap: "400", alignItems: "center", children: /* @__PURE__ */ jsx5(Text3, { color: "neutral.11", children: "No content items found." }) }) : /* @__PURE__ */ jsx5(DataTable2, { columns, rows, "aria-label": "Content items" })
|
|
686
939
|
] }),
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
DataTable2,
|
|
940
|
+
/* @__PURE__ */ jsx5(
|
|
941
|
+
Dialog2.Root,
|
|
690
942
|
{
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
case "actions":
|
|
723
|
-
return /* @__PURE__ */ jsxs3(Spacings3.Inline, { scale: "s", alignItems: "center", children: [
|
|
724
|
-
/* @__PURE__ */ jsx4(
|
|
725
|
-
PrimaryButton2,
|
|
726
|
-
{
|
|
727
|
-
label: "Edit",
|
|
728
|
-
size: "20",
|
|
729
|
-
onClick: () => history.push(`/${row.key}`, { contentName: row.value.name })
|
|
730
|
-
}
|
|
731
|
-
),
|
|
732
|
-
/* @__PURE__ */ jsx4(
|
|
733
|
-
FlatButton2,
|
|
734
|
-
{
|
|
735
|
-
tone: "critical",
|
|
736
|
-
label: deleting === row.key ? "\u2026" : "Delete",
|
|
737
|
-
isDisabled: deleting === row.key,
|
|
738
|
-
onClick: () => void handleDelete(row.key)
|
|
739
|
-
}
|
|
740
|
-
)
|
|
741
|
-
] });
|
|
742
|
-
default:
|
|
743
|
-
return null;
|
|
744
|
-
}
|
|
745
|
-
}
|
|
943
|
+
isOpen: pendingDelete !== null,
|
|
944
|
+
onOpenChange: (open) => {
|
|
945
|
+
if (!open) setPendingDelete(null);
|
|
946
|
+
},
|
|
947
|
+
children: /* @__PURE__ */ jsxs3(Dialog2.Content, { children: [
|
|
948
|
+
/* @__PURE__ */ jsxs3(Dialog2.Header, { children: [
|
|
949
|
+
/* @__PURE__ */ jsx5(Dialog2.Title, { children: "Delete content item?" }),
|
|
950
|
+
/* @__PURE__ */ jsx5(Dialog2.CloseTrigger, {})
|
|
951
|
+
] }),
|
|
952
|
+
/* @__PURE__ */ jsx5(Dialog2.Body, { children: /* @__PURE__ */ jsxs3(Text3, { children: [
|
|
953
|
+
"Are you sure you want to delete",
|
|
954
|
+
" ",
|
|
955
|
+
/* @__PURE__ */ jsx5(Text3, { as: "span", fontWeight: "700", children: pendingDelete?.value.name }),
|
|
956
|
+
" ",
|
|
957
|
+
"and all its versions? This cannot be undone."
|
|
958
|
+
] }) }),
|
|
959
|
+
/* @__PURE__ */ jsxs3(Dialog2.Footer, { children: [
|
|
960
|
+
/* @__PURE__ */ jsx5(Button2, { slot: "close", variant: "outline", isDisabled: deleting !== null, children: "Cancel" }),
|
|
961
|
+
/* @__PURE__ */ jsx5(
|
|
962
|
+
Button2,
|
|
963
|
+
{
|
|
964
|
+
colorPalette: "critical",
|
|
965
|
+
isDisabled: deleting !== null,
|
|
966
|
+
onPress: () => {
|
|
967
|
+
if (pendingDelete) void handleDelete(pendingDelete);
|
|
968
|
+
},
|
|
969
|
+
children: deleting !== null ? "Deleting\u2026" : "Delete"
|
|
970
|
+
}
|
|
971
|
+
)
|
|
972
|
+
] })
|
|
973
|
+
] })
|
|
746
974
|
}
|
|
747
975
|
)
|
|
748
|
-
] })
|
|
976
|
+
] });
|
|
749
977
|
};
|
|
750
978
|
var ContentEditorRoute = ({ config, backButton }) => {
|
|
751
979
|
const { contentKey } = useParams();
|
|
@@ -764,9 +992,13 @@ var ContentEditorRoute = ({ config, backButton }) => {
|
|
|
764
992
|
revertToPublished,
|
|
765
993
|
loadVersions
|
|
766
994
|
} = usePuckContent2(contentKey);
|
|
995
|
+
const { createTemplate } = usePuckTemplates3("content");
|
|
767
996
|
const latestDataRef = useRef2(null);
|
|
768
|
-
const [hasUnsavedChanges, setHasUnsavedChanges] = useState3(false);
|
|
769
997
|
const [isApplyingVersion, setIsApplyingVersion] = useState3(false);
|
|
998
|
+
const [reloadNonce, setReloadNonce] = useState3(0);
|
|
999
|
+
const [pendingNav, setPendingNav] = useState3(null);
|
|
1000
|
+
const [templateDialogOpen, setTemplateDialogOpen] = useState3(false);
|
|
1001
|
+
const [templateSaving, setTemplateSaving] = useState3(false);
|
|
770
1002
|
const currentData = states.draft?.data ?? content?.data ?? { content: [], root: { props: {} } };
|
|
771
1003
|
const versionHistory = useVersionHistoryPanel2({
|
|
772
1004
|
versions,
|
|
@@ -776,37 +1008,52 @@ var ContentEditorRoute = ({ config, backButton }) => {
|
|
|
776
1008
|
const diff = useVersionDiff2(versionHistory.previewData, currentData);
|
|
777
1009
|
const isPreviewingRef = useRef2(false);
|
|
778
1010
|
isPreviewingRef.current = versionHistory.isPreviewingHistory;
|
|
779
|
-
const
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
1011
|
+
const canvasKey = `${contentKey}:${versionHistory.selectedVersionId ?? "current"}:${reloadNonce}`;
|
|
1012
|
+
const { isDirty: hasUnsavedChanges, markChange, markSaved } = useDirtyState2(canvasKey);
|
|
1013
|
+
const guardedNavigate = useCallback2(
|
|
1014
|
+
(navFn) => {
|
|
1015
|
+
if (hasUnsavedChanges) {
|
|
1016
|
+
setPendingNav(() => navFn);
|
|
1017
|
+
} else {
|
|
1018
|
+
navFn();
|
|
1019
|
+
}
|
|
1020
|
+
},
|
|
1021
|
+
[hasUnsavedChanges]
|
|
1022
|
+
);
|
|
1023
|
+
const handleChange = useCallback2(
|
|
1024
|
+
(data) => {
|
|
1025
|
+
if (isPreviewingRef.current) return;
|
|
1026
|
+
latestDataRef.current = data;
|
|
1027
|
+
markChange(data);
|
|
1028
|
+
},
|
|
1029
|
+
[markChange]
|
|
1030
|
+
);
|
|
784
1031
|
const handleSave = useCallback2(async () => {
|
|
785
1032
|
const data = latestDataRef.current;
|
|
786
1033
|
if (!data) return;
|
|
787
1034
|
try {
|
|
788
1035
|
await saveDraft(data);
|
|
789
|
-
|
|
1036
|
+
markSaved(data);
|
|
790
1037
|
} catch (err) {
|
|
791
1038
|
console.error("[ContentManagerRouter] save error:", err);
|
|
792
1039
|
}
|
|
793
|
-
}, [saveDraft]);
|
|
1040
|
+
}, [saveDraft, markSaved]);
|
|
794
1041
|
const handlePublish = useCallback2(
|
|
795
1042
|
async (data) => {
|
|
796
1043
|
try {
|
|
797
1044
|
await saveDraft(data);
|
|
798
|
-
|
|
1045
|
+
markSaved(data);
|
|
799
1046
|
await publish(false);
|
|
800
1047
|
} catch (err) {
|
|
801
1048
|
console.error("[ContentManagerRouter] publish error:", err);
|
|
802
1049
|
}
|
|
803
1050
|
},
|
|
804
|
-
[saveDraft, publish]
|
|
1051
|
+
[saveDraft, publish, markSaved]
|
|
805
1052
|
);
|
|
806
1053
|
const handleRevert = useCallback2(async () => {
|
|
807
1054
|
try {
|
|
808
1055
|
await revertToPublished();
|
|
809
|
-
|
|
1056
|
+
setReloadNonce((n) => n + 1);
|
|
810
1057
|
} catch (err) {
|
|
811
1058
|
console.error("[ContentManagerRouter] revert error:", err);
|
|
812
1059
|
}
|
|
@@ -817,14 +1064,14 @@ var ContentEditorRoute = ({ config, backButton }) => {
|
|
|
817
1064
|
setIsApplyingVersion(true);
|
|
818
1065
|
try {
|
|
819
1066
|
await saveDraft(versionData);
|
|
820
|
-
|
|
1067
|
+
markSaved(versionData);
|
|
821
1068
|
versionHistory.clearSelection();
|
|
822
1069
|
} catch (err) {
|
|
823
1070
|
console.error("[ContentManagerRouter] apply version error:", err);
|
|
824
1071
|
} finally {
|
|
825
1072
|
setIsApplyingVersion(false);
|
|
826
1073
|
}
|
|
827
|
-
}, [versionHistory, saveDraft]);
|
|
1074
|
+
}, [versionHistory, saveDraft, markSaved]);
|
|
828
1075
|
const contentConfig = useMemo2(() => {
|
|
829
1076
|
const otherRootFields = Object.fromEntries(
|
|
830
1077
|
Object.entries(config.root?.fields ?? {}).filter(([k]) => k !== "title")
|
|
@@ -846,22 +1093,38 @@ var ContentEditorRoute = ({ config, backButton }) => {
|
|
|
846
1093
|
}
|
|
847
1094
|
};
|
|
848
1095
|
}, [config]);
|
|
1096
|
+
const handleCreateTemplate = useCallback2(
|
|
1097
|
+
async (name, withoutData) => {
|
|
1098
|
+
const source = latestDataRef.current ?? currentData;
|
|
1099
|
+
const puckData = withoutData ? stripPuckDataToTemplate2(source, contentConfig) : source;
|
|
1100
|
+
setTemplateSaving(true);
|
|
1101
|
+
try {
|
|
1102
|
+
await createTemplate({ name, kind: "content", puckData });
|
|
1103
|
+
setTemplateDialogOpen(false);
|
|
1104
|
+
} catch (err) {
|
|
1105
|
+
console.error("[ContentManagerRouter] create template error:", err);
|
|
1106
|
+
} finally {
|
|
1107
|
+
setTemplateSaving(false);
|
|
1108
|
+
}
|
|
1109
|
+
},
|
|
1110
|
+
[createTemplate, currentData, contentConfig]
|
|
1111
|
+
);
|
|
849
1112
|
if (loading) {
|
|
850
|
-
return /* @__PURE__ */
|
|
851
|
-
/* @__PURE__ */
|
|
852
|
-
/* @__PURE__ */
|
|
1113
|
+
return /* @__PURE__ */ jsx5("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", height: "100vh" }, children: /* @__PURE__ */ jsxs3(Stack3, { direction: "column", gap: "400", alignItems: "center", children: [
|
|
1114
|
+
/* @__PURE__ */ jsx5(LoadingSpinner3, {}),
|
|
1115
|
+
/* @__PURE__ */ jsx5(Text3, { color: "neutral.11", children: "Loading editor\u2026" })
|
|
853
1116
|
] }) });
|
|
854
1117
|
}
|
|
855
1118
|
if (error) {
|
|
856
|
-
return /* @__PURE__ */
|
|
857
|
-
/* @__PURE__ */
|
|
1119
|
+
return /* @__PURE__ */ jsx5("div", { style: { padding: "32px" }, children: /* @__PURE__ */ jsxs3(Text3, { color: "critical.11", children: [
|
|
1120
|
+
/* @__PURE__ */ jsx5("strong", { children: "Error loading content:" }),
|
|
858
1121
|
" ",
|
|
859
1122
|
error
|
|
860
1123
|
] }) });
|
|
861
1124
|
}
|
|
862
1125
|
const activeData = versionHistory.previewData ?? currentData;
|
|
863
1126
|
const toolbarStates = states;
|
|
864
|
-
return /* @__PURE__ */
|
|
1127
|
+
return /* @__PURE__ */ jsxs3(
|
|
865
1128
|
VersionHistoryProvider2,
|
|
866
1129
|
{
|
|
867
1130
|
diff,
|
|
@@ -874,82 +1137,164 @@ var ContentEditorRoute = ({ config, backButton }) => {
|
|
|
874
1137
|
onApply: () => void handleApplyVersion(),
|
|
875
1138
|
onDiscard: versionHistory.clearSelection,
|
|
876
1139
|
onLoadVersions: versionHistory.openPanel,
|
|
877
|
-
children:
|
|
878
|
-
/* @__PURE__ */ jsxs3("div", { style:
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1140
|
+
children: [
|
|
1141
|
+
/* @__PURE__ */ jsxs3("div", { style: { display: "flex", flexDirection: "column", height: "100%" }, children: [
|
|
1142
|
+
/* @__PURE__ */ jsxs3("div", { style: NAV_BAR_STYLE, children: [
|
|
1143
|
+
backButton,
|
|
1144
|
+
backButton && /* @__PURE__ */ jsx5(Text3, { color: "neutral.11", children: "/" }),
|
|
1145
|
+
/* @__PURE__ */ jsxs3(
|
|
1146
|
+
Button2,
|
|
1147
|
+
{
|
|
1148
|
+
variant: "ghost",
|
|
1149
|
+
onPress: () => guardedNavigate(() => history.push("/")),
|
|
1150
|
+
children: [
|
|
1151
|
+
/* @__PURE__ */ jsx5(Icon2, { as: ChevronLeft }),
|
|
1152
|
+
" Content Items"
|
|
1153
|
+
]
|
|
1154
|
+
}
|
|
1155
|
+
),
|
|
1156
|
+
/* @__PURE__ */ jsx5(Text3, { color: "neutral.11", children: "/" }),
|
|
1157
|
+
/* @__PURE__ */ jsx5(Text3, { fontWeight: "bold", children: contentName })
|
|
1158
|
+
] }),
|
|
1159
|
+
/* @__PURE__ */ jsxs3("div", { className: "puck-editor-fill", style: { flex: 1, overflow: "hidden" }, children: [
|
|
1160
|
+
/* @__PURE__ */ jsx5(ComponentSearchProvider2, { children: /* @__PURE__ */ jsx5(
|
|
1161
|
+
Puck2,
|
|
1162
|
+
{
|
|
1163
|
+
config: contentConfig,
|
|
1164
|
+
data: activeData,
|
|
1165
|
+
onChange: handleChange,
|
|
1166
|
+
onPublish: handlePublish,
|
|
1167
|
+
overrides: {
|
|
1168
|
+
fieldTypes: nimbusFieldTypes2,
|
|
1169
|
+
header: () => versionHistory.isPreviewingHistory ? /* @__PURE__ */ jsxs3(
|
|
1170
|
+
Stack3,
|
|
1171
|
+
{
|
|
1172
|
+
gridArea: "header",
|
|
1173
|
+
direction: "row",
|
|
1174
|
+
gap: "200",
|
|
1175
|
+
alignItems: "center",
|
|
1176
|
+
justifyContent: "flex-end",
|
|
1177
|
+
padding: "200",
|
|
1178
|
+
children: [
|
|
1179
|
+
/* @__PURE__ */ jsx5(
|
|
1180
|
+
VersionPreviewBanner2,
|
|
1181
|
+
{
|
|
1182
|
+
timestamp: versionHistory.selectedVersion.timestamp,
|
|
1183
|
+
onApply: () => void handleApplyVersion(),
|
|
1184
|
+
onDiscard: versionHistory.clearSelection,
|
|
1185
|
+
isApplying: isApplyingVersion
|
|
1186
|
+
}
|
|
1187
|
+
),
|
|
1188
|
+
/* @__PURE__ */ jsx5(VersionHistoryButton2, { disabled: isApplyingVersion })
|
|
1189
|
+
]
|
|
1190
|
+
}
|
|
1191
|
+
) : /* @__PURE__ */ jsx5(
|
|
1192
|
+
EditorToolbar2,
|
|
1193
|
+
{
|
|
1194
|
+
title: content?.name ?? contentName,
|
|
1195
|
+
saving,
|
|
1196
|
+
isDirty: hasUnsavedChanges,
|
|
1197
|
+
states: toolbarStates,
|
|
1198
|
+
onSave: () => void handleSave(),
|
|
1199
|
+
onPublish: () => void handlePublish(activeData),
|
|
1200
|
+
onRevert: () => void handleRevert(),
|
|
1201
|
+
onPreview: () => guardedNavigate(
|
|
1202
|
+
() => history.push(`/${contentKey}/preview`, { contentName })
|
|
1203
|
+
),
|
|
1204
|
+
onCreateTemplate: () => setTemplateDialogOpen(true),
|
|
1205
|
+
createTemplateLabel: "Create a template from this content",
|
|
1206
|
+
showPublishButton: true
|
|
1207
|
+
}
|
|
1208
|
+
),
|
|
1209
|
+
components: ({ children }) => /* @__PURE__ */ jsx5(ComponentsPanel2, { children }),
|
|
1210
|
+
componentItem: ({ children, name }) => /* @__PURE__ */ jsx5(ComponentItemFilter2, { name, children }),
|
|
1211
|
+
fields: ({ children, isLoading }) => /* @__PURE__ */ jsx5(VersionAwareFieldsPanel2, { isLoading, children })
|
|
1212
|
+
}
|
|
1213
|
+
},
|
|
1214
|
+
`${versionHistory.selectedVersionId ?? "current"}:${reloadNonce}`
|
|
1215
|
+
) }),
|
|
1216
|
+
/* @__PURE__ */ jsx5(PropertiesResizer, {})
|
|
1217
|
+
] })
|
|
892
1218
|
] }),
|
|
893
|
-
/* @__PURE__ */
|
|
894
|
-
|
|
1219
|
+
/* @__PURE__ */ jsx5(
|
|
1220
|
+
UnsavedChangesDialog,
|
|
895
1221
|
{
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
EditorToolbar2,
|
|
914
|
-
{
|
|
915
|
-
saving,
|
|
916
|
-
isDirty: hasUnsavedChanges,
|
|
917
|
-
states: toolbarStates,
|
|
918
|
-
onSave: () => void handleSave(),
|
|
919
|
-
onPublish: () => void handlePublish(activeData),
|
|
920
|
-
onRevert: () => void handleRevert(),
|
|
921
|
-
showPublishButton: true
|
|
922
|
-
}
|
|
923
|
-
),
|
|
924
|
-
components: ({ children }) => /* @__PURE__ */ jsx4(ComponentsPanel2, { children }),
|
|
925
|
-
componentItem: ({ children, name }) => /* @__PURE__ */ jsx4(ComponentItemFilter2, { name, children }),
|
|
926
|
-
fields: ({ children, isLoading }) => /* @__PURE__ */ jsx4(VersionAwareFieldsPanel2, { isLoading, children })
|
|
927
|
-
}
|
|
928
|
-
},
|
|
929
|
-
versionHistory.selectedVersionId ?? "current"
|
|
930
|
-
) }) })
|
|
931
|
-
] })
|
|
1222
|
+
isOpen: pendingNav !== null,
|
|
1223
|
+
onOpenChange: (open) => {
|
|
1224
|
+
if (!open) setPendingNav(null);
|
|
1225
|
+
},
|
|
1226
|
+
onConfirm: () => pendingNav?.()
|
|
1227
|
+
}
|
|
1228
|
+
),
|
|
1229
|
+
/* @__PURE__ */ jsx5(
|
|
1230
|
+
CreateTemplateDialog2,
|
|
1231
|
+
{
|
|
1232
|
+
isOpen: templateDialogOpen,
|
|
1233
|
+
onOpenChange: (open) => setTemplateDialogOpen(open),
|
|
1234
|
+
onConfirm: handleCreateTemplate,
|
|
1235
|
+
saving: templateSaving
|
|
1236
|
+
}
|
|
1237
|
+
)
|
|
1238
|
+
]
|
|
932
1239
|
}
|
|
933
1240
|
);
|
|
934
1241
|
};
|
|
1242
|
+
var ContentPreviewRoute = ({ config, backButton }) => {
|
|
1243
|
+
const { contentKey } = useParams();
|
|
1244
|
+
const history = useHistory();
|
|
1245
|
+
const location = useLocation();
|
|
1246
|
+
const contentName = location.state?.contentName ?? contentKey ?? "Content";
|
|
1247
|
+
return /* @__PURE__ */ jsxs3("div", { children: [
|
|
1248
|
+
/* @__PURE__ */ jsxs3("div", { style: NAV_BAR_STYLE, children: [
|
|
1249
|
+
backButton,
|
|
1250
|
+
backButton && /* @__PURE__ */ jsx5(Text3, { color: "neutral.11", children: "/" }),
|
|
1251
|
+
/* @__PURE__ */ jsxs3(Button2, { variant: "ghost", onPress: () => history.push("/"), children: [
|
|
1252
|
+
/* @__PURE__ */ jsx5(Icon2, { as: ChevronLeft }),
|
|
1253
|
+
" Content Items"
|
|
1254
|
+
] }),
|
|
1255
|
+
/* @__PURE__ */ jsx5(Text3, { color: "neutral.11", children: "/" }),
|
|
1256
|
+
/* @__PURE__ */ jsx5(
|
|
1257
|
+
Button2,
|
|
1258
|
+
{
|
|
1259
|
+
variant: "ghost",
|
|
1260
|
+
onPress: () => history.push(`/${contentKey}`, { contentName }),
|
|
1261
|
+
children: contentName
|
|
1262
|
+
}
|
|
1263
|
+
),
|
|
1264
|
+
/* @__PURE__ */ jsx5(Badge2, { colorPalette: "primary", size: "xs", children: "Preview" }),
|
|
1265
|
+
/* @__PURE__ */ jsx5("div", { style: { marginLeft: "auto" }, children: /* @__PURE__ */ jsxs3(Button2, { variant: "outline", size: "xs", onPress: () => history.goBack(), children: [
|
|
1266
|
+
/* @__PURE__ */ jsx5(Icon2, { as: Close2 }),
|
|
1267
|
+
" Close preview"
|
|
1268
|
+
] }) })
|
|
1269
|
+
] }),
|
|
1270
|
+
/* @__PURE__ */ jsx5(PuckRenderer, { type: "content", contentKey, mode: "preview", config })
|
|
1271
|
+
] });
|
|
1272
|
+
};
|
|
935
1273
|
var ContentManagerRouterInner = ({
|
|
936
1274
|
config,
|
|
937
1275
|
defaultContentType,
|
|
938
1276
|
backButton
|
|
939
1277
|
}) => /* @__PURE__ */ jsxs3(Switch, { children: [
|
|
940
|
-
/* @__PURE__ */
|
|
1278
|
+
/* @__PURE__ */ jsx5(
|
|
941
1279
|
Route,
|
|
942
1280
|
{
|
|
943
1281
|
exact: true,
|
|
944
1282
|
path: "/",
|
|
945
|
-
render: () => /* @__PURE__ */
|
|
1283
|
+
render: () => /* @__PURE__ */ jsx5(ContentListRoute, { defaultContentType, backButton })
|
|
1284
|
+
}
|
|
1285
|
+
),
|
|
1286
|
+
/* @__PURE__ */ jsx5(
|
|
1287
|
+
Route,
|
|
1288
|
+
{
|
|
1289
|
+
path: "/:contentKey/preview",
|
|
1290
|
+
render: () => /* @__PURE__ */ jsx5(ContentPreviewRoute, { config, backButton })
|
|
946
1291
|
}
|
|
947
1292
|
),
|
|
948
|
-
/* @__PURE__ */
|
|
1293
|
+
/* @__PURE__ */ jsx5(
|
|
949
1294
|
Route,
|
|
950
1295
|
{
|
|
951
1296
|
path: "/:contentKey",
|
|
952
|
-
render: () => /* @__PURE__ */
|
|
1297
|
+
render: () => /* @__PURE__ */ jsx5(ContentEditorRoute, { config, backButton })
|
|
953
1298
|
}
|
|
954
1299
|
)
|
|
955
1300
|
] });
|
|
@@ -959,17 +1304,19 @@ var ContentManager = ({
|
|
|
959
1304
|
projectKey,
|
|
960
1305
|
businessUnitKey,
|
|
961
1306
|
jwtToken,
|
|
1307
|
+
locale,
|
|
962
1308
|
config = DEFAULT_CONFIG,
|
|
963
1309
|
defaultContentType,
|
|
964
1310
|
backButton
|
|
965
|
-
}) => /* @__PURE__ */
|
|
1311
|
+
}) => /* @__PURE__ */ jsx5(EnsureNimbusProvider, { locale, children: /* @__PURE__ */ jsx5(EnsureIntlProvider, { children: /* @__PURE__ */ jsx5(
|
|
966
1312
|
PuckApiProvider3,
|
|
967
1313
|
{
|
|
968
1314
|
baseURL,
|
|
969
1315
|
projectKey,
|
|
970
1316
|
businessUnitKey,
|
|
971
1317
|
jwtToken,
|
|
972
|
-
|
|
1318
|
+
locale,
|
|
1319
|
+
children: /* @__PURE__ */ jsx5(BrowserRouter, { basename: parentUrl, children: /* @__PURE__ */ jsx5(
|
|
973
1320
|
ContentManagerRouterInner,
|
|
974
1321
|
{
|
|
975
1322
|
config,
|
|
@@ -978,7 +1325,7 @@ var ContentManager = ({
|
|
|
978
1325
|
}
|
|
979
1326
|
) })
|
|
980
1327
|
}
|
|
981
|
-
) });
|
|
1328
|
+
) }) });
|
|
982
1329
|
export {
|
|
983
1330
|
ContentEditor,
|
|
984
1331
|
ContentManager,
|