@orion-studios/payload-studio 0.6.0-beta.67 → 0.6.0-beta.68

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.
@@ -4177,16 +4177,6 @@ function AdminStudioNewPageView(props) {
4177
4177
  var import_react18 = require("react");
4178
4178
  var import_ui6 = require("@payloadcms/ui");
4179
4179
  var import_jsx_runtime24 = require("react/jsx-runtime");
4180
- var hasAdminAccess = (user) => {
4181
- if (!user || typeof user !== "object") return false;
4182
- const role = user.role;
4183
- return typeof role === "string" && (role === "admin" || role === "developer");
4184
- };
4185
- var isEditor = (user) => {
4186
- if (!user || typeof user !== "object") return false;
4187
- const role = user.role;
4188
- return typeof role === "string" && role === "editor";
4189
- };
4190
4180
  var getPropString4 = (props, key, fallback) => {
4191
4181
  if (!props || typeof props !== "object") return fallback;
4192
4182
  const direct = props[key];
@@ -4213,51 +4203,9 @@ var getPageIDFromPathname = (pathname) => {
4213
4203
  const pagePart = pathname.slice(markerIndex + marker.length).split("/")[0];
4214
4204
  return pagePart ? decodeURIComponent(pagePart) : null;
4215
4205
  };
4216
- function PageEditorBreadcrumbs({
4217
- pageID,
4218
- pagesPath
4219
- }) {
4220
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
4221
- "nav",
4222
- {
4223
- "aria-label": "Breadcrumb",
4224
- style: {
4225
- alignItems: "center",
4226
- color: "var(--orion-admin-muted)",
4227
- display: "flex",
4228
- flexWrap: "wrap",
4229
- fontSize: "0.8rem",
4230
- fontWeight: 800,
4231
- gap: "0.35rem",
4232
- marginBottom: "0.25rem"
4233
- },
4234
- children: [
4235
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4236
- "a",
4237
- {
4238
- href: pagesPath,
4239
- style: {
4240
- color: "var(--orion-admin-accent)",
4241
- textDecoration: "none"
4242
- },
4243
- children: "Pages"
4244
- }
4245
- ),
4246
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { "aria-hidden": "true", children: "/" }),
4247
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { children: pageID ? `Page ${pageID}` : "Page Editor" })
4248
- ]
4249
- }
4250
- );
4251
- }
4252
4206
  function AdminStudioPageEditView(props) {
4253
- const { user } = (0, import_ui6.useAuth)();
4254
4207
  const adminBasePath = useAdminBasePath();
4255
4208
  const iframeRef = (0, import_react18.useRef)(null);
4256
- const [saving, setSaving] = (0, import_react18.useState)(null);
4257
- const [dirty, setDirty] = (0, import_react18.useState)(false);
4258
- const [hasUnpublishedChanges, setHasUnpublishedChanges] = (0, import_react18.useState)(false);
4259
- const [canUndo, setCanUndo] = (0, import_react18.useState)(false);
4260
- const [canRedo, setCanRedo] = (0, import_react18.useState)(false);
4261
4209
  const builderBasePath = getPropString4(props, "builderBasePath", "/builder");
4262
4210
  const pagesPath = resolveAdminPath(adminBasePath, "/pages");
4263
4211
  const pageIDFromParams = (0, import_react18.useMemo)(() => getParam(props.params, "id"), [props.params]);
@@ -4274,96 +4222,6 @@ function AdminStudioPageEditView(props) {
4274
4222
  }
4275
4223
  setDidResolvePathFallback(true);
4276
4224
  }, [pageIDFromParams]);
