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