@commercetools-demo/puck-page-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
@@ -35,47 +25,61 @@ __export(index_exports, {
35
25
  module.exports = __toCommonJS(index_exports);
36
26
 
37
27
  // src/PageManager.tsx
38
- var import_react = require("react");
28
+ var import_react2 = require("react");
39
29
  var import_react_router_dom = require("react-router-dom");
40
30
  var import_puck_api = require("@commercetools-demo/puck-api");
41
31
  var import_puck_editor = require("@commercetools-demo/puck-editor");
42
32
  var import_puck_renderer = require("@commercetools-demo/puck-renderer");
43
- var import_data_table = __toESM(require("@commercetools-uikit/data-table"));
44
- var import_primary_button = __toESM(require("@commercetools-uikit/primary-button"));
45
- var import_secondary_button = __toESM(require("@commercetools-uikit/secondary-button"));
46
- var import_flat_button = __toESM(require("@commercetools-uikit/flat-button"));
47
- var import_card = __toESM(require("@commercetools-uikit/card"));
48
- var import_spacings = __toESM(require("@commercetools-uikit/spacings"));
49
- var import_text = __toESM(require("@commercetools-uikit/text"));
50
- var import_loading_spinner = __toESM(require("@commercetools-uikit/loading-spinner"));
51
- var import_text_input = __toESM(require("@commercetools-uikit/text-input"));
52
- var import_label = __toESM(require("@commercetools-uikit/label"));
53
- var import_icons = require("@commercetools-uikit/icons");
33
+
34
+ // src/EnsureIntlProvider.tsx
35
+ var import_react = require("react");
36
+ var import_react_intl = require("react-intl");
54
37
  var import_jsx_runtime = require("react/jsx-runtime");
55
- var DEFAULT_CONFIG = {
56
- ...import_puck_editor.defaultPuckConfig,
57
- components: { ...import_puck_editor.defaultPuckConfig.components }
58
- };
59
- var StatusBadge = ({ variant }) => {
60
- const styles = variant === "published" ? { background: "var(--color-success-95)", color: "var(--color-success-40)", border: "1px solid var(--color-success-85)" } : variant === "draft" ? { background: "var(--color-warning-95)", color: "var(--color-warning-40)", border: "1px solid var(--color-warning-85)" } : { background: "var(--color-neutral-95)", color: "var(--color-neutral-50)", border: "1px solid var(--color-neutral-85)" };
38
+ var EnsureIntlProvider = ({ children }) => {
39
+ const intl = (0, import_react.useContext)(import_react_intl.IntlContext);
40
+ if (intl) {
41
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
42
+ }
61
43
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
62
- "span",
44
+ import_react_intl.IntlProvider,
63
45
  {
64
- style: {
65
- ...styles,
66
- display: "inline-flex",
67
- alignItems: "center",
68
- padding: "2px 8px",
69
- borderRadius: "var(--border-radius-20)",
70
- fontSize: "var(--font-size-10)",
71
- fontWeight: "var(--font-weight-600)",
72
- marginRight: "4px",
73
- whiteSpace: "nowrap"
46
+ locale: "en",
47
+ defaultLocale: "en",
48
+ messages: {},
49
+ onError: (err) => {
50
+ if (err.code === import_react_intl.ReactIntlErrorCode.MISSING_TRANSLATION) return;
51
+ console.error(err);
74
52
  },
75
- children: variant === "published" ? "Published" : variant === "draft" ? "Draft" : "No state"
53
+ children
76
54
  }
77
55
  );
78
56
  };
57
+
58
+ // src/EnsureNimbusProvider.tsx
59
+ var import_nimbus = require("@commercetools/nimbus");
60
+ var import_jsx_runtime2 = require("react/jsx-runtime");
61
+ var EnsureNimbusProvider = ({
62
+ locale = "en",
63
+ children
64
+ }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_nimbus.NimbusProvider, { locale, children });
65
+
66
+ // src/PageManager.tsx
67
+ var import_nimbus2 = require("@commercetools/nimbus");
68
+ var import_nimbus_icons = require("@commercetools/nimbus-icons");
69
+ var import_jsx_runtime3 = require("react/jsx-runtime");
70
+ var DEFAULT_CONFIG = {
71
+ ...import_puck_editor.defaultPuckConfig,
72
+ components: { ...import_puck_editor.defaultPuckConfig.components }
73
+ };
74
+ var STATUS_BADGE = {
75
+ draft: { colorPalette: "warning", label: "Draft" },
76
+ published: { colorPalette: "positive", label: "Published" },
77
+ none: { colorPalette: "neutral", label: "No state" }
78
+ };
79
+ var StatusBadge = ({ variant }) => {
80
+ const meta = STATUS_BADGE[variant];
81
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Badge, { colorPalette: meta.colorPalette, size: "xs", children: meta.label });
82
+ };
79
83
  var NAV_BAR_STYLE = {
80
84
  position: "sticky",
81
85
  top: 0,
@@ -84,26 +88,23 @@ var NAV_BAR_STYLE = {
84
88
  gap: "12px",
85
89
  padding: "8px 16px",
86
90
  background: "var(--color-surface, #fff)",
87
- borderBottom: "1px solid var(--color-neutral-90)",
91
+ borderBottom: "1px solid var(--color-neutral-90, #e0e0e0)",
88
92
  zIndex: 200,
89
93
  flexShrink: 0
90
94
  };
91
- var COLUMNS = [
92
- { key: "name", label: "Name" },
93
- { key: "slug", label: "Slug" },
94
- { key: "status", label: "Status" },
95
- { key: "updatedAt", label: "Updated" },
96
- { key: "actions", label: "Actions", shouldIgnoreRowClick: true }
97
- ];
98
95
  var PageList = ({ backButton }) => {
99
96
  const history = (0, import_react_router_dom.useHistory)();
100
97
  const { pages, loading, error, createPage, deletePage, refresh } = (0, import_puck_api.usePuckPages)();
101
- const [creating, setCreating] = (0, import_react.useState)(false);
102
- const [newName, setNewName] = (0, import_react.useState)("");
103
- const [newSlug, setNewSlug] = (0, import_react.useState)("");
104
- const [formError, setFormError] = (0, import_react.useState)("");
105
- const [submitting, setSubmitting] = (0, import_react.useState)(false);
106
- const [deleting, setDeleting] = (0, import_react.useState)(null);
98
+ const { templates } = (0, import_puck_api.usePuckTemplates)("page");
99
+ const [creating, setCreating] = (0, import_react2.useState)(false);
100
+ const [newName, setNewName] = (0, import_react2.useState)("");
101
+ const [newSlug, setNewSlug] = (0, import_react2.useState)("");
102
+ const [templateKey, setTemplateKey] = (0, import_react2.useState)("");
103
+ const [formError, setFormError] = (0, import_react2.useState)("");
104
+ const [submitting, setSubmitting] = (0, import_react2.useState)(false);
105
+ const [deleting, setDeleting] = (0, import_react2.useState)(null);
106
+ const [pendingDelete, setPendingDelete] = (0, import_react2.useState)(null);
107
+ const [search, setSearch] = (0, import_react2.useState)("");
107
108
  const handleCreate = async () => {
108
109
  if (!newName.trim()) {
109
110
  setFormError("Name is required");
@@ -120,10 +121,15 @@ var PageList = ({ backButton }) => {
120
121
  name: newName.trim(),
121
122
  slug: newSlug.trim().startsWith("/") ? newSlug.trim() : `/${newSlug.trim()}`
122
123
  };
124
+ if (templateKey) {
125
+ const template = templates.find((t) => t.key === templateKey);
126
+ if (template) input.puckData = template.value.puckData;
127
+ }
123
128
  const created = await createPage(input);
124
129
  setCreating(false);
125
130
  setNewName("");
126
131
  setNewSlug("");
132
+ setTemplateKey("");
127
133
  history.push(`/${created.key}/edit`, { pageName: created.value.name });
128
134
  } catch (err) {
129
135
  setFormError(err.message);
@@ -132,206 +138,323 @@ var PageList = ({ backButton }) => {
132
138
  }
133
139
  };
134
140
  const handleDelete = async (page) => {
135
- if (!confirm(`Delete "${page.value.name}"? This cannot be undone.`)) return;
136
141
  setDeleting(page.key);
137
142
  try {
138
143
  await deletePage(page.key);
139
144
  await refresh();
145
+ setPendingDelete(null);
140
146
  } finally {
141
147
  setDeleting(null);
142
148
  }
143
149
  };
144
150
  if (loading) {
145
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: "64px", display: "flex", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_loading_spinner.default, {}) });
151
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { padding: "64px", display: "flex", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.LoadingSpinner, {}) });
146
152
  }