4277
- const canPublish = hasAdminAccess(user) || isEditor(user);
4278
- const refreshUnpublishedState = async (id) => {
4279
- try {
4280
- const response = await fetch(
4281
- `/api/pages/versions?depth=0&limit=25&sort=-updatedAt&where[parent][equals]=${encodeURIComponent(id)}`,
4282
- {
4283
- credentials: "include"
4284
- }
4285
- );
4286
- if (!response.ok) {
4287
- return;
4288
- }
4289
- const payload = await response.json();
4290
- const docs = Array.isArray(payload.docs) ? payload.docs : [];
4291
- let latestDraft = 0;
4292
- let latestPublished = 0;
4293
- docs.forEach((doc) => {
4294
- const status = doc.version?._status;
4295
- const millis = typeof doc.updatedAt === "string" ? Date.parse(doc.updatedAt) : Number.NaN;
4296
- if (!Number.isFinite(millis)) {
4297
- return;
4298
- }
4299
- if (status === "draft") {
4300
- latestDraft = Math.max(latestDraft, millis);
4301
- }
4302
- if (status === "published") {
4303
- latestPublished = Math.max(latestPublished, millis);
4304
- }
4305
- });
4306
- setHasUnpublishedChanges(latestDraft > 0 && latestDraft >= latestPublished);
4307
- } catch {
4308
- }
4309
- };
4310
- (0, import_react18.useEffect)(() => {
4311
- if (!pageID) {
4312
- return;
4313
- }
4314
- void refreshUnpublishedState(pageID);
4315
- }, [pageID]);
4316
- const requestSave = (status) => {
4317
- const iframe = iframeRef.current;
4318
- if (!iframe?.contentWindow) {
4319
- import_ui6.toast.error("Editor is not ready yet. Please try again.");
4320
- return;
4321
- }
4322
- setSaving(status);
4323
- iframe.contentWindow.postMessage({ source: "payload-visual-builder-parent", type: "save", status }, "*");
4324
- };
4325
- const requestHistoryAction = (type) => {
4326
- const iframe = iframeRef.current;
4327
- if (!iframe?.contentWindow) {
4328
- import_ui6.toast.error("Editor is not ready yet. Please try again.");
4329
- return;
4330
- }
4331
- iframe.contentWindow.postMessage({ source: "payload-visual-builder-parent", type }, "*");
4332
- };
4333
- (0, import_react18.useEffect)(() => {
4334
- const onMessage = (event) => {
4335
- const data = event.data;
4336
- if (!data || data.source !== "payload-visual-builder-child" || typeof data.type !== "string") {
4337
- return;
4338
- }
4339
- if (data.type === "dirty-state") {
4340
- setDirty(Boolean(data.dirty));
4341
- return;
4342
- }
4343
- if (data.type === "history-state") {
4344
- setCanUndo(Boolean(data.canUndo));
4345
- setCanRedo(Boolean(data.canRedo));
4346
- return;
4347
- }
4348
- if (data.type === "save-result") {
4349
- setSaving(null);
4350
- if (data.ok) {
4351
- if (data.status === "draft") {
4352
- setHasUnpublishedChanges(true);
4353
- } else if (data.status === "published") {
4354
- setHasUnpublishedChanges(false);
4355
- } else if (pageID) {
4356
- void refreshUnpublishedState(pageID);
4357
- }
4358
- import_ui6.toast.success(typeof data.message === "string" ? data.message : "Saved.");
4359
- } else {
4360
- import_ui6.toast.error(typeof data.message === "string" ? data.message : "Save failed.");
4361
- }
4362
- }
4363
- };
4364
- window.addEventListener("message", onMessage);
4365
- return () => window.removeEventListener("message", onMessage);
4366
- }, []);
4367
4225
  if (!pageID && !didResolvePathFallback) {
4368
4226
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(StudioSectionLayout, { navProps: props, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
4369
4227
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
@@ -4404,163 +4262,21 @@ function AdminStudioPageEditView(props) {
4404
4262
  ]
4405
4263
  }
4406
4264
  ),
4407
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { display: "grid", gridTemplateRows: "auto 1fr", height: "calc(100vh - 120px)" }, children: [
4408
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
4409
- "div",
4410
- {
4411
- style: {
4412
- alignItems: "center",
4413
- background: "color-mix(in srgb, var(--orion-admin-card-bg) 96%, white)",
4414
- border: "1px solid var(--orion-admin-card-border)",
4415
- borderRadius: 14,
4416
- boxShadow: "0 12px 28px rgba(62, 42, 24, 0.08)",
4417
- colorScheme: "light",
4418
- display: "flex",
4419
- gap: "0.6rem",
4420
- justifyContent: "space-between",
4421
- padding: "0.65rem 0.9rem",
4422
- position: "sticky",
4423
- top: 0,
4424
- zIndex: 20
4425
- },
4426
- children: [
4427
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { minWidth: 0 }, children: [
4428
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(PageEditorBreadcrumbs, { pageID, pagesPath }),
4429
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { color: "var(--orion-admin-text)", fontWeight: 900 }, children: "Page Editor" }),
4430
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
4431
- "div",
4432
- {
4433
- style: {
4434
- color: "var(--orion-admin-muted)",
4435
- fontSize: "0.85rem",
4436
- overflow: "hidden",
4437
- textOverflow: "ellipsis"
4438
- },
4439
- children: [
4440
- "Editing: ",
4441
- pageID
4442
- ]
4443
- }
4444
- )
4445
- ] }),
4446
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { alignItems: "center", display: "flex", gap: "0.5rem" }, children: [
4447
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { color: dirty ? "var(--orion-admin-text)" : "var(--orion-admin-muted)", fontSize: "0.85rem", fontWeight: 700 }, children: dirty ? "Unsaved changes" : "All changes saved" }),
4448
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4449
- "div",
4450
- {
4451
- style: {
4452
- background: hasUnpublishedChanges ? "#fff3cd" : "var(--orion-admin-accent-subtle)",
4453
- border: `1px solid ${hasUnpublishedChanges ? "#f0c36d" : "color-mix(in srgb, var(--orion-admin-accent) 36%, transparent)"}`,
4454
- borderRadius: 999,
4455
- color: hasUnpublishedChanges ? "#6a4a00" : "var(--orion-admin-accent)",
4456
- fontSize: "0.75rem",
4457
- fontWeight: 800,
4458
- padding: "0.2rem 0.55rem",
4459
- whiteSpace: "nowrap"
4460
- },
4461
- title: hasUnpublishedChanges ? "There are saved draft changes not yet published." : "The live page matches the latest published content.",
4462
- children: hasUnpublishedChanges ? "Unpublished draft changes" : "Live is up to date"
4463
- }
4464
- ),
4465
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4466
- "button",
4467
- {
4468
- disabled: !canUndo,
4469
- onClick: () => requestHistoryAction("undo"),
4470
- style: {
4471
- background: "transparent",
4472
- border: "1px solid var(--orion-admin-card-border)",
4473
- borderRadius: 12,
4474
- cursor: canUndo ? "pointer" : "not-allowed",
4475
- color: "var(--orion-admin-text)",
4476
- fontWeight: 800,
4477
- padding: "0.5rem 0.65rem"
4478
- },
4479
- type: "button",
4480
- children: "Undo"
4481
- }
4482
- ),
4483
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4484
- "button",
4485
- {
4486
- disabled: !canRedo,
4487
- onClick: () => requestHistoryAction("redo"),
4488
- style: {
4489
- background: "transparent",
4490
- border: "1px solid var(--orion-admin-card-border)",
4491
- borderRadius: 12,
4492
- cursor: canRedo ? "pointer" : "not-allowed",
4493
- color: "var(--orion-admin-text)",
4494
- fontWeight: 800,
4495
- padding: "0.5rem 0.65rem"
4496
- },
4497
- type: "button",
4498
- children: "Redo"
4499
- }
4500
- ),
4501
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4502
- "button",
4503
- {
4504
- disabled: saving !== null,
4505
- onClick: () => requestSave("draft"),
4506
- style: {
4507
- background: "var(--orion-admin-card-bg)",
4508
- border: "1px solid var(--orion-admin-card-border)",
4509
- borderRadius: 12,
4510
- cursor: saving ? "not-allowed" : "pointer",
4511
- color: "var(--orion-admin-text)",
4512
- fontWeight: 800,
4513
- padding: "0.5rem 0.75rem"
4514
- },
4515
- type: "button",
4516
- children: saving === "draft" ? "Saving\u2026" : "Save Draft"
4517
- }
4518
- ),
4519
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4520
- "button",
4521
- {
4522
- disabled: !canPublish || saving !== null,
4523
- onClick: () => requestSave("published"),
4524
- style: {
4525
- background: canPublish ? "var(--orion-admin-button-bg)" : "var(--orion-admin-card-border)",
4526
- border: "none",
4527
- borderRadius: 12,
4528
- color: canPublish ? "var(--orion-admin-button-text)" : "var(--orion-admin-text)",
4529
- cursor: !canPublish || saving ? "not-allowed" : "pointer",
4530
- fontWeight: 900,
4531
- padding: "0.5rem 0.75rem"
4532
- },
4533
- type: "button",
4534
- title: !canPublish ? "You do not have publish permissions." : void 0,
4535
- children: saving === "published" ? "Publishing\u2026" : "Publish"
4536
- }
4537
- )
4538
- ] })
4539
- ]
4540
- }
4541
- ),
4542
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4543
- "iframe",
4544
- {
4545
- ref: iframeRef,
4546
- src: `${builderBasePath.replace(/\/$/, "")}/${pageID}`,
4547
- style: { border: "none", height: "100%", width: "100%" },
4548
- title: "Page Builder",
4549
- onLoad: () => {
4550
- const iframe = iframeRef.current;
4551
- if (!iframe?.contentWindow) return;
4552
- iframe.contentWindow.postMessage({ source: "payload-visual-builder-parent", type: "dirty-check-request" }, "*");
4553
- iframe.contentWindow.postMessage({ source: "payload-visual-builder-parent", type: "history-check-request" }, "*");
4554
- }
4555
- }
4556
- )
4557
- ] })
4265
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { height: "calc(100vh - 80px)" }, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4266
+ "iframe",
4267
+ {
4268
+ ref: iframeRef,
4269
+ src: `${builderBasePath.replace(/\/$/, "")}/${pageID}`,
4270
+ style: { border: "none", height: "100%", width: "100%" },
4271
+ title: "Page Builder"
4272
+ }
4273
+ ) })
4558
4274
  ] }) });
4559
4275
  }
4560
4276
 
4561
4277
  // src/admin/components/studio/AdminStudioPagesListView.tsx
4562
4278
  var import_jsx_runtime25 = require("react/jsx-runtime");
