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