147
153
  if (error) {
148
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: "32px" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_text.default.Body, { tone: "negative", children: [
154
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { padding: "32px" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.Text, { color: "critical.11", children: [
149
155
  "Error: ",
150
156
  error
151
157
  ] }) });
152
158
  }
153
- const rows = pages.map((p) => ({ ...p, id: p.key }));
154
- 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: [
155
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_spacings.default.Inline, { justifyContent: "space-between", alignItems: "center", children: [
156
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_spacings.default.Inline, { scale: "m", alignItems: "center", children: [
157
- backButton,
158
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Headline, { as: "h1", children: "Puck Pages" })
159
- ] }),
160
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
161
- import_primary_button.default,
159
+ const term = search.trim().toLowerCase();
160
+ const filteredPages = term ? pages.filter(
161
+ (p) => p.value.name.toLowerCase().includes(term) || p.value.slug.toLowerCase().includes(term)
162
+ ) : pages;
163
+ const rows = filteredPages.map((p) => ({ ...p, id: p.key }));
164
+ const columns = [
165
+ {
166
+ id: "name",
167
+ header: "Name",
168
+ accessor: (row) => row.value.name,
169
+ render: ({ row }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Text, { fontWeight: "bold", children: row.value.name })
170
+ },
171
+ {
172
+ id: "slug",
173
+ header: "Slug",
174
+ accessor: (row) => row.value.slug,
175
+ render: ({ row }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
176
+ "code",
162
177
  {
163
- label: "New Page",
164
- iconLeft: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.PlusThinIcon, {}),
165
- onClick: () => setCreating(true)
178
+ style: {
179
+ background: "#f4f4f4",
180
+ padding: "2px 6px",
181
+ borderRadius: "4px",
182
+ fontSize: "11px",
183
+ fontFamily: "monospace"
184
+ },
185
+ children: row.value.slug
166
186
  }
167
187
  )
168
- ] }),
169
- creating && /* @__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: [
170
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Subheadline, { as: "h4", isBold: true, children: "Create New Page" }),
171
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_spacings.default.Inline, { scale: "m", children: [
172
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_spacings.default.Stack, { scale: "xs", children: [
173
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_label.default, { htmlFor: "new-page-name", children: "Name *" }),
174
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
175
- import_text_input.default,
176
- {
177
- id: "new-page-name",
178
- value: newName,
179
- onChange: (e) => setNewName(e.target.value),
180
- placeholder: "Home Page"
181
- }
182
- )
183
- ] }) }),
184
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_spacings.default.Stack, { scale: "xs", children: [
185
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_label.default, { htmlFor: "new-page-slug", children: "Slug *" }),
186
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
187
- import_text_input.default,
188
- {
189
- id: "new-page-slug",
190
- value: newSlug,
191
- onChange: (e) => setNewSlug(e.target.value),
192
- placeholder: "/home"
193
- }
194
- )
195
- ] }) })
196
- ] }),
197
- formError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Body, { tone: "negative", children: formError }),
198
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_spacings.default.Inline, { scale: "s", children: [
199
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
200
- import_primary_button.default,
188
+ },
189
+ {
190
+ id: "status",
191
+ header: "Status",
192
+ accessor: () => "",
193
+ isSortable: false,
194
+ render: ({ row }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.Stack, { direction: "row", gap: "100", wrap: "wrap", children: [
195
+ row.states.draft && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatusBadge, { variant: "draft" }),
196
+ row.states.published && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatusBadge, { variant: "published" }),
197
+ !row.states.draft && !row.states.published && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(StatusBadge, { variant: "none" })
198
+ ] })
199
+ },
200
+ {
201
+ id: "updatedAt",
202
+ header: "Updated",
203
+ accessor: (row) => row.value.updatedAt,
204
+ render: ({ row }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Text, { fontSize: "xs", color: "neutral.11", children: new Date(row.value.updatedAt).toLocaleString() })
205
+ },
206
+ {
207
+ id: "actions",
208
+ header: "Actions",
209
+ accessor: () => "",
210
+ isSortable: false,
211
+ render: ({ row }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.Stack, { direction: "row", gap: "100", alignItems: "center", children: [
212
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
213
+ import_nimbus2.IconButton,
201
214
  {
202
- label: submitting ? "Creating\u2026" : "Create",
203
- onClick: () => void handleCreate(),
204
- isDisabled: submitting
215
+ "aria-label": `Edit ${row.value.name}`,
216
+ variant: "ghost",
217
+ size: "xs",
218
+ onPress: () => history.push(`/${row.key}/edit`, { pageName: row.value.name }),
219
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus_icons.Edit, {})
205
220
  }
206
221
  ),
207
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
208
- import_secondary_button.default,
222
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
223
+ import_nimbus2.IconButton,
209
224
  {
210
- label: "Cancel",
211
- onClick: () => {
212
- setCreating(false);
213
- setFormError("");
214
- }
225
+ "aria-label": `Preview ${row.value.name}`,
226
+ variant: "ghost",
227
+ size: "xs",
228
+ onPress: () => history.push(`/${row.key}/preview`, { pageName: row.value.name }),
229
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus_icons.Visibility, {})
230
+ }
231
+ ),
232
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
233
+ import_nimbus2.IconButton,
234
+ {
235
+ "aria-label": `Delete ${row.value.name}`,
236
+ variant: "ghost",
237
+ colorPalette: "critical",
238
+ size: "xs",
239
+ isDisabled: deleting === row.key,
240
+ onPress: () => setPendingDelete(row),
241
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus_icons.Delete, {})
215
242
  }
216
243
  )
217
244
  ] })
218
- ] }) }),
219
- pages.length === 0 && !creating ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_spacings.default.Stack, { scale: "m", alignItems: "center", children: [
220
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Body, { tone: "secondary", children: "No pages yet." }),
221
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
222
- import_primary_button.default,
223
- {
224
- label: "Create first page",
225
- iconLeft: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.PlusThinIcon, {}),
226
- onClick: () => setCreating(true)
227
- }
228
- )
229
- ] }) : pages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
230
- import_data_table.default,
231
- {
232
- columns: COLUMNS,
233
- rows,
234
- itemRenderer: (row, column) => {
235
- switch (column.key) {
236
- case "name":
237
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_spacings.default.Stack, { scale: "xs", children: [
238
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Body, { isBold: true, children: row.value.name }),
239
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Detail, { tone: "secondary", children: row.key })
240
- ] });
241
- case "slug":
242
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
243
- "code",
244
- {
245
- style: {
246
- background: "var(--color-neutral-95)",
247
- padding: "2px 6px",
248
- borderRadius: "var(--border-radius-4)",
249
- fontSize: "var(--font-size-10)",
250
- fontFamily: "monospace"
251
- },
252
- children: row.value.slug
253
- }
254
- );
255
- case "status":
256
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
257
- row.states.draft && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusBadge, { variant: "draft" }),
258
- row.states.published && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusBadge, { variant: "published" }),
259
- !row.states.draft && !row.states.published && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusBadge, { variant: "none" })
260
- ] });
261
- case "updatedAt":
262
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Body, { tone: "secondary", children: new Date(row.value.updatedAt).toLocaleString() });
263
- case "actions":
264
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
265
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
266
- import_primary_button.default,
267
- {
268
- label: "Edit",
269
- size: "20",
270
- onClick: () => history.push(`/${row.key}/edit`, { pageName: row.value.name })
271
- }
272
- ),
273
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
274
- import_secondary_button.default,
275
- {
276
- label: "Preview",
277
- size: "20",
278
- onClick: () => history.push(`/${row.key}/preview`, { pageName: row.value.name })
279
- }
280
- ),
281
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
282
- import_flat_button.default,
283
- {
284
- tone: "critical",
285
- label: deleting === row.key ? "\u2026" : "Delete",
286
- isDisabled: deleting === row.key,
287
- onClick: () => void handleDelete(row)
288
- }
289
- )
290
- ] });
291
- default:
292
- return null;
245
+ }
246
+ ];
247
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { maxWidth: "1200px", margin: "0 auto", padding: "32px 24px" }, children: [
248
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.Stack, { direction: "column", gap: "600", children: [
249
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.Stack, { direction: "row", justifyContent: "space-between", alignItems: "center", children: [
250
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.Stack, { direction: "row", gap: "400", alignItems: "center", children: [
251
+ backButton,
252
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Text, { as: "h1", fontSize: "2xl", fontWeight: "700", children: "Pages" })
253
+ ] }),
254
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.Button, { variant: "solid", onPress: () => setCreating(true), children: [
255
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Icon, { as: import_nimbus_icons.Add }),
256
+ " New Page"
257
+ ] })
258
+ ] }),
259
+ creating && /* @__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: [
260
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Text, { as: "h4", fontSize: "xl", fontWeight: "700", children: "Create New Page" }),
261
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.Stack, { direction: "row", gap: "400", children: [
262
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.FormField.Root, { isRequired: true, children: [
263
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.FormField.Label, { children: "Name" }),
264
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.FormField.Input, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
265
+ import_nimbus2.TextInput,
266
+ {
267
+ value: newName,
268
+ onChange: (v) => setNewName(v),
269
+ placeholder: "Home Page"
270
+ }
271
+ ) })
272
+ ] }) }),
273
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.FormField.Root, { isRequired: true, children: [
274
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.FormField.Label, { children: "Slug" }),
275
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.FormField.Input, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
276
+ import_nimbus2.TextInput,
277
+ {
278
+ value: newSlug,
279
+ onChange: (v) => setNewSlug(v),
280
+ placeholder: "/home"
281
+ }
282
+ ) })
283
+ ] }) })
284
+ ] }),
285
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.FormField.Root, { children: [
286
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.FormField.Label, { children: "Template" }),
287
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.FormField.Input, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
288
+ import_nimbus2.Select.Root,
289
+ {
290
+ "aria-label": "Template",
291
+ selectedKey: templateKey || "empty",
292
+ onSelectionChange: (key) => setTemplateKey(key == null || key === "empty" ? "" : String(key)),
293
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.Select.Options, { children: [
294
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Select.Option, { id: "empty", children: "Empty" }),
295
+ templates.map((t) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Select.Option, { id: t.key, children: t.value.name }, t.key))
296
+ ] })
297
+ }
298
+ ) })
299
+ ] }),
300
+ formError && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Text, { color: "critical.11", children: formError }),
301
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.Stack, { direction: "row", gap: "200", children: [
302
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Button, { variant: "solid", onPress: () => void handleCreate(), isDisabled: submitting, children: submitting ? "Creating\u2026" : "Create" }),
303
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
304
+ import_nimbus2.Button,
305
+ {
306
+ variant: "outline",
307
+ onPress: () => {
308
+ setCreating(false);
309
+ setFormError("");
310
+ },
311
+ children: "Cancel"
312
+ }
313
+ )
314
+ ] })
315
+ ] }) }) }),
316
+ pages.length === 0 && !creating ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.Stack, { direction: "column", gap: "400", alignItems: "center", children: [
317
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Text, { color: "neutral.11", children: "No pages yet." }),
318
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.Button, { variant: "solid", onPress: () => setCreating(true), children: [
319
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Icon, { as: import_nimbus_icons.Add }),
320
+ " Create first page"
321
+ ] })
322
+ ] }) : pages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.Stack, { direction: "column", gap: "400", children: [
323
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { maxWidth: 360 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
324
+ import_nimbus2.TextInput,
325
+ {
326
+ "aria-label": "Search pages",
327
+ placeholder: "Search by name or path\u2026",
328
+ value: search,
329
+ onChange: (v) => setSearch(v),
330
+ width: "100%",
331
+ trailingElement: search !== "" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
332
+ import_nimbus2.IconButton,
333
+ {
334
+ "aria-label": "Clear search",
335
+ variant: "ghost",
336
+ colorPalette: "neutral",
337
+ size: "2xs",
338
+ onPress: () => setSearch(""),
339
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus_icons.Close, {})
340
+ }
341
+ ) : void 0
293
342
  }
294
- }
343
+ ) }),
344
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "puck-page-list", children: [
345
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("style", { children: `
346
+ .puck-page-list .pin-rows-column-header,
347
+ .puck-page-list [data-slot="pin-row-cell"] { display: none !important; }
348
+ ` }),
349
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.DataTable, { columns, rows, "aria-label": "Pages" })
350
+ ] })
351
+ ] }) : null
352
+ ] }),
353
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
354
+ import_nimbus2.Dialog.Root,
355
+ {
356
+ isOpen: pendingDelete !== null,
357
+ onOpenChange: (open) => {
358
+ if (!open) setPendingDelete(null);
359
+ },
360
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.Dialog.Content, { children: [
361
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.Dialog.Header, { children: [
362
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Dialog.Title, { children: "Delete page?" }),
363
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Dialog.CloseTrigger, {})
364
+ ] }),
365
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Dialog.Body, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.Text, { children: [
366
+ "Are you sure you want to delete",
367
+ " ",
368
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Text, { as: "span", fontWeight: "700", children: pendingDelete?.value.name }),
369
+ "? This cannot be undone."
370
+ ] }) }),
371
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.Dialog.Footer, { children: [
372
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Button, { slot: "close", variant: "outline", isDisabled: deleting !== null, children: "Cancel" }),
373
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
374
+ import_nimbus2.Button,
375
+ {
376
+ colorPalette: "critical",
377
+ isDisabled: deleting !== null,
378
+ onPress: () => {
379
+ if (pendingDelete) void handleDelete(pendingDelete);
380
+ },
381
+ children: deleting !== null ? "Deleting\u2026" : "Delete"
382
+ }
383
+ )
384
+ ] })
385
+ ] })
295
386
  }
296
- ) : null
297
- ] }) });
387
+ )
388
+ ] });
298
389
  };
299
390
  var PageEditorRoute = ({ config, backButton }) => {
300
391
  const { pageKey } = (0, import_react_router_dom.useParams)();
301
392
  const history = (0, import_react_router_dom.useHistory)();
302
393
  const location = (0, import_react_router_dom.useLocation)();
303
- const { baseURL, projectKey, businessUnitKey, jwtToken } = (0, import_puck_api.usePuckApiContext)();
394
+ const { baseURL, projectKey, businessUnitKey, jwtToken, locale } = (0, import_puck_api.usePuckApiContext)();
304
395
  const pageName = location.state?.pageName ?? pageKey ?? "Page";
305
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", flexDirection: "column", height: "100%" }, children: [
306
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: NAV_BAR_STYLE, children: [
396
+ const [isDirty, setIsDirty] = (0, import_react2.useState)(false);
397
+ const [pendingNav, setPendingNav] = (0, import_react2.useState)(null);
398
+ const guardedNavigate = (0, import_react2.useCallback)(
399
+ (navFn) => {
400
+ if (isDirty) {
401
+ setPendingNav(() => navFn);
402
+ } else {
403
+ navFn();
404
+ }
405
+ },
406
+ [isDirty]
407
+ );
408
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", flexDirection: "column", height: "100%" }, children: [
409
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: NAV_BAR_STYLE, children: [
307
410
  backButton,
308
- backButton && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Body, { tone: "secondary", children: "/" }),
309
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
310
- import_flat_button.default,
411
+ backButton && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Text, { color: "neutral.11", children: "/" }),
412
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
413
+ import_nimbus2.Button,
311
414
  {
312
- label: "Pages",
313
- icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.AngleLeftIcon, {}),
314
- iconPosition: "left",
315
- onClick: () => history.push("/")
415
+ variant: "ghost",
416
+ onPress: () => guardedNavigate(() => history.push("/")),
417
+ children: [
418
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Icon, { as: import_nimbus_icons.ChevronLeft }),
419
+ " Pages"
420
+ ]
316
421
  }
317
422
  ),
318
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Body, { tone: "secondary", children: "/" }),
319
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Body, { isBold: true, children: pageName })
423
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Text, { color: "neutral.11", children: "/" }),
424
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Text, { fontWeight: "bold", children: pageName })
320
425
  ] }),
321
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { flex: 1, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
322
- import_puck_editor.PuckEditor,
323
- {
324
- baseURL,
325
- projectKey,
326
- businessUnitKey,
327
- jwtToken: jwtToken ?? "",
328
- pageKey,
329
- config,
330
- onError: (err) => {
331
- console.error("[PageManager] editor error:", err);
426
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "puck-editor-fill", style: { flex: 1, overflow: "hidden" }, children: [
427
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
428
+ import_puck_editor.PuckEditor,
429
+ {
430
+ baseURL,
431
+ projectKey,
432
+ businessUnitKey,
433
+ jwtToken: jwtToken ?? "",
434
+ locale,
435
+ pageKey,
436
+ config,
437
+ onDirtyChange: setIsDirty,
438
+ onPreview: () => guardedNavigate(
439
+ () => history.push(`/${pageKey}/preview`, { pageName })
440
+ ),
441
+ onError: (err) => {
442
+ console.error("[PageManager] editor error:", err);
443
+ }
332
444
  }
445
+ ),
446
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_puck_editor.PropertiesResizer, {})
447
+ ] }),
448
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
449
+ import_puck_editor.UnsavedChangesDialog,
450
+ {
451
+ isOpen: pendingNav !== null,
452
+ onOpenChange: (open) => {
453
+ if (!open) setPendingNav(null);
454
+ },
455
+ onConfirm: () => pendingNav?.()
333
456
  }
334
- ) })
457
+ )
335
458
  ] });
336
459
  };
337
460
  var PagePreviewRoute = ({ config, backButton }) => {
@@ -339,56 +462,39 @@ var PagePreviewRoute = ({ config, backButton }) => {
339
462
  const history = (0, import_react_router_dom.useHistory)();
340
463
  const location = (0, import_react_router_dom.useLocation)();
341
464
  const pageName = location.state?.pageName ?? pageKey ?? "Page";
342
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
343
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: NAV_BAR_STYLE, children: [
465
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
466
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: NAV_BAR_STYLE, children: [
344
467
  backButton,
345
- backButton && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Body, { tone: "secondary", children: "/" }),
346
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
347
- import_flat_button.default,
348
- {
349
- label: "Pages",
350
- icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.AngleLeftIcon, {}),
351
- iconPosition: "left",
352
- onClick: () => history.push("/")
353
- }
354
- ),
355
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Body, { tone: "secondary", children: "/" }),
356
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Body, { isBold: true, children: pageName }),
357
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
358
- "span",
359
- {
360
- style: {
361
- background: "var(--color-primary-95)",
362
- color: "var(--color-primary-25)",
363
- border: "1px solid var(--color-primary-85)",
364
- display: "inline-flex",
365
- alignItems: "center",
366
- padding: "2px 10px",
367
- borderRadius: "var(--border-radius-20)",
368
- fontSize: "var(--font-size-10)",
369
- fontWeight: "var(--font-weight-600)"
370
- },
371
- children: "Preview"
372
- }
373
- )
468
+ backButton && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Text, { color: "neutral.11", children: "/" }),
469
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.Button, { variant: "ghost", onPress: () => history.push("/"), children: [
470
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Icon, { as: import_nimbus_icons.ChevronLeft }),
471
+ " Pages"
472
+ ] }),
473
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Text, { color: "neutral.11", children: "/" }),
474
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Text, { fontWeight: "bold", children: pageName }),
475
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Badge, { colorPalette: "primary", size: "xs", children: "Preview" }),
476
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { marginLeft: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_nimbus2.Button, { variant: "outline", size: "xs", onPress: () => history.goBack(), children: [
477
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_nimbus2.Icon, { as: import_nimbus_icons.Close }),
478
+ " Close preview"
479
+ ] }) })
374
480
  ] }),
375
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_puck_renderer.PuckRenderer, { pageKey, mode: "preview", config })
481
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_puck_renderer.PuckRenderer, { pageKey, mode: "preview", config })
376
482
  ] });
377
483
  };
378
- var PageManagerInner = ({ config, backButton }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_router_dom.Switch, { children: [
379
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_router_dom.Route, { exact: true, path: "/", render: () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PageList, { backButton }) }),
380
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
484
+ var PageManagerInner = ({ config, backButton }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react_router_dom.Switch, { children: [
485
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_router_dom.Route, { exact: true, path: "/", render: () => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PageList, { backButton }) }),
486
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
381
487
  import_react_router_dom.Route,
382
488
  {
383
489
  path: "/:pageKey/edit",
384
- render: () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PageEditorRoute, { config, backButton })
490
+ render: () => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PageEditorRoute, { config, backButton })
385
491
  }
386
492
  ),
387
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
493
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
388
494
  import_react_router_dom.Route,
389
495
  {
390
496
  path: "/:pageKey/preview",
391
- render: () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PagePreviewRoute, { config, backButton })
497
+ render: () => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PagePreviewRoute, { config, backButton })
392
498
  }
393
499
  )
394
500
  ] });
@@ -398,18 +504,20 @@ var PageManager = ({
398
504
  projectKey,
399
505
  businessUnitKey,
400
506
  jwtToken,
507
+ locale,
401
508
  config = DEFAULT_CONFIG,
402
509
  backButton
403
- }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
510
+ }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(EnsureNimbusProvider, { locale, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(EnsureIntlProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
404
511
  import_puck_api.PuckApiProvider,
405
512
  {
406
513
  baseURL,
407
514
  projectKey,
408
515
  businessUnitKey,
409
516
  jwtToken,
410
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_router_dom.BrowserRouter, { basename: parentUrl, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PageManagerInner, { config, backButton }) })
517
+ locale,
518
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_router_dom.BrowserRouter, { basename: parentUrl, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PageManagerInner, { config, backButton }) })
411
519
  }
412
- );
520
+ ) }) });
413
521
  // Annotate the CommonJS export names for ESM import in node:
414
522
  0 && (module.exports = {
415
523
  PageManager