4563
- var hasAdminAccess2 = (user) => {
4279
+ var hasAdminAccess = (user) => {
4564
4280
  if (!user || typeof user !== "object") return false;
4565
4281
  const role = user.role;
4566
4282
  return typeof role === "string" && (role === "admin" || role === "developer");
@@ -4641,7 +4357,7 @@ function AdminStudioPagesIndexView({
4641
4357
  return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(StudioSectionLayout, { navProps: props, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
4642
4358
  AdminPage,
4643
4359
  {
4644
- actions: hasAdminAccess2(user) ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_link2.default, { className: "orion-admin-action-button", href: newPagePath, children: "New Page" }) : null,
4360
+ actions: hasAdminAccess(user) ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_link2.default, { className: "orion-admin-action-button", href: newPagePath, children: "New Page" }) : null,
4645
4361
  breadcrumbs: [
4646
4362
  { label: "Dashboard", href: adminBasePath },
4647
4363
  { label: "Pages" }
@@ -7606,17 +7322,17 @@ var FORM_TONE_OVERRIDES = {
7606
7322
  var IDENTITY_KEYS = /* @__PURE__ */ new Set(["contactEmail", "email", "firstName", "lastName", "name"]);
7607
7323
  var RESPONSE_FIELD_PREVIEW_LIMIT = 3;
7608
7324
  var RESPONSE_SCROLL_THRESHOLD = 3;
7609
- var hasAdminAccess3 = (user) => {
7325
+ var hasAdminAccess2 = (user) => {
7610
7326
  if (!user || typeof user !== "object") return false;
7611
7327
  const role = user.role;
7612
7328
  return typeof role === "string" && (role === "admin" || role === "developer");
7613
7329
  };
7614
- var isEditor2 = (user) => {
7330
+ var isEditor = (user) => {
7615
7331
  if (!user || typeof user !== "object") return false;
7616
7332
  const role = user.role;
7617
7333
  return typeof role === "string" && role === "editor";
7618
7334
  };
7619
- var canReviewForms2 = (user) => hasAdminAccess3(user) || isEditor2(user);
7335
+ var canReviewForms2 = (user) => hasAdminAccess2(user) || isEditor(user);
7620
7336
  var getPropString13 = (props, key, fallback) => {
7621
7337
  if (!props || typeof props !== "object") return fallback;
7622
7338
  const direct = props[key];
@@ -9722,7 +9438,7 @@ var import_react35 = require("react");
9722
9438
  var import_ui12 = require("@payloadcms/ui");
9723
9439
  var import_jsx_runtime44 = require("react/jsx-runtime");
9724
9440
  var userRoles = ["admin", "developer", "editor", "client"];
9725
- var hasAdminAccess4 = (user) => {
9441
+ var hasAdminAccess3 = (user) => {
9726
9442
  if (!user || typeof user !== "object") return false;
9727
9443
  const role = user.role;
9728
9444
  return typeof role === "string" && (role === "admin" || role === "developer");
@@ -9737,7 +9453,7 @@ function AdminStudioToolsView(props) {
9737
9453
  const [savedMessage, setSavedMessage] = (0, import_react35.useState)(null);
9738
9454
  const [createSubmitting, setCreateSubmitting] = (0, import_react35.useState)(false);
9739
9455
  const [updatingUserID, setUpdatingUserID] = (0, import_react35.useState)(null);
9740
- if (!hasAdminAccess4(user)) {
9456
+ if (!hasAdminAccess3(user)) {
9741
9457
  return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(StudioSectionLayout, { navProps: props, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
9742
9458
  AdminPage,
9743
9459
  {
@@ -2670,7 +2670,7 @@ function AdminStudioDashboard(rawProps) {
2670
2670
  import { useEffect as useEffect9, useMemo as useMemo5, useState as useState10 } from "react";
2671
2671
  import Link2 from "next/link";
2672
2672
  import { usePathname as usePathname3 } from "next/navigation";
2673
- import { useAuth as useAuth5 } from "@payloadcms/ui";
2673
+ import { useAuth as useAuth4 } from "@payloadcms/ui";
2674
2674
 
2675
2675
  // src/admin/components/studio/AdminStudioNewPageView.tsx
2676
2676
  import { useState as useState8 } from "react";
@@ -2795,18 +2795,8 @@ function AdminStudioNewPageView(props) {
2795
2795
 
2796
2796
  // src/admin/components/studio/AdminStudioPageEditView.tsx
2797
2797
  import { useEffect as useEffect8, useMemo as useMemo4, useRef as useRef3, useState as useState9 } from "react";
2798
- import { SetStepNav, toast, useAuth as useAuth4 } from "@payloadcms/ui";
2798
+ import { SetStepNav } from "@payloadcms/ui";
2799
2799
  import { Fragment as Fragment4, jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
2800
- var hasAdminAccess = (user) => {
2801
- if (!user || typeof user !== "object") return false;
2802
- const role = user.role;
2803
- return typeof role === "string" && (role === "admin" || role === "developer");
2804
- };
2805
- var isEditor = (user) => {
2806
- if (!user || typeof user !== "object") return false;
2807
- const role = user.role;
2808
- return typeof role === "string" && role === "editor";
2809
- };
2810
2800
  var getPropString4 = (props, key, fallback) => {
2811
2801
  if (!props || typeof props !== "object") return fallback;
2812
2802
  const direct = props[key];
@@ -2833,51 +2823,9 @@ var getPageIDFromPathname = (pathname) => {
2833
2823
  const pagePart = pathname.slice(markerIndex + marker.length).split("/")[0];
2834
2824
  return pagePart ? decodeURIComponent(pagePart) : null;
2835
2825
  };
2836
- function PageEditorBreadcrumbs({
2837
- pageID,
2838
- pagesPath
2839
- }) {
2840
- return /* @__PURE__ */ jsxs16(
2841
- "nav",
2842
- {
2843
- "aria-label": "Breadcrumb",
2844
- style: {
2845
- alignItems: "center",
2846
- color: "var(--orion-admin-muted)",
2847
- display: "flex",
2848
- flexWrap: "wrap",
2849
- fontSize: "0.8rem",
2850
- fontWeight: 800,
2851
- gap: "0.35rem",
2852
- marginBottom: "0.25rem"
2853
- },
2854
- children: [
2855
- /* @__PURE__ */ jsx19(
2856
- "a",
2857
- {
2858
- href: pagesPath,
2859
- style: {
2860
- color: "var(--orion-admin-accent)",
2861
- textDecoration: "none"
2862
- },
2863
- children: "Pages"
2864
- }
2865
- ),
2866
- /* @__PURE__ */ jsx19("span", { "aria-hidden": "true", children: "/" }),
2867
- /* @__PURE__ */ jsx19("span", { children: pageID ? `Page ${pageID}` : "Page Editor" })
2868
- ]
2869
- }
2870
- );
2871
- }
2872
2826
  function AdminStudioPageEditView(props) {
2873
- const { user } = useAuth4();
2874
2827
  const adminBasePath = useAdminBasePath();
2875
2828
  const iframeRef = useRef3(null);
2876
- const [saving, setSaving] = useState9(null);
2877
- const [dirty, setDirty] = useState9(false);
2878
- const [hasUnpublishedChanges, setHasUnpublishedChanges] = useState9(false);
2879
- const [canUndo, setCanUndo] = useState9(false);
2880
- const [canRedo, setCanRedo] = useState9(false);
2881
2829
  const builderBasePath = getPropString4(props, "builderBasePath", "/builder");
2882
2830
  const pagesPath = resolveAdminPath(adminBasePath, "/pages");
2883
2831
  const pageIDFromParams = useMemo4(() => getParam(props.params, "id"), [props.params]);
@@ -2894,96 +2842,6 @@ function AdminStudioPageEditView(props) {
2894
2842
  }
2895
2843
  setDidResolvePathFallback(true);
2896
2844
  }, [pageIDFromParams]);
2897
- const canPublish = hasAdminAccess(user) || isEditor(user);
2898
- const refreshUnpublishedState = async (id) => {
2899
- try {
2900
- const response = await fetch(
2901
- `/api/pages/versions?depth=0&limit=25&sort=-updatedAt&where[parent][equals]=${encodeURIComponent(id)}`,
2902
- {
2903
- credentials: "include"
2904
- }
2905
- );
2906
- if (!response.ok) {
2907
- return;
2908
- }
2909
- const payload = await response.json();
2910
- const docs = Array.isArray(payload.docs) ? payload.docs : [];
2911
- let latestDraft = 0;
2912
- let latestPublished = 0;
2913
- docs.forEach((doc) => {
2914
- const status = doc.version?._status;
2915
- const millis = typeof doc.updatedAt === "string" ? Date.parse(doc.updatedAt) : Number.NaN;
2916
- if (!Number.isFinite(millis)) {
2917
- return;
2918
- }
2919
- if (status === "draft") {
2920
- latestDraft = Math.max(latestDraft, millis);
2921
- }
2922
- if (status === "published") {
2923
- latestPublished = Math.max(latestPublished, millis);
2924
- }
2925
- });
2926
- setHasUnpublishedChanges(latestDraft > 0 && latestDraft >= latestPublished);
2927
- } catch {
2928
- }
2929
- };
2930
- useEffect8(() => {
2931
- if (!pageID) {
2932
- return;
2933
- }
2934
- void refreshUnpublishedState(pageID);
2935
- }, [pageID]);
2936
- const requestSave = (status) => {
2937
- const iframe = iframeRef.current;
2938
- if (!iframe?.contentWindow) {
2939
- toast.error("Editor is not ready yet. Please try again.");
2940
- return;
2941
- }
2942
- setSaving(status);
2943
- iframe.contentWindow.postMessage({ source: "payload-visual-builder-parent", type: "save", status }, "*");
2944
- };
2945
- const requestHistoryAction = (type) => {
2946
- const iframe = iframeRef.current;
2947
- if (!iframe?.contentWindow) {
2948
- toast.error("Editor is not ready yet. Please try again.");
2949
- return;
2950
- }
2951
- iframe.contentWindow.postMessage({ source: "payload-visual-builder-parent", type }, "*");
2952
- };
2953
- useEffect8(() => {
2954
- const onMessage = (event) => {
2955
- const data = event.data;
2956
- if (!data || data.source !== "payload-visual-builder-child" || typeof data.type !== "string") {
2957
- return;
2958
- }
2959
- if (data.type === "dirty-state") {
2960
- setDirty(Boolean(data.dirty));
2961
- return;
2962
- }
2963
- if (data.type === "history-state") {
2964
- setCanUndo(Boolean(data.canUndo));
2965
- setCanRedo(Boolean(data.canRedo));
2966
- return;
2967
- }
2968
- if (data.type === "save-result") {
2969
- setSaving(null);
2970
- if (data.ok) {
2971
- if (data.status === "draft") {
2972
- setHasUnpublishedChanges(true);
2973
- } else if (data.status === "published") {
2974
- setHasUnpublishedChanges(false);
2975
- } else if (pageID) {
2976
- void refreshUnpublishedState(pageID);
2977
- }
2978
- toast.success(typeof data.message === "string" ? data.message : "Saved.");
2979
- } else {
2980
- toast.error(typeof data.message === "string" ? data.message : "Save failed.");
2981
- }
2982
- }
2983
- };
2984
- window.addEventListener("message", onMessage);
2985
- return () => window.removeEventListener("message", onMessage);
2986
- }, []);
2987
2845
  if (!pageID && !didResolvePathFallback) {
2988
2846
  return /* @__PURE__ */ jsx19(StudioSectionLayout, { navProps: props, children: /* @__PURE__ */ jsxs16(Fragment4, { children: [
2989
2847
  /* @__PURE__ */ jsx19(
@@ -3024,163 +2882,21 @@ function AdminStudioPageEditView(props) {
3024
2882
  ]
3025
2883
  }
3026
2884
  ),
3027
- /* @__PURE__ */ jsxs16("div", { style: { display: "grid", gridTemplateRows: "auto 1fr", height: "calc(100vh - 120px)" }, children: [
3028
- /* @__PURE__ */ jsxs16(
3029
- "div",
3030
- {
3031
- style: {
3032
- alignItems: "center",
3033
- background: "color-mix(in srgb, var(--orion-admin-card-bg) 96%, white)",
3034
- border: "1px solid var(--orion-admin-card-border)",
3035
- borderRadius: 14,
3036
- boxShadow: "0 12px 28px rgba(62, 42, 24, 0.08)",
3037
- colorScheme: "light",
3038
- display: "flex",
3039
- gap: "0.6rem",
3040
- justifyContent: "space-between",
3041
- padding: "0.65rem 0.9rem",
3042
- position: "sticky",
3043
- top: 0,
3044
- zIndex: 20
3045
- },
3046
- children: [
3047
- /* @__PURE__ */ jsxs16("div", { style: { minWidth: 0 }, children: [
3048
- /* @__PURE__ */ jsx19(PageEditorBreadcrumbs, { pageID, pagesPath }),
3049
- /* @__PURE__ */ jsx19("div", { style: { color: "var(--orion-admin-text)", fontWeight: 900 }, children: "Page Editor" }),
3050
- /* @__PURE__ */ jsxs16(
3051
- "div",
3052
- {
3053
- style: {
3054
- color: "var(--orion-admin-muted)",
3055
- fontSize: "0.85rem",
3056
- overflow: "hidden",
3057
- textOverflow: "ellipsis"
3058
- },
3059
- children: [
3060
- "Editing: ",
3061
- pageID
3062
- ]
3063
- }
3064
- )
3065
- ] }),
3066
- /* @__PURE__ */ jsxs16("div", { style: { alignItems: "center", display: "flex", gap: "0.5rem" }, children: [
3067
- /* @__PURE__ */ jsx19("div", { style: { color: dirty ? "var(--orion-admin-text)" : "var(--orion-admin-muted)", fontSize: "0.85rem", fontWeight: 700 }, children: dirty ? "Unsaved changes" : "All changes saved" }),
3068
- /* @__PURE__ */ jsx19(
3069
- "div",
3070
- {
3071
- style: {
3072
- background: hasUnpublishedChanges ? "#fff3cd" : "var(--orion-admin-accent-subtle)",
3073
- border: `1px solid ${hasUnpublishedChanges ? "#f0c36d" : "color-mix(in srgb, var(--orion-admin-accent) 36%, transparent)"}`,
3074
- borderRadius: 999,
3075
- color: hasUnpublishedChanges ? "#6a4a00" : "var(--orion-admin-accent)",
3076
- fontSize: "0.75rem",
3077
- fontWeight: 800,
3078
- padding: "0.2rem 0.55rem",
3079
- whiteSpace: "nowrap"
3080
- },
3081
- title: hasUnpublishedChanges ? "There are saved draft changes not yet published." : "The live page matches the latest published content.",
3082
- children: hasUnpublishedChanges ? "Unpublished draft changes" : "Live is up to date"
3083
- }
3084
- ),
3085
- /* @__PURE__ */ jsx19(
3086
- "button",
3087
- {
3088
- disabled: !canUndo,
3089
- onClick: () => requestHistoryAction("undo"),
3090
- style: {
3091
- background: "transparent",
3092
- border: "1px solid var(--orion-admin-card-border)",
3093
- borderRadius: 12,
3094
- cursor: canUndo ? "pointer" : "not-allowed",
3095
- color: "var(--orion-admin-text)",
3096
- fontWeight: 800,
3097
- padding: "0.5rem 0.65rem"
3098
- },
3099
- type: "button",
3100
- children: "Undo"
3101
- }
3102
- ),
3103
- /* @__PURE__ */ jsx19(
3104
- "button",
3105
- {
3106
- disabled: !canRedo,
3107
- onClick: () => requestHistoryAction("redo"),
3108
- style: {
3109
- background: "transparent",
3110
- border: "1px solid var(--orion-admin-card-border)",
3111
- borderRadius: 12,
3112
- cursor: canRedo ? "pointer" : "not-allowed",
3113
- color: "var(--orion-admin-text)",
3114
- fontWeight: 800,
3115
- padding: "0.5rem 0.65rem"
3116
- },
3117
- type: "button",
3118
- children: "Redo"
3119
- }
3120
- ),
3121
- /* @__PURE__ */ jsx19(
3122
- "button",
3123
- {
3124
- disabled: saving !== null,
3125
- onClick: () => requestSave("draft"),
3126
- style: {
3127
- background: "var(--orion-admin-card-bg)",
3128
- border: "1px solid var(--orion-admin-card-border)",
3129
- borderRadius: 12,
3130
- cursor: saving ? "not-allowed" : "pointer",
3131
- color: "var(--orion-admin-text)",
3132
- fontWeight: 800,
3133
- padding: "0.5rem 0.75rem"
3134
- },
3135
- type: "button",
3136
- children: saving === "draft" ? "Saving\u2026" : "Save Draft"
3137
- }
3138
- ),
3139
- /* @__PURE__ */ jsx19(
3140
- "button",
3141
- {
3142
- disabled: !canPublish || saving !== null,
3143
- onClick: () => requestSave("published"),
3144
- style: {
3145
- background: canPublish ? "var(--orion-admin-button-bg)" : "var(--orion-admin-card-border)",
3146
- border: "none",
3147
- borderRadius: 12,
3148
- color: canPublish ? "var(--orion-admin-button-text)" : "var(--orion-admin-text)",
3149
- cursor: !canPublish || saving ? "not-allowed" : "pointer",
3150
- fontWeight: 900,
3151
- padding: "0.5rem 0.75rem"
3152
- },
3153
- type: "button",
3154
- title: !canPublish ? "You do not have publish permissions." : void 0,
3155
- children: saving === "published" ? "Publishing\u2026" : "Publish"
3156
- }
3157
- )
3158
- ] })
3159
- ]
3160
- }
3161
- ),
3162
- /* @__PURE__ */ jsx19(
3163
- "iframe",
3164
- {
3165
- ref: iframeRef,
3166
- src: `${builderBasePath.replace(/\/$/, "")}/${pageID}`,
3167
- style: { border: "none", height: "100%", width: "100%" },
3168
- title: "Page Builder",
3169
- onLoad: () => {
3170
- const iframe = iframeRef.current;
3171
- if (!iframe?.contentWindow) return;
3172
- iframe.contentWindow.postMessage({ source: "payload-visual-builder-parent", type: "dirty-check-request" }, "*");
3173
- iframe.contentWindow.postMessage({ source: "payload-visual-builder-parent", type: "history-check-request" }, "*");
3174
- }
3175
- }
3176
- )
3177
- ] })
2885
+ /* @__PURE__ */ jsx19("div", { style: { height: "calc(100vh - 80px)" }, children: /* @__PURE__ */ jsx19(
2886
+ "iframe",
2887
+ {
2888
+ ref: iframeRef,
2889
+ src: `${builderBasePath.replace(/\/$/, "")}/${pageID}`,
2890
+ style: { border: "none", height: "100%", width: "100%" },
2891
+ title: "Page Builder"
2892
+ }
2893
+ ) })
3178
2894
  ] }) });
3179
2895
  }
3180
2896
 
3181
2897
  // src/admin/components/studio/AdminStudioPagesListView.tsx
3182
2898
  import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
3183
- var hasAdminAccess2 = (user) => {
2899
+ var hasAdminAccess = (user) => {
3184
2900
  if (!user || typeof user !== "object") return false;
3185
2901
  const role = user.role;
3186
2902
  return typeof role === "string" && (role === "admin" || role === "developer");
@@ -3213,7 +2929,7 @@ function AdminStudioPagesIndexView({
3213
2929
  adminBasePath,
3214
2930
  ...props
3215
2931
  }) {
3216
- const { user } = useAuth5();
2932
+ const { user } = useAuth4();
3217
2933
  const pagesCollectionSlug = getPropString5(props, "pagesCollectionSlug", "pages");
3218
2934
  const newPagePath = resolveAdminPath(adminBasePath, "/pages/new");
3219
2935
  const [loading, setLoading] = useState10(true);
@@ -3261,7 +2977,7 @@ function AdminStudioPagesIndexView({
3261
2977
  return /* @__PURE__ */ jsx20(StudioSectionLayout, { navProps: props, children: /* @__PURE__ */ jsxs17(
3262
2978
  AdminPage,
3263
2979
  {
3264
- actions: hasAdminAccess2(user) ? /* @__PURE__ */ jsx20(Link2, { className: "orion-admin-action-button", href: newPagePath, children: "New Page" }) : null,
2980
+ actions: hasAdminAccess(user) ? /* @__PURE__ */ jsx20(Link2, { className: "orion-admin-action-button", href: newPagePath, children: "New Page" }) : null,
3265
2981
  breadcrumbs: [
3266
2982
  { label: "Dashboard", href: adminBasePath },
3267
2983
  { label: "Pages" }
@@ -5227,7 +4943,7 @@ function AdminStudioMediaItemView(props) {
5227
4943
  // src/admin/components/studio/AdminStudioFormsView.tsx
5228
4944
  import Link3 from "next/link";
5229
4945
  import { useEffect as useEffect17, useMemo as useMemo13, useState as useState18 } from "react";
5230
- import { useAuth as useAuth6 } from "@payloadcms/ui";
4946
+ import { useAuth as useAuth5 } from "@payloadcms/ui";
5231
4947
  import { jsx as jsx29, jsxs as jsxs26 } from "react/jsx-runtime";
5232
4948
  var FORM_TONES = [
5233
4949
  {
@@ -5269,17 +4985,17 @@ var FORM_TONE_OVERRIDES = {
5269
4985
  var IDENTITY_KEYS = /* @__PURE__ */ new Set(["contactEmail", "email", "firstName", "lastName", "name"]);
5270
4986
  var RESPONSE_FIELD_PREVIEW_LIMIT = 3;
5271
4987
  var RESPONSE_SCROLL_THRESHOLD = 3;
5272
- var hasAdminAccess3 = (user) => {
4988
+ var hasAdminAccess2 = (user) => {
5273
4989
  if (!user || typeof user !== "object") return false;
5274
4990
  const role = user.role;
5275
4991
  return typeof role === "string" && (role === "admin" || role === "developer");
5276
4992
  };
5277
- var isEditor2 = (user) => {
4993
+ var isEditor = (user) => {
5278
4994
  if (!user || typeof user !== "object") return false;
5279
4995
  const role = user.role;
5280
4996
  return typeof role === "string" && role === "editor";
5281
4997
  };
5282
- var canReviewForms2 = (user) => hasAdminAccess3(user) || isEditor2(user);
4998
+ var canReviewForms2 = (user) => hasAdminAccess2(user) || isEditor(user);
5283
4999
  var getPropString13 = (props, key, fallback) => {
5284
5000
  if (!props || typeof props !== "object") return fallback;
5285
5001
  const direct = props[key];
@@ -5445,7 +5161,7 @@ var renderEmptyMessage = (message) => /* @__PURE__ */ jsxs26("div", { className:
5445
5161
  /* @__PURE__ */ jsx29("span", { children: message })
5446
5162
  ] });
5447
5163
  function AdminStudioFormsView(props) {
5448
- const { user } = useAuth6();
5164
+ const { user } = useAuth5();
5449
5165
  const formsCollectionSlug = getPropString13(props, "formsCollectionSlug", "forms");
5450
5166
  const formSubmissionsCollectionSlug = getPropString13(
5451
5167
  props,
@@ -7382,17 +7098,17 @@ function AdminStudioFormUploadView(props) {
7382
7098
 
7383
7099
  // src/admin/components/studio/AdminStudioToolsView.tsx
7384
7100
  import { useEffect as useEffect21, useState as useState22 } from "react";
7385
- import { useAuth as useAuth7 } from "@payloadcms/ui";
7101
+ import { useAuth as useAuth6 } from "@payloadcms/ui";
7386
7102
  import { jsx as jsx33, jsxs as jsxs30 } from "react/jsx-runtime";
7387
7103
  var userRoles = ["admin", "developer", "editor", "client"];
7388
- var hasAdminAccess4 = (user) => {
7104
+ var hasAdminAccess3 = (user) => {
7389
7105
  if (!user || typeof user !== "object") return false;
7390
7106
  const role = user.role;
7391
7107
  return typeof role === "string" && (role === "admin" || role === "developer");
7392
7108
  };
7393
7109
  var normalizeRole = (value) => userRoles.includes(value) ? value : "editor";
7394
7110
  function AdminStudioToolsView(props) {
7395
- const { user } = useAuth7();
7111
+ const { user } = useAuth6();
7396
7112
  const adminBasePath = useAdminBasePath();
7397
7113
  const [docs, setDocs] = useState22([]);
7398
7114
  const [loading, setLoading] = useState22(true);
@@ -7400,7 +7116,7 @@ function AdminStudioToolsView(props) {
7400
7116
  const [savedMessage, setSavedMessage] = useState22(null);
7401
7117
  const [createSubmitting, setCreateSubmitting] = useState22(false);
7402
7118
  const [updatingUserID, setUpdatingUserID] = useState22(null);
7403
- if (!hasAdminAccess4(user)) {
7119
+ if (!hasAdminAccess3(user)) {
7404
7120
  return /* @__PURE__ */ jsx33(StudioSectionLayout, { navProps: props, children: /* @__PURE__ */ jsx33(
7405
7121
  AdminPage,
7406
7122
  {
@@ -1176,8 +1176,10 @@ function GrapesPageEditor({
1176
1176
  const selectedComponentRef = (0, import_react.useRef)(null);
1177
1177
  const [error, setError] = (0, import_react.useState)("");
1178
1178
  const [historyState, setHistoryState] = (0, import_react.useState)({ canRedo: false, canUndo: false });
1179
+ const [isDirty, setIsDirty] = (0, import_react.useState)(false);
1179
1180
  const [lastSavedAt, setLastSavedAt] = (0, import_react.useState)("");
1180
1181
  const [loading, setLoading] = (0, import_react.useState)(true);
1182
+ const [publicationState, setPublicationState] = (0, import_react.useState)("live");
1181
1183
  const [selectedDevice, setSelectedDevice] = (0, import_react.useState)("desktop");
1182
1184
  const [saving, setSaving] = (0, import_react.useState)(null);
1183
1185
  const [saveMessage, setSaveMessage] = (0, import_react.useState)("");
@@ -1326,7 +1328,9 @@ function GrapesPageEditor({
1326
1328
  editor.loadProjectData(projectData);
1327
1329
  void loadPayloadMediaAssets(editor);
1328
1330
  editor.on("update", () => {
1329
- postToParent({ dirty: editor.getDirtyCount() > 0, type: "dirty-state" });
1331
+ const hasDirtyChanges = editor.getDirtyCount() > 0;
1332
+ setIsDirty(hasDirtyChanges);
1333
+ postToParent({ dirty: hasDirtyChanges, type: "dirty-state" });
1330
1334
  updateHistoryState(editor);
1331
1335
  runValidation(editor);
1332
1336
  setSaveMessage("Unsaved changes");
@@ -1343,7 +1347,6 @@ function GrapesPageEditor({
1343
1347
  const typed = component;
1344
1348
  selectedComponentRef.current = typed;
1345
1349
  setSelectionSummary(summarizeSelectedComponent(typed));
1346
- setSaveMessage("Selection ready");
1347
1350
  });
1348
1351
  setSelectedDevice(editor.getDevice() || "desktop");
1349
1352
  runValidation(editor);
@@ -1425,6 +1428,8 @@ function GrapesPageEditor({
1425
1428
  throw new Error(await parsePayloadErrorMessage(response, "Could not save this page."));
1426
1429
  }
1427
1430
  editor.clearDirtyCount();
1431
+ setIsDirty(false);
1432
+ setPublicationState(status === "published" ? "live" : "draft");
1428
1433
  postToParent({
1429
1434
  dirty: false,
1430
1435
  type: "dirty-state"
@@ -1484,6 +1489,9 @@ function GrapesPageEditor({
1484
1489
  window.addEventListener("message", onMessage);
1485
1490
  return () => window.removeEventListener("message", onMessage);
1486
1491
  }, [saving]);
1492
+ const statusLabel = saving ? saving === "published" ? "Publishing..." : "Saving draft..." : isDirty ? "Unsaved changes" : saveMessage && saveMessage !== "Unsaved changes" ? saveMessage : "All changes saved";
1493
+ const statusMeta = lastSavedAt ? `Last saved ${lastSavedAt}` : "Ready";
1494
+ const liveStatusLabel = isDirty ? "Save draft to update" : publicationState === "draft" ? "Unpublished draft changes" : "Live is up to date";
1487
1495
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "orion-builder-v2-editor", children: [
1488
1496
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { className: "orion-builder-v2-topbar", children: [
1489
1497
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
@@ -1491,6 +1499,11 @@ function GrapesPageEditor({
1491
1499
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: initialData?.title || "Untitled page" })
1492
1500
  ] }),
1493
1501
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "orion-builder-v2-toolbar", "aria-label": "Builder controls", children: [
1502
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `orion-builder-v2-sync is-${isDirty ? "dirty" : publicationState}`, children: [
1503
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: statusLabel }),
1504
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: liveStatusLabel }),
1505
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: statusMeta })
1506
+ ] }),
1494
1507
  ["desktop", "tablet", "mobile"].map((device) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1495
1508
  "button",
1496
1509
  {
@@ -1574,13 +1587,6 @@ function GrapesPageEditor({
1574
1587
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "orion-builder-v2-canvas", ref: containerRef })
1575
1588
  ] }),
1576
1589
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("aside", { className: "orion-builder-v2-inspector", "aria-label": "Selected section settings", children: [
1577
- saveMessage || lastSavedAt ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "orion-builder-v2-save-status", children: [
1578
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: saveMessage || "Ready" }),
1579
- lastSavedAt ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1580
- "Last saved ",
1581
- lastSavedAt
1582
- ] }) : null
1583
- ] }) : null,
1584
1590
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "orion-builder-v2-panel", children: [
1585
1591
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { children: selectionSummary ? selectionSummary.label : "No section selected" }),
1586
1592
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: selectionSummary ? "Edit this section content, images, links, layout, and spacing." : "Select a section or element on the canvas to edit its settings." }),
@@ -1052,8 +1052,10 @@ function GrapesPageEditor({
1052
1052
  const selectedComponentRef = useRef(null);
1053
1053
  const [error, setError] = useState("");
1054
1054
  const [historyState, setHistoryState] = useState({ canRedo: false, canUndo: false });
1055
+ const [isDirty, setIsDirty] = useState(false);
1055
1056
  const [lastSavedAt, setLastSavedAt] = useState("");
1056
1057
  const [loading, setLoading] = useState(true);
1058
+ const [publicationState, setPublicationState] = useState("live");
1057
1059
  const [selectedDevice, setSelectedDevice] = useState("desktop");
1058
1060
  const [saving, setSaving] = useState(null);
1059
1061
  const [saveMessage, setSaveMessage] = useState("");
@@ -1202,7 +1204,9 @@ function GrapesPageEditor({
1202
1204
  editor.loadProjectData(projectData);
1203
1205
  void loadPayloadMediaAssets(editor);
1204
1206
  editor.on("update", () => {
1205
- postToParent({ dirty: editor.getDirtyCount() > 0, type: "dirty-state" });
1207
+ const hasDirtyChanges = editor.getDirtyCount() > 0;
1208
+ setIsDirty(hasDirtyChanges);
1209
+ postToParent({ dirty: hasDirtyChanges, type: "dirty-state" });
1206
1210
  updateHistoryState(editor);
1207
1211
  runValidation(editor);
1208
1212
  setSaveMessage("Unsaved changes");
@@ -1219,7 +1223,6 @@ function GrapesPageEditor({
1219
1223
  const typed = component;
1220
1224
  selectedComponentRef.current = typed;
1221
1225
  setSelectionSummary(summarizeSelectedComponent(typed));
1222
- setSaveMessage("Selection ready");
1223
1226
  });
1224
1227
  setSelectedDevice(editor.getDevice() || "desktop");
1225
1228
  runValidation(editor);
@@ -1301,6 +1304,8 @@ function GrapesPageEditor({
1301
1304
  throw new Error(await parsePayloadErrorMessage(response, "Could not save this page."));
1302
1305
  }
1303
1306
  editor.clearDirtyCount();
1307
+ setIsDirty(false);
1308
+ setPublicationState(status === "published" ? "live" : "draft");
1304
1309
  postToParent({
1305
1310
  dirty: false,
1306
1311
  type: "dirty-state"
@@ -1360,6 +1365,9 @@ function GrapesPageEditor({
1360
1365
  window.addEventListener("message", onMessage);
1361
1366
  return () => window.removeEventListener("message", onMessage);
1362
1367
  }, [saving]);
1368
+ const statusLabel = saving ? saving === "published" ? "Publishing..." : "Saving draft..." : isDirty ? "Unsaved changes" : saveMessage && saveMessage !== "Unsaved changes" ? saveMessage : "All changes saved";
1369
+ const statusMeta = lastSavedAt ? `Last saved ${lastSavedAt}` : "Ready";
1370
+ const liveStatusLabel = isDirty ? "Save draft to update" : publicationState === "draft" ? "Unpublished draft changes" : "Live is up to date";
1363
1371
  return /* @__PURE__ */ jsxs("div", { className: "orion-builder-v2-editor", children: [
1364
1372
  /* @__PURE__ */ jsxs("header", { className: "orion-builder-v2-topbar", children: [
1365
1373
  /* @__PURE__ */ jsxs("div", { children: [
@@ -1367,6 +1375,11 @@ function GrapesPageEditor({
1367
1375
  /* @__PURE__ */ jsx("h1", { children: initialData?.title || "Untitled page" })
1368
1376
  ] }),
1369
1377
  /* @__PURE__ */ jsxs("div", { className: "orion-builder-v2-toolbar", "aria-label": "Builder controls", children: [
1378
+ /* @__PURE__ */ jsxs("div", { className: `orion-builder-v2-sync is-${isDirty ? "dirty" : publicationState}`, children: [
1379
+ /* @__PURE__ */ jsx("strong", { children: statusLabel }),
1380
+ /* @__PURE__ */ jsx("span", { children: liveStatusLabel }),
1381
+ /* @__PURE__ */ jsx("small", { children: statusMeta })
1382
+ ] }),
1370
1383
  ["desktop", "tablet", "mobile"].map((device) => /* @__PURE__ */ jsx(
1371
1384
  "button",
1372
1385
  {
@@ -1450,13 +1463,6 @@ function GrapesPageEditor({
1450
1463
  /* @__PURE__ */ jsx("div", { className: "orion-builder-v2-canvas", ref: containerRef })
1451
1464
  ] }),
1452
1465
  /* @__PURE__ */ jsxs("aside", { className: "orion-builder-v2-inspector", "aria-label": "Selected section settings", children: [
1453
- saveMessage || lastSavedAt ? /* @__PURE__ */ jsxs("div", { className: "orion-builder-v2-save-status", children: [
1454
- /* @__PURE__ */ jsx("strong", { children: saveMessage || "Ready" }),
1455
- lastSavedAt ? /* @__PURE__ */ jsxs("span", { children: [
1456
- "Last saved ",
1457
- lastSavedAt
1458
- ] }) : null
1459
- ] }) : null,
1460
1466
  /* @__PURE__ */ jsxs("div", { className: "orion-builder-v2-panel", children: [
1461
1467
  /* @__PURE__ */ jsx("h2", { children: selectionSummary ? selectionSummary.label : "No section selected" }),
1462
1468
  /* @__PURE__ */ jsx("p", { children: selectionSummary ? "Edit this section content, images, links, layout, and spacing." : "Select a section or element on the canvas to edit its settings." }),
@@ -56,6 +56,59 @@
56
56
  justify-content: flex-end;
57
57
  }
58
58
 
59
+ .orion-builder-v2-sync {
60
+ align-items: center;
61
+ background: #fff8ef;
62
+ border: 1px solid #e7cbb9;
63
+ border-radius: 12px;
64
+ color: var(--builder-v2-ink);
65
+ display: grid;
66
+ gap: 1px;
67
+ min-height: 38px;
68
+ min-width: 172px;
69
+ padding: 6px 10px;
70
+ }
71
+
72
+ .orion-builder-v2-sync strong {
73
+ font-size: 0.76rem;
74
+ font-weight: 900;
75
+ line-height: 1.1;
76
+ }
77
+
78
+ .orion-builder-v2-sync span,
79
+ .orion-builder-v2-sync small {
80
+ font-size: 0.66rem;
81
+ font-weight: 800;
82
+ line-height: 1.1;
83
+ white-space: nowrap;
84
+ }
85
+
86
+ .orion-builder-v2-sync span {
87
+ color: #2f5472;
88
+ }
89
+
90
+ .orion-builder-v2-sync small {
91
+ color: var(--builder-v2-muted);
92
+ }
93
+
94
+ .orion-builder-v2-sync.is-dirty {
95
+ background: #fff7ed;
96
+ border-color: #e3a879;
97
+ }
98
+
99
+ .orion-builder-v2-sync.is-dirty span {
100
+ color: #9a4c1e;
101
+ }
102
+
103
+ .orion-builder-v2-sync.is-draft {
104
+ background: #fff9e8;
105
+ border-color: #efcf84;
106
+ }
107
+
108
+ .orion-builder-v2-sync.is-draft span {
109
+ color: #7d5a0a;
110
+ }
111
+
59
112
  .orion-builder-v2-tool {
60
113
  background: #fffaf4;
61
114
  border: 1px solid #e7cbb9;
@@ -551,15 +604,55 @@
551
604
  .orion-builder-v2-editor .gjs-sm-properties {
552
605
  display: grid;
553
606
  gap: 8px;
607
+ grid-template-columns: repeat(2, minmax(0, 1fr));
554
608
  padding: 10px;
555
609
  }
556
610
 
557
611
  .orion-builder-v2-editor .gjs-sm-property {
612
+ box-sizing: border-box;
558
613
  float: none;
559
614
  margin: 0;
615
+ min-width: 0;
560
616
  width: 100%;
561
617
  }
562
618
 
619
+ .orion-builder-v2-editor .gjs-sm-property__display,
620
+ .orion-builder-v2-editor .gjs-sm-property__position,
621
+ .orion-builder-v2-editor .gjs-sm-property__width,
622
+ .orion-builder-v2-editor .gjs-sm-property__height,
623
+ .orion-builder-v2-editor .gjs-sm-property__min-height,
624
+ .orion-builder-v2-editor .gjs-sm-property__font-family,
625
+ .orion-builder-v2-editor .gjs-sm-property__background,
626
+ .orion-builder-v2-editor .gjs-sm-property__border,
627
+ .orion-builder-v2-editor .gjs-sm-property__box-shadow,
628
+ .orion-builder-v2-editor .gjs-sm-composite {
629
+ grid-column: 1 / -1;
630
+ }
631
+
632
+ .orion-builder-v2-editor .gjs-sm-property__top,
633
+ .orion-builder-v2-editor .gjs-sm-property__margin-top,
634
+ .orion-builder-v2-editor .gjs-sm-property__padding-top {
635
+ order: 10;
636
+ }
637
+
638
+ .orion-builder-v2-editor .gjs-sm-property__bottom,
639
+ .orion-builder-v2-editor .gjs-sm-property__margin-bottom,
640
+ .orion-builder-v2-editor .gjs-sm-property__padding-bottom {
641
+ order: 11;
642
+ }
643
+
644
+ .orion-builder-v2-editor .gjs-sm-property__left,
645
+ .orion-builder-v2-editor .gjs-sm-property__margin-left,
646
+ .orion-builder-v2-editor .gjs-sm-property__padding-left {
647
+ order: 12;
648
+ }
649
+
650
+ .orion-builder-v2-editor .gjs-sm-property__right,
651
+ .orion-builder-v2-editor .gjs-sm-property__margin-right,
652
+ .orion-builder-v2-editor .gjs-sm-property__padding-right {
653
+ order: 13;
654
+ }
655
+
563
656
  .orion-builder-v2-editor .gjs-sm-property .gjs-sm-label {
564
657
  display: block;
565
658
  margin-bottom: 7px;
@@ -568,10 +661,15 @@
568
661
  .orion-builder-v2-editor .gjs-sm-property:not(.gjs-sm-radio) .gjs-field,
569
662
  .orion-builder-v2-editor .gjs-sm-property .gjs-sm-field {
570
663
  background: #fffdf9 !important;
571
- border: 1px solid rgba(205, 181, 163, 0.95) !important;
572
- border-radius: 6px;
664
+ background-clip: padding-box;
665
+ border: 1px solid #d6b7a1 !important;
666
+ border-radius: 8px;
667
+ box-sizing: border-box;
573
668
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.88);
574
669
  min-height: 36px;
670
+ min-width: 0;
671
+ overflow: hidden;
672
+ width: 100% !important;
575
673
  }
576
674
 
577
675
  .orion-builder-v2-editor .gjs-sm-property:not(.gjs-sm-radio) .gjs-field:hover,
@@ -591,8 +689,21 @@
591
689
  .orion-builder-v2-editor .gjs-field input,
592
690
  .orion-builder-v2-editor .gjs-field select,
593
691
  .orion-builder-v2-editor .gjs-field textarea {
692
+ background: transparent !important;
693
+ border: 0 !important;
694
+ box-shadow: none !important;
695
+ box-sizing: border-box;
594
696
  min-height: 36px;
595
697
  outline: none !important;
698
+ width: 100% !important;
699
+ }
700
+
701
+ .orion-builder-v2-editor .gjs-field .gjs-input-unit {
702
+ background: transparent !important;
703
+ border: 0 !important;
704
+ color: #7a879f;
705
+ height: 100%;
706
+ padding-right: 8px;
596
707
  }
597
708
 
598
709
  .orion-builder-v2-editor .gjs-field textarea {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orion-studios/payload-studio",
3
- "version": "0.6.0-beta.67",
3
+ "version": "0.6.0-beta.68",
4
4
  "description": "Base CMS, builder, and custom admin toolkit for Orion Studios websites",
5
5
  "types": "./dist/index.d.ts",
6
6
  "main": "./dist/index.js",