@orion-studios/payload-studio 0.6.0-beta.18 → 0.6.0-beta.180

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.
Files changed (38) hide show
  1. package/dist/admin/client.js +742 -540
  2. package/dist/admin/client.mjs +825 -632
  3. package/dist/admin/index.js +37 -25
  4. package/dist/admin/index.mjs +2 -2
  5. package/dist/admin-app/client.js +14 -5
  6. package/dist/admin-app/client.mjs +1 -1
  7. package/dist/admin-app/styles.css +257 -0
  8. package/dist/admin.css +80 -0
  9. package/dist/builder-v2/client.d.mts +18 -0
  10. package/dist/builder-v2/client.d.ts +18 -0
  11. package/dist/builder-v2/client.js +4339 -0
  12. package/dist/builder-v2/client.mjs +4214 -0
  13. package/dist/builder-v2/index.d.mts +249 -0
  14. package/dist/builder-v2/index.d.ts +249 -0
  15. package/dist/builder-v2/index.js +805 -0
  16. package/dist/builder-v2/index.mjs +755 -0
  17. package/dist/builder-v2/styles.css +2693 -0
  18. package/dist/{chunk-XKUTZ7IU.mjs → chunk-276KAPGM.mjs} +56 -5
  19. package/dist/{chunk-PF3EBZXF.mjs → chunk-7ZMXZRBP.mjs} +39 -3
  20. package/dist/{chunk-3AHBR7RI.mjs → chunk-KHK6RTGC.mjs} +40 -28
  21. package/dist/{chunk-KPIX7OSV.mjs → chunk-NF37A575.mjs} +14 -5
  22. package/dist/{chunk-OTHERBGX.mjs → chunk-ZADL33R6.mjs} +1 -1
  23. package/dist/{index-Cv-6qnrw.d.mts → index-D5zrOdyv.d.mts} +3 -1
  24. package/dist/{index-Crx_MtPw.d.ts → index-Dv-Alx4h.d.ts} +3 -1
  25. package/dist/index.d.mts +1 -1
  26. package/dist/index.d.ts +1 -1
  27. package/dist/index.js +128 -29
  28. package/dist/index.mjs +10 -10
  29. package/dist/nextjs/index.js +39 -3
  30. package/dist/nextjs/index.mjs +2 -2
  31. package/dist/studio-pages/builder.css +66 -5
  32. package/dist/studio-pages/client.js +618 -73
  33. package/dist/studio-pages/client.mjs +641 -96
  34. package/dist/studio-pages/index.d.mts +1 -1
  35. package/dist/studio-pages/index.d.ts +1 -1
  36. package/dist/studio-pages/index.js +91 -4
  37. package/dist/studio-pages/index.mjs +3 -3
  38. package/package.json +22 -3
@@ -749,7 +749,7 @@ var init_OrionBlocksFieldImpl = __esm({
749
749
  const schemaPath = schemaPathFromProps ?? name;
750
750
  const minRows = minRowsProp ?? (required ? 1 : 0);
751
751
  const { setDocFieldPreferences } = (0, import_DocumentInfo.useDocumentInfo)();
752
- const { addFieldRow, dispatchFields, getFields, moveFieldRow, removeFieldRow, replaceState, setModified } = (0, import_Form.useForm)();
752
+ const { addFieldRow, dispatchFields, getFields: getFields2, moveFieldRow, removeFieldRow, replaceState, setModified } = (0, import_Form.useForm)();
753
753
  const { code: locale } = (0, import_Locale.useLocale)();
754
754
  const configContext = (0, import_Config.useConfig)();
755
755
  const config = configContext?.config ?? {};
@@ -911,7 +911,7 @@ var init_OrionBlocksFieldImpl = __esm({
911
911
  (rowIndex) => {
912
912
  const result = clipboardCopy({
913
913
  getDataToCopy: () => reduceFormStateByPath({
914
- formState: getFields(),
914
+ formState: getFields2(),
915
915
  path: safePath,
916
916
  rowIndex
917
917
  }),
@@ -929,13 +929,13 @@ var init_OrionBlocksFieldImpl = __esm({
929
929
  import_sonner.toast.success(t("general:copied"));
930
930
  }
931
931
  },
932
- [clientBlocks, getFields, safePath, t, type]
932
+ [clientBlocks, getFields2, safePath, t, type]
933
933
  );
934
934
  const pasteRow = (0, import_react10.useCallback)(
935
935
  (rowIndex) => {
936
936
  const result = clipboardPaste({
937
937
  onPaste: (dataFromClipboard) => {
938
- const formState = getFields();
938
+ const formState = getFields2();
939
939
  const newState = mergeFormStateFromClipboard({
940
940
  dataFromClipboard,
941
941
  formState,
@@ -952,11 +952,11 @@ var init_OrionBlocksFieldImpl = __esm({
952
952
  import_sonner.toast.error(result);
953
953
  }
954
954
  },
955
- [clientBlocks, getFields, replaceState, safePath, setModified, t]
955
+ [clientBlocks, getFields2, replaceState, safePath, setModified, t]
956
956
  );
957
957
  const pasteBlocks = (0, import_react10.useCallback)(
958
958
  (dataFromClipboard) => {
959
- const formState = getFields();
959
+ const formState = getFields2();
960
960
  const newState = mergeFormStateFromClipboard({
961
961
  dataFromClipboard,
962
962
  formState,
@@ -965,7 +965,7 @@ var init_OrionBlocksFieldImpl = __esm({
965
965
  replaceState(newState);
966
966
  setModified(true);
967
967
  },
968
- [getFields, replaceState, safePath, setModified]
968
+ [getFields2, replaceState, safePath, setModified]
969
969
  );
970
970
  const hasMaxRows = Boolean(maxRows && rows.length >= maxRows);
971
971
  const fieldErrorCount = errorPaths.length;
@@ -1042,7 +1042,7 @@ var init_OrionBlocksFieldImpl = __esm({
1042
1042
  className: `${baseClass}__header-action`,
1043
1043
  disabled,
1044
1044
  getDataToCopy: () => reduceFormStateByPath({
1045
- formState: getFields(),
1045
+ formState: getFields2(),
1046
1046
  path: safePath
1047
1047
  }),
1048
1048
  onPaste: pasteBlocks,
@@ -2873,8 +2873,26 @@ function NavIcon({ sectionID }) {
2873
2873
  return null;
2874
2874
  }
2875
2875
  }
2876
+ function LogoutIcon() {
2877
+ const props = {
2878
+ fill: "none",
2879
+ height: iconSize2,
2880
+ stroke: "currentColor",
2881
+ strokeLinecap: "round",
2882
+ strokeLinejoin: "round",
2883
+ strokeWidth: 2,
2884
+ viewBox: "0 0 24 24",
2885
+ width: iconSize2
2886
+ };
2887
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("svg", { ...props, "aria-hidden": "true", children: [
2888
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("path", { d: "M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4" }),
2889
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("polyline", { points: "10 17 5 12 10 7" }),
2890
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("line", { x1: "5", y1: "12", x2: "17", y2: "12" })
2891
+ ] });
2892
+ }
2876
2893
  function AdminStudioNav(props) {
2877
2894
  const { user } = (0, import_ui3.useAuth)();
2895
+ const [loggingOut, setLoggingOut] = (0, import_react13.useState)(false);
2878
2896
  const brandName = getPropString(props, "brandName", "Orion Studio");
2879
2897
  const logoUrl = getPropString(props, "logoUrl", "");
2880
2898
  const compact = getPropBoolean(props, "compact", false);
@@ -2885,6 +2903,18 @@ function AdminStudioNav(props) {
2885
2903
  const dashboardPath = adminBasePath;
2886
2904
  const userRole = readUserRole(user);
2887
2905
  const links = (0, import_react13.useMemo)(() => buildStudioNavItems(props, adminBasePath), [adminBasePath, props]);
2906
+ const logout = async () => {
2907
+ setLoggingOut(true);
2908
+ try {
2909
+ await fetch("/api/users/logout", {
2910
+ credentials: "include",
2911
+ method: "POST"
2912
+ });
2913
+ window.location.href = `${adminBasePath}/login`;
2914
+ } finally {
2915
+ setLoggingOut(false);
2916
+ }
2917
+ };
2888
2918
  if (isStudioShellRoute(pathname, props, adminBasePath)) {
2889
2919
  return null;
2890
2920
  }
@@ -2975,7 +3005,38 @@ function AdminStudioNav(props) {
2975
3005
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { color: "var(--theme-elevation-700)", fontSize: "0.85rem" }, children: "Signed in as" }),
2976
3006
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { fontWeight: 800, marginBottom: "0.55rem" }, children: typeof user?.email === "string" ? user.email : "User" })
2977
3007
  ] }) : null,
2978
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_ui3.Logout, {})
3008
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
3009
+ "button",
3010
+ {
3011
+ "aria-label": "Log out",
3012
+ disabled: loggingOut,
3013
+ onClick: () => void logout(),
3014
+ style: {
3015
+ alignItems: "center",
3016
+ background: "transparent",
3017
+ border: "1px solid var(--theme-elevation-150)",
3018
+ borderRadius: 10,
3019
+ color: "var(--theme-elevation-900)",
3020
+ cursor: loggingOut ? "not-allowed" : "pointer",
3021
+ display: "inline-flex",
3022
+ font: "inherit",
3023
+ fontWeight: 800,
3024
+ gap: compact ? 0 : "0.45rem",
3025
+ justifyContent: "center",
3026
+ minHeight: compact ? 48 : 34,
3027
+ minWidth: compact ? 48 : 0,
3028
+ opacity: loggingOut ? 0.55 : 1,
3029
+ padding: compact ? 0 : "0.4rem 0.62rem",
3030
+ width: compact ? 48 : "auto"
3031
+ },
3032
+ title: "Log out",
3033
+ type: "button",
3034
+ children: [
3035
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(LogoutIcon, {}),
3036
+ !compact ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { children: loggingOut ? "Logging out..." : "Log out" }) : null
3037
+ ]
3038
+ }
3039
+ )
2979
3040
  ]
2980
3041
  }
2981
3042
  )
@@ -3048,6 +3109,14 @@ function NavIcon2({ name }) {
3048
3109
  return null;
3049
3110
  }
3050
3111
  }
3112
+ function LogoutIcon2() {
3113
+ const props = { width: iconSize3, height: iconSize3, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", style: iconStyle };
3114
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("svg", { ...props, "aria-hidden": "true", children: [
3115
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("path", { d: "M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4" }),
3116
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("polyline", { points: "10 17 5 12 10 7" }),
3117
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("line", { x1: "5", y1: "12", x2: "17", y2: "12" })
3118
+ ] });
3119
+ }
3051
3120
  function AdminShellClient({
3052
3121
  children,
3053
3122
  brandName,
@@ -3059,14 +3128,12 @@ function AdminShellClient({
3059
3128
  onLogout,
3060
3129
  storageKey = "orion-admin-shell-collapsed"
3061
3130
  }) {
3062
- const [collapsed, setCollapsed] = (0, import_react14.useState)(false);
3131
+ const [collapsed, setCollapsed] = (0, import_react14.useState)(true);
3063
3132
  const [loggingOut, setLoggingOut] = (0, import_react14.useState)(false);
3064
3133
  (0, import_react14.useEffect)(() => {
3065
3134
  try {
3066
3135
  const stored = window.localStorage.getItem(storageKey);
3067
- if (stored === "1") {
3068
- setCollapsed(true);
3069
- }
3136
+ setCollapsed(stored === "1");
3070
3137
  } catch {
3071
3138
  }
3072
3139
  }, [storageKey]);
@@ -3128,7 +3195,10 @@ function AdminShellClient({
3128
3195
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "orion-admin-user-label", children: "Signed in as" }),
3129
3196
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "orion-admin-user-email", children: userEmail })
3130
3197
  ] }) : null,
3131
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { className: "orion-admin-logout", disabled: loggingOut, onClick: handleLogout, type: "button", children: loggingOut ? "..." : "Log out" })
3198
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("button", { className: "orion-admin-logout", disabled: loggingOut, onClick: handleLogout, type: "button", children: [
3199
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(LogoutIcon2, {}),
3200
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { children: loggingOut ? "Logging out..." : "Log out" })
3201
+ ] })
3132
3202
  ] })
3133
3203
  ] }),
3134
3204
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("main", { className: "orion-admin-main", children })
@@ -4040,15 +4110,16 @@ function AdminStudioDashboard(rawProps) {
4040
4110
  }
4041
4111
 
4042
4112
  // src/admin/components/studio/AdminStudioPagesListView.tsx
4043
- var import_react17 = require("react");
4113
+ var import_react19 = require("react");
4044
4114
  var import_link2 = __toESM(require("next/link"));
4115
+ var import_navigation3 = require("next/navigation");
4116
+ var import_ui7 = require("@payloadcms/ui");
4117
+
4118
+ // src/admin/components/studio/AdminStudioNewPageView.tsx
4119
+ var import_react17 = require("react");
4045
4120
  var import_ui5 = require("@payloadcms/ui");
4046
4121
  var import_jsx_runtime23 = require("react/jsx-runtime");
4047
- var hasAdminAccess = (user) => {
4048
- if (!user || typeof user !== "object") return false;
4049
- const role = user.role;
4050
- return typeof role === "string" && (role === "admin" || role === "developer");
4051
- };
4122
+ var pageTemplates = ["standard", "landing", "services", "contact"];
4052
4123
  var getPropString3 = (props, key, fallback) => {
4053
4124
  if (!props || typeof props !== "object") return fallback;
4054
4125
  const direct = props[key];
@@ -4060,83 +4131,107 @@ var getPropString3 = (props, key, fallback) => {
4060
4131
  }
4061
4132
  return fallback;
4062
4133
  };
4063
- function AdminStudioPagesListView(props) {
4134
+ var canManagePages = (user) => {
4135
+ if (!user || typeof user !== "object") return false;
4136
+ const role = user.role;
4137
+ return role === "admin" || role === "developer" || role === "editor";
4138
+ };
4139
+ var slugify = (value) => value.toLowerCase().trim().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-");
4140
+ function AdminStudioNewPageView(props) {
4064
4141
  const { user } = (0, import_ui5.useAuth)();
4065
- const pagesCollectionSlug = getPropString3(props, "pagesCollectionSlug", "pages");
4066
4142
  const adminBasePath = useAdminBasePath();
4067
- const newPagePath = resolveAdminPath(adminBasePath, "/pages/new");
4068
- const [loading, setLoading] = (0, import_react17.useState)(true);
4143
+ const pagesCollectionSlug = getPropString3(props, "pagesCollectionSlug", "pages");
4144
+ const [submitting, setSubmitting] = (0, import_react17.useState)(false);
4069
4145
  const [error, setError] = (0, import_react17.useState)(null);
4070
- const [docs, setDocs] = (0, import_react17.useState)([]);
4071
- const apiURL = (0, import_react17.useMemo)(() => {
4072
- const params = new URLSearchParams({
4073
- depth: "0",
4074
- limit: "100",
4075
- sort: "-updatedAt",
4076
- draft: "true"
4077
- });
4078
- return `/api/${pagesCollectionSlug}?${params.toString()}`;
4079
- }, [pagesCollectionSlug]);
4080
- (0, import_react17.useEffect)(() => {
4081
- let cancelled = false;
4082
- const run = async () => {
4083
- setLoading(true);
4084
- setError(null);
4085
- try {
4086
- const res = await fetch(apiURL, { credentials: "include" });
4087
- if (!res.ok) {
4088
- const body = await res.text();
4089
- throw new Error(body || "Failed to fetch pages");
4090
- }
4091
- const data = await res.json();
4092
- if (!cancelled) {
4093
- setDocs(Array.isArray(data.docs) ? data.docs : []);
4094
- }
4095
- } catch (err) {
4096
- if (!cancelled) {
4097
- setError(err instanceof Error ? err.message : "Failed to fetch pages");
4098
- }
4099
- } finally {
4100
- if (!cancelled) {
4101
- setLoading(false);
4102
- }
4146
+ if (!canManagePages(user)) {
4147
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(StudioSectionLayout, { navProps: props, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
4148
+ AdminPage,
4149
+ {
4150
+ breadcrumbs: [
4151
+ { label: "Dashboard", href: adminBasePath },
4152
+ { label: "Pages", href: resolveAdminPath(adminBasePath, "/pages") },
4153
+ { label: "New Page" }
4154
+ ],
4155
+ description: "You do not have access to create pages.",
4156
+ title: "New Page",
4157
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "orion-admin-card", children: [
4158
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("strong", { children: "Access denied" }),
4159
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { children: "This section is restricted to administrator, developer, and editor accounts." })
4160
+ ] })
4103
4161
  }
4104
- };
4105
- void run();
4106
- return () => {
4107
- cancelled = true;
4108
- };
4109
- }, [apiURL]);
4110
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(StudioSectionLayout, { navProps: props, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
4162
+ ) });
4163
+ }
4164
+ const createPage = async (event) => {
4165
+ event.preventDefault();
4166
+ setSubmitting(true);
4167
+ setError(null);
4168
+ try {
4169
+ const formData = new FormData(event.currentTarget);
4170
+ const titleValue = String(formData.get("title") || "").trim();
4171
+ const slugValue = String(formData.get("slug") || "").trim();
4172
+ const templateValue = String(formData.get("template") || "standard").trim();
4173
+ const template = pageTemplates.includes(templateValue) ? templateValue : "standard";
4174
+ const title = titleValue || "Untitled Page";
4175
+ const slug = slugValue || slugify(title) || "untitled-page";
4176
+ const response = await fetch(`/api/${pagesCollectionSlug}`, {
4177
+ body: JSON.stringify({
4178
+ _status: "draft",
4179
+ slug,
4180
+ template,
4181
+ title
4182
+ }),
4183
+ credentials: "include",
4184
+ headers: {
4185
+ "Content-Type": "application/json"
4186
+ },
4187
+ method: "POST"
4188
+ });
4189
+ if (!response.ok) {
4190
+ throw new Error(`Failed to create page (${response.status}).`);
4191
+ }
4192
+ const payload = await response.json();
4193
+ const id = typeof payload.id === "string" || typeof payload.id === "number" ? String(payload.id) : "";
4194
+ if (!id) {
4195
+ throw new Error("Page created but no document ID was returned.");
4196
+ }
4197
+ window.location.assign(resolveAdminPath(adminBasePath, `/pages/${id}`));
4198
+ } catch (createError) {
4199
+ setError(createError instanceof Error ? createError.message : "Failed to create page.");
4200
+ } finally {
4201
+ setSubmitting(false);
4202
+ }
4203
+ };
4204
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(StudioSectionLayout, { navProps: props, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
4111
4205
  AdminPage,
4112
4206
  {
4113
- actions: hasAdminAccess(user) ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_link2.default, { className: "orion-admin-action-button", href: newPagePath, children: "New Page" }) : null,
4114
4207
  breadcrumbs: [
4115
4208
  { label: "Dashboard", href: adminBasePath },
4116
- { label: "Pages" }
4209
+ { label: "Pages", href: resolveAdminPath(adminBasePath, "/pages") },
4210
+ { label: "New Page" }
4117
4211
  ],
4118
- description: "Open a page to edit it in the inline custom builder.",
4119
- title: "Pages",
4120
- children: [
4121
- loading ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "orion-admin-list-meta", children: "Loading..." }) : null,
4212
+ description: "Create a new page and open it in the custom editor.",
4213
+ title: "New Page",
4214
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("form", { className: "orion-admin-form", onSubmit: createPage, children: [
4122
4215
  error ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "orion-admin-error", children: error }) : null,
4123
- /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "orion-admin-list", children: [
4124
- !loading && !error && docs.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "orion-admin-card", children: [
4125
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("strong", { children: "No pages yet" }),
4126
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { children: "Create the first page to start building content." })
4127
- ] }) : null,
4128
- docs.map((doc) => {
4129
- const id = typeof doc.id === "string" || typeof doc.id === "number" ? String(doc.id) : "";
4130
- if (!id) return null;
4131
- const title = typeof doc.title === "string" ? doc.title : "Untitled Page";
4132
- const status = typeof doc._status === "string" ? doc._status : "draft";
4133
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_link2.default, { className: "orion-admin-list-item", href: resolveAdminPath(adminBasePath, `/pages/${id}`), children: [
4134
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("strong", { children: title }) }),
4135
- /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "orion-admin-pill", children: status })
4136
- ] }, id);
4137
- })
4138
- ] })
4139
- ]
4216
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { children: [
4217
+ "Title",
4218
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("input", { name: "title", placeholder: "Services", required: true, type: "text" })
4219
+ ] }),
4220
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { children: [
4221
+ "Slug",
4222
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("input", { name: "slug", placeholder: "services", type: "text" })
4223
+ ] }),
4224
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("label", { children: [
4225
+ "Template",
4226
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("select", { defaultValue: "standard", name: "template", children: [
4227
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("option", { value: "standard", children: "Standard" }),
4228
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("option", { value: "landing", children: "Landing" }),
4229
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("option", { value: "contact", children: "Contact" }),
4230
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("option", { value: "services", children: "Services" })
4231
+ ] })
4232
+ ] }),
4233
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { disabled: submitting, type: "submit", children: submitting ? "Creating..." : "Create Page" })
4234
+ ] })
4140
4235
  }
4141
4236
  ) });
4142
4237
  }
@@ -4145,16 +4240,6 @@ function AdminStudioPagesListView(props) {
4145
4240
  var import_react18 = require("react");
4146
4241
  var import_ui6 = require("@payloadcms/ui");
4147
4242
  var import_jsx_runtime24 = require("react/jsx-runtime");
4148
- var hasAdminAccess2 = (user) => {
4149
- if (!user || typeof user !== "object") return false;
4150
- const role = user.role;
4151
- return typeof role === "string" && (role === "admin" || role === "developer");
4152
- };
4153
- var isEditor = (user) => {
4154
- if (!user || typeof user !== "object") return false;
4155
- const role = user.role;
4156
- return typeof role === "string" && role === "editor";
4157
- };
4158
4243
  var getPropString4 = (props, key, fallback) => {
4159
4244
  if (!props || typeof props !== "object") return fallback;
4160
4245
  const direct = props[key];
@@ -4182,14 +4267,8 @@ var getPageIDFromPathname = (pathname) => {
4182
4267
  return pagePart ? decodeURIComponent(pagePart) : null;
4183
4268
  };
4184
4269
  function AdminStudioPageEditView(props) {
4185
- const { user } = (0, import_ui6.useAuth)();
4186
4270
  const adminBasePath = useAdminBasePath();
4187
4271
  const iframeRef = (0, import_react18.useRef)(null);
4188
- const [saving, setSaving] = (0, import_react18.useState)(null);
4189
- const [dirty, setDirty] = (0, import_react18.useState)(false);
4190
- const [hasUnpublishedChanges, setHasUnpublishedChanges] = (0, import_react18.useState)(false);
4191
- const [canUndo, setCanUndo] = (0, import_react18.useState)(false);
4192
- const [canRedo, setCanRedo] = (0, import_react18.useState)(false);
4193
4272
  const builderBasePath = getPropString4(props, "builderBasePath", "/builder");
4194
4273
  const pagesPath = resolveAdminPath(adminBasePath, "/pages");
4195
4274
  const pageIDFromParams = (0, import_react18.useMemo)(() => getParam(props.params, "id"), [props.params]);
@@ -4206,96 +4285,6 @@ function AdminStudioPageEditView(props) {
4206
4285
  }
4207
4286
  setDidResolvePathFallback(true);
4208
4287
  }, [pageIDFromParams]);
4209
- const canPublish = hasAdminAccess2(user) || isEditor(user);
4210
- const refreshUnpublishedState = async (id) => {
4211
- try {
4212
- const response = await fetch(
4213
- `/api/pages/versions?depth=0&limit=25&sort=-updatedAt&where[parent][equals]=${encodeURIComponent(id)}`,
4214
- {
4215
- credentials: "include"
4216
- }
4217
- );
4218
- if (!response.ok) {
4219
- return;
4220
- }
4221
- const payload = await response.json();
4222
- const docs = Array.isArray(payload.docs) ? payload.docs : [];
4223
- let latestDraft = 0;
4224
- let latestPublished = 0;
4225
- docs.forEach((doc) => {
4226
- const status = doc.version?._status;
4227
- const millis = typeof doc.updatedAt === "string" ? Date.parse(doc.updatedAt) : Number.NaN;
4228
- if (!Number.isFinite(millis)) {
4229
- return;
4230
- }
4231
- if (status === "draft") {
4232
- latestDraft = Math.max(latestDraft, millis);
4233
- }
4234
- if (status === "published") {
4235
- latestPublished = Math.max(latestPublished, millis);
4236
- }
4237
- });
4238
- setHasUnpublishedChanges(latestDraft > 0 && latestDraft >= latestPublished);
4239
- } catch {
4240
- }
4241
- };
4242
- (0, import_react18.useEffect)(() => {
4243
- if (!pageID) {
4244
- return;
4245
- }
4246
- void refreshUnpublishedState(pageID);
4247
- }, [pageID]);
4248
- const requestSave = (status) => {
4249
- const iframe = iframeRef.current;
4250
- if (!iframe?.contentWindow) {
4251
- import_ui6.toast.error("Editor is not ready yet. Please try again.");
4252
- return;
4253
- }
4254
- setSaving(status);
4255
- iframe.contentWindow.postMessage({ source: "payload-visual-builder-parent", type: "save", status }, "*");
4256
- };
4257
- const requestHistoryAction = (type) => {
4258
- const iframe = iframeRef.current;
4259
- if (!iframe?.contentWindow) {
4260
- import_ui6.toast.error("Editor is not ready yet. Please try again.");
4261
- return;
4262
- }
4263
- iframe.contentWindow.postMessage({ source: "payload-visual-builder-parent", type }, "*");
4264
- };
4265
- (0, import_react18.useEffect)(() => {
4266
- const onMessage = (event) => {
4267
- const data = event.data;
4268
- if (!data || data.source !== "payload-visual-builder-child" || typeof data.type !== "string") {
4269
- return;
4270
- }
4271
- if (data.type === "dirty-state") {
4272
- setDirty(Boolean(data.dirty));
4273
- return;
4274
- }
4275
- if (data.type === "history-state") {
4276
- setCanUndo(Boolean(data.canUndo));
4277
- setCanRedo(Boolean(data.canRedo));
4278
- return;
4279
- }
4280
- if (data.type === "save-result") {
4281
- setSaving(null);
4282
- if (data.ok) {
4283
- if (data.status === "draft") {
4284
- setHasUnpublishedChanges(true);
4285
- } else if (data.status === "published") {
4286
- setHasUnpublishedChanges(false);
4287
- } else if (pageID) {
4288
- void refreshUnpublishedState(pageID);
4289
- }
4290
- import_ui6.toast.success(typeof data.message === "string" ? data.message : "Saved.");
4291
- } else {
4292
- import_ui6.toast.error(typeof data.message === "string" ? data.message : "Save failed.");
4293
- }
4294
- }
4295
- };
4296
- window.addEventListener("message", onMessage);
4297
- return () => window.removeEventListener("message", onMessage);
4298
- }, []);
4299
4288
  if (!pageID && !didResolvePathFallback) {
4300
4289
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(StudioSectionLayout, { navProps: props, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
4301
4290
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
@@ -4336,164 +4325,25 @@ function AdminStudioPageEditView(props) {
4336
4325
  ]
4337
4326
  }
4338
4327
  ),
4339
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { display: "grid", gridTemplateRows: "auto 1fr", height: "calc(100vh - 120px)" }, children: [
4340
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
4341
- "div",
4342
- {
4343
- style: {
4344
- alignItems: "center",
4345
- background: "color-mix(in srgb, var(--orion-admin-card-bg) 96%, white)",
4346
- border: "1px solid var(--orion-admin-card-border)",
4347
- borderRadius: 14,
4348
- boxShadow: "0 12px 28px rgba(62, 42, 24, 0.08)",
4349
- colorScheme: "light",
4350
- display: "flex",
4351
- gap: "0.6rem",
4352
- justifyContent: "space-between",
4353
- padding: "0.65rem 0.9rem",
4354
- position: "sticky",
4355
- top: 0,
4356
- zIndex: 20
4357
- },
4358
- children: [
4359
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { minWidth: 0 }, children: [
4360
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { color: "var(--orion-admin-text)", fontWeight: 900 }, children: "Page Editor" }),
4361
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
4362
- "div",
4363
- {
4364
- style: {
4365
- color: "var(--orion-admin-muted)",
4366
- fontSize: "0.85rem",
4367
- overflow: "hidden",
4368
- textOverflow: "ellipsis"
4369
- },
4370
- children: [
4371
- "Editing: ",
4372
- pageID
4373
- ]
4374
- }
4375
- )
4376
- ] }),
4377
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { alignItems: "center", display: "flex", gap: "0.5rem" }, children: [
4378
- /* @__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" }),
4379
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4380
- "div",
4381
- {
4382
- style: {
4383
- background: hasUnpublishedChanges ? "#fff3cd" : "var(--orion-admin-accent-subtle)",
4384
- border: `1px solid ${hasUnpublishedChanges ? "#f0c36d" : "color-mix(in srgb, var(--orion-admin-accent) 36%, transparent)"}`,
4385
- borderRadius: 999,
4386
- color: hasUnpublishedChanges ? "#6a4a00" : "var(--orion-admin-accent)",
4387
- fontSize: "0.75rem",
4388
- fontWeight: 800,
4389
- padding: "0.2rem 0.55rem",
4390
- whiteSpace: "nowrap"
4391
- },
4392
- title: hasUnpublishedChanges ? "There are saved draft changes not yet published." : "The live page matches the latest published content.",
4393
- children: hasUnpublishedChanges ? "Unpublished draft changes" : "Live is up to date"
4394
- }
4395
- ),
4396
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4397
- "button",
4398
- {
4399
- disabled: !canUndo,
4400
- onClick: () => requestHistoryAction("undo"),
4401
- style: {
4402
- background: "transparent",
4403
- border: "1px solid var(--orion-admin-card-border)",
4404
- borderRadius: 12,
4405
- cursor: canUndo ? "pointer" : "not-allowed",
4406
- color: "var(--orion-admin-text)",
4407
- fontWeight: 800,
4408
- padding: "0.5rem 0.65rem"
4409
- },
4410
- type: "button",
4411
- children: "Undo"
4412
- }
4413
- ),
4414
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4415
- "button",
4416
- {
4417
- disabled: !canRedo,
4418
- onClick: () => requestHistoryAction("redo"),
4419
- style: {
4420
- background: "transparent",
4421
- border: "1px solid var(--orion-admin-card-border)",
4422
- borderRadius: 12,
4423
- cursor: canRedo ? "pointer" : "not-allowed",
4424
- color: "var(--orion-admin-text)",
4425
- fontWeight: 800,
4426
- padding: "0.5rem 0.65rem"
4427
- },
4428
- type: "button",
4429
- children: "Redo"
4430
- }
4431
- ),
4432
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4433
- "button",
4434
- {
4435
- disabled: saving !== null,
4436
- onClick: () => requestSave("draft"),
4437
- style: {
4438
- background: "var(--orion-admin-card-bg)",
4439
- border: "1px solid var(--orion-admin-card-border)",
4440
- borderRadius: 12,
4441
- cursor: saving ? "not-allowed" : "pointer",
4442
- color: "var(--orion-admin-text)",
4443
- fontWeight: 800,
4444
- padding: "0.5rem 0.75rem"
4445
- },
4446
- type: "button",
4447
- children: saving === "draft" ? "Saving\u2026" : "Save Draft"
4448
- }
4449
- ),
4450
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4451
- "button",
4452
- {
4453
- disabled: !canPublish || saving !== null,
4454
- onClick: () => requestSave("published"),
4455
- style: {
4456
- background: canPublish ? "var(--orion-admin-button-bg)" : "var(--orion-admin-card-border)",
4457
- border: "none",
4458
- borderRadius: 12,
4459
- color: canPublish ? "var(--orion-admin-button-text)" : "var(--orion-admin-text)",
4460
- cursor: !canPublish || saving ? "not-allowed" : "pointer",
4461
- fontWeight: 900,
4462
- padding: "0.5rem 0.75rem"
4463
- },
4464
- type: "button",
4465
- title: !canPublish ? "You do not have publish permissions." : void 0,
4466
- children: saving === "published" ? "Publishing\u2026" : "Publish"
4467
- }
4468
- )
4469
- ] })
4470
- ]
4471
- }
4472
- ),
4473
- /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4474
- "iframe",
4475
- {
4476
- ref: iframeRef,
4477
- src: `${builderBasePath.replace(/\/$/, "")}/${pageID}`,
4478
- style: { border: "none", height: "100%", width: "100%" },
4479
- title: "Page Builder",
4480
- onLoad: () => {
4481
- const iframe = iframeRef.current;
4482
- if (!iframe?.contentWindow) return;
4483
- iframe.contentWindow.postMessage({ source: "payload-visual-builder-parent", type: "dirty-check-request" }, "*");
4484
- iframe.contentWindow.postMessage({ source: "payload-visual-builder-parent", type: "history-check-request" }, "*");
4485
- }
4486
- }
4487
- )
4488
- ] })
4328
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { height: "100dvh", overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4329
+ "iframe",
4330
+ {
4331
+ ref: iframeRef,
4332
+ src: `${builderBasePath.replace(/\/$/, "")}/${pageID}`,
4333
+ style: { border: "none", height: "100%", width: "100%" },
4334
+ title: "Page Builder"
4335
+ }
4336
+ ) })
4489
4337
  ] }) });
4490
4338
  }
4491
4339
 
4492
- // src/admin/components/studio/AdminStudioNewPageView.tsx
4493
- var import_react19 = require("react");
4494
- var import_ui7 = require("@payloadcms/ui");
4340
+ // src/admin/components/studio/AdminStudioPagesListView.tsx
4495
4341
  var import_jsx_runtime25 = require("react/jsx-runtime");
4496
- var pageTemplates = ["standard", "landing", "services", "contact"];
4342
+ var hasAdminAccess = (user) => {
4343
+ if (!user || typeof user !== "object") return false;
4344
+ const role = user.role;
4345
+ return typeof role === "string" && (role === "admin" || role === "developer");
4346
+ };
4497
4347
  var getPropString5 = (props, key, fallback) => {
4498
4348
  if (!props || typeof props !== "object") return fallback;
4499
4349
  const direct = props[key];
@@ -4505,107 +4355,98 @@ var getPropString5 = (props, key, fallback) => {
4505
4355
  }
4506
4356
  return fallback;
4507
4357
  };
4508
- var canManagePages = (user) => {
4509
- if (!user || typeof user !== "object") return false;
4510
- const role = user.role;
4511
- return role === "admin" || role === "developer" || role === "editor";
4512
- };
4513
- var slugify = (value) => value.toLowerCase().trim().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-");
4514
- function AdminStudioNewPageView(props) {
4515
- const { user } = (0, import_ui7.useAuth)();
4358
+ function AdminStudioPagesListView(props) {
4516
4359
  const adminBasePath = useAdminBasePath();
4360
+ const pathname = (0, import_navigation3.usePathname)();
4361
+ const pagesPath = resolveAdminPath(adminBasePath, "/pages");
4362
+ const nestedPagePath = pathname && pathname.startsWith(`${pagesPath}/`) ? pathname.slice(`${pagesPath}/`.length).split("/")[0] : "";
4363
+ if (nestedPagePath === "new") {
4364
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(AdminStudioNewPageView, { ...props });
4365
+ }
4366
+ if (nestedPagePath) {
4367
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(AdminStudioPageEditView, { ...props });
4368
+ }
4369
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(AdminStudioPagesIndexView, { ...props, adminBasePath });
4370
+ }
4371
+ function AdminStudioPagesIndexView({
4372
+ adminBasePath,
4373
+ ...props
4374
+ }) {
4375
+ const { user } = (0, import_ui7.useAuth)();
4517
4376
  const pagesCollectionSlug = getPropString5(props, "pagesCollectionSlug", "pages");
4518
- const [submitting, setSubmitting] = (0, import_react19.useState)(false);
4377
+ const newPagePath = resolveAdminPath(adminBasePath, "/pages/new");
4378
+ const [loading, setLoading] = (0, import_react19.useState)(true);
4519
4379
  const [error, setError] = (0, import_react19.useState)(null);
4520
- if (!canManagePages(user)) {
4521
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(StudioSectionLayout, { navProps: props, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
4522
- AdminPage,
4523
- {
4524
- breadcrumbs: [
4525
- { label: "Dashboard", href: adminBasePath },
4526
- { label: "Pages", href: resolveAdminPath(adminBasePath, "/pages") },
4527
- { label: "New Page" }
4528
- ],
4529
- description: "You do not have access to create pages.",
4530
- title: "New Page",
4531
- children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "orion-admin-card", children: [
4532
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("strong", { children: "Access denied" }),
4533
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: "This section is restricted to administrator, developer, and editor accounts." })
4534
- ] })
4535
- }
4536
- ) });
4537
- }
4538
- const createPage = async (event) => {
4539
- event.preventDefault();
4540
- setSubmitting(true);
4541
- setError(null);
4542
- try {
4543
- const formData = new FormData(event.currentTarget);
4544
- const titleValue = String(formData.get("title") || "").trim();
4545
- const slugValue = String(formData.get("slug") || "").trim();
4546
- const templateValue = String(formData.get("template") || "standard").trim();
4547
- const template = pageTemplates.includes(templateValue) ? templateValue : "standard";
4548
- const title = titleValue || "Untitled Page";
4549
- const slug = slugValue || slugify(title) || "untitled-page";
4550
- const response = await fetch(`/api/${pagesCollectionSlug}`, {
4551
- body: JSON.stringify({
4552
- _status: "draft",
4553
- slug,
4554
- template,
4555
- title
4556
- }),
4557
- credentials: "include",
4558
- headers: {
4559
- "Content-Type": "application/json"
4560
- },
4561
- method: "POST"
4562
- });
4563
- if (!response.ok) {
4564
- throw new Error(`Failed to create page (${response.status}).`);
4565
- }
4566
- const payload = await response.json();
4567
- const id = typeof payload.id === "string" || typeof payload.id === "number" ? String(payload.id) : "";
4568
- if (!id) {
4569
- throw new Error("Page created but no document ID was returned.");
4380
+ const [docs, setDocs] = (0, import_react19.useState)([]);
4381
+ const apiURL = (0, import_react19.useMemo)(() => {
4382
+ const params = new URLSearchParams({
4383
+ depth: "0",
4384
+ limit: "100",
4385
+ sort: "-updatedAt",
4386
+ draft: "true"
4387
+ });
4388
+ return `/api/${pagesCollectionSlug}?${params.toString()}`;
4389
+ }, [pagesCollectionSlug]);
4390
+ (0, import_react19.useEffect)(() => {
4391
+ let cancelled = false;
4392
+ const run = async () => {
4393
+ setLoading(true);
4394
+ setError(null);
4395
+ try {
4396
+ const res = await fetch(apiURL, { credentials: "include" });
4397
+ if (!res.ok) {
4398
+ const body = await res.text();
4399
+ throw new Error(body || "Failed to fetch pages");
4400
+ }
4401
+ const data = await res.json();
4402
+ if (!cancelled) {
4403
+ setDocs(Array.isArray(data.docs) ? data.docs : []);
4404
+ }
4405
+ } catch (err) {
4406
+ if (!cancelled) {
4407
+ setError(err instanceof Error ? err.message : "Failed to fetch pages");
4408
+ }
4409
+ } finally {
4410
+ if (!cancelled) {
4411
+ setLoading(false);
4412
+ }
4570
4413
  }
4571
- window.location.assign(resolveAdminPath(adminBasePath, `/pages/${id}`));
4572
- } catch (createError) {
4573
- setError(createError instanceof Error ? createError.message : "Failed to create page.");
4574
- } finally {
4575
- setSubmitting(false);
4576
- }
4577
- };
4578
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(StudioSectionLayout, { navProps: props, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
4414
+ };
4415
+ void run();
4416
+ return () => {
4417
+ cancelled = true;
4418
+ };
4419
+ }, [apiURL]);
4420
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(StudioSectionLayout, { navProps: props, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
4579
4421
  AdminPage,
4580
4422
  {
4423
+ actions: hasAdminAccess(user) ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_link2.default, { className: "orion-admin-action-button", href: newPagePath, children: "New Page" }) : null,
4581
4424
  breadcrumbs: [
4582
4425
  { label: "Dashboard", href: adminBasePath },
4583
- { label: "Pages", href: resolveAdminPath(adminBasePath, "/pages") },
4584
- { label: "New Page" }
4426
+ { label: "Pages" }
4585
4427
  ],
4586
- description: "Create a new page and open it in the custom editor.",
4587
- title: "New Page",
4588
- children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("form", { className: "orion-admin-form", onSubmit: createPage, children: [
4428
+ description: "Open a page to edit it in the inline custom builder.",
4429
+ title: "Pages",
4430
+ children: [
4431
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "orion-admin-list-meta", children: "Loading..." }) : null,
4589
4432
  error ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "orion-admin-error", children: error }) : null,
4590
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("label", { children: [
4591
- "Title",
4592
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("input", { name: "title", placeholder: "Services", required: true, type: "text" })
4593
- ] }),
4594
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("label", { children: [
4595
- "Slug",
4596
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("input", { name: "slug", placeholder: "services", type: "text" })
4597
- ] }),
4598
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("label", { children: [
4599
- "Template",
4600
- /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("select", { defaultValue: "standard", name: "template", children: [
4601
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("option", { value: "standard", children: "Standard" }),
4602
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("option", { value: "landing", children: "Landing" }),
4603
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("option", { value: "contact", children: "Contact" }),
4604
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("option", { value: "services", children: "Services" })
4605
- ] })
4606
- ] }),
4607
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { disabled: submitting, type: "submit", children: submitting ? "Creating..." : "Create Page" })
4608
- ] })
4433
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "orion-admin-list", children: [
4434
+ !loading && !error && docs.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "orion-admin-card", children: [
4435
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("strong", { children: "No pages yet" }),
4436
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: "Create the first page to start building content." })
4437
+ ] }) : null,
4438
+ docs.map((doc) => {
4439
+ const id = typeof doc.id === "string" || typeof doc.id === "number" ? String(doc.id) : "";
4440
+ if (!id) return null;
4441
+ const title = typeof doc.title === "string" ? doc.title : "Untitled Page";
4442
+ const status = typeof doc._status === "string" ? doc._status : "draft";
4443
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_link2.default, { className: "orion-admin-list-item", href: resolveAdminPath(adminBasePath, `/pages/${id}`), children: [
4444
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("strong", { children: title }) }),
4445
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "orion-admin-pill", children: status })
4446
+ ] }, id);
4447
+ })
4448
+ ] })
4449
+ ]
4609
4450
  }
4610
4451
  ) });
4611
4452
  }
@@ -6874,7 +6715,7 @@ function MediaListItem({
6874
6715
 
6875
6716
  // src/admin-app/components/MediaUploadForm.tsx
6876
6717
  var import_react27 = require("react");
6877
- var import_navigation3 = require("next/navigation");
6718
+ var import_navigation4 = require("next/navigation");
6878
6719
 
6879
6720
  // src/shared/clientImageUploadOptimization.ts
6880
6721
  var MAX_DIRECT_UPLOAD_BYTES = 4e6;
@@ -7009,7 +6850,7 @@ var parseUploadError = async (response) => {
7009
6850
  return fallback;
7010
6851
  };
7011
6852
  function MediaUploadForm() {
7012
- const router = (0, import_navigation3.useRouter)();
6853
+ const router = (0, import_navigation4.useRouter)();
7013
6854
  const fileInputRef = (0, import_react27.useRef)(null);
7014
6855
  const [alt, setAlt] = (0, import_react27.useState)("");
7015
6856
  const [file, setFile] = (0, import_react27.useState)(null);
@@ -7544,17 +7385,17 @@ var FORM_TONE_OVERRIDES = {
7544
7385
  var IDENTITY_KEYS = /* @__PURE__ */ new Set(["contactEmail", "email", "firstName", "lastName", "name"]);
7545
7386
  var RESPONSE_FIELD_PREVIEW_LIMIT = 3;
7546
7387
  var RESPONSE_SCROLL_THRESHOLD = 3;
7547
- var hasAdminAccess3 = (user) => {
7388
+ var hasAdminAccess2 = (user) => {
7548
7389
  if (!user || typeof user !== "object") return false;
7549
7390
  const role = user.role;
7550
7391
  return typeof role === "string" && (role === "admin" || role === "developer");
7551
7392
  };
7552
- var isEditor2 = (user) => {
7393
+ var isEditor = (user) => {
7553
7394
  if (!user || typeof user !== "object") return false;
7554
7395
  const role = user.role;
7555
7396
  return typeof role === "string" && role === "editor";
7556
7397
  };
7557
- var canReviewForms2 = (user) => hasAdminAccess3(user) || isEditor2(user);
7398
+ var canReviewForms2 = (user) => hasAdminAccess2(user) || isEditor(user);
7558
7399
  var getPropString13 = (props, key, fallback) => {
7559
7400
  if (!props || typeof props !== "object") return fallback;
7560
7401
  const direct = props[key];
@@ -8240,15 +8081,22 @@ var getFormTitle2 = (value) => {
8240
8081
  // src/admin/components/studio/AdminStudioFormDetailView.tsx
8241
8082
  var import_jsx_runtime41 = require("react/jsx-runtime");
8242
8083
  var getNonEmptyText = (value, fallback = "") => typeof value === "string" && value.trim().length > 0 ? value : fallback;
8243
- var formatStepsText = (value) => {
8084
+ var normalizeSteps = (value) => {
8244
8085
  if (!Array.isArray(value)) {
8245
- return "[]";
8086
+ return [];
8246
8087
  }
8247
- try {
8248
- return JSON.stringify(value, null, 2);
8249
- } catch {
8250
- return "[]";
8088
+ return value.map((step) => step && typeof step === "object" && !Array.isArray(step) ? step : {});
8089
+ };
8090
+ var normalizeFormResponse = (value) => {
8091
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
8092
+ return {};
8251
8093
  }
8094
+ const record = value;
8095
+ const nestedDoc = record.doc;
8096
+ if (nestedDoc && typeof nestedDoc === "object" && !Array.isArray(nestedDoc)) {
8097
+ return nestedDoc;
8098
+ }
8099
+ return record;
8252
8100
  };
8253
8101
  var toEditorState = (doc) => {
8254
8102
  const emails = doc.emails && typeof doc.emails === "object" ? doc.emails : null;
@@ -8264,7 +8112,7 @@ var toEditorState = (doc) => {
8264
8112
  sendAdmin: emails?.sendAdmin !== false,
8265
8113
  sendConfirmation: emails?.sendConfirmation !== false,
8266
8114
  slug: getNonEmptyText(doc.slug),
8267
- stepsText: formatStepsText(doc.steps),
8115
+ steps: normalizeSteps(doc.steps),
8268
8116
  submitLabel: getNonEmptyText(doc.submitLabel, "Submit"),
8269
8117
  successMessage: getNonEmptyText(doc.successMessage),
8270
8118
  title: getNonEmptyText(doc.title, "Untitled Form")
@@ -8275,25 +8123,72 @@ var checkboxLabelStyle = {
8275
8123
  display: "flex",
8276
8124
  gap: "0.6rem"
8277
8125
  };
8278
- var codeStyle = {
8279
- background: "color-mix(in srgb, var(--orion-admin-card-bg) 82%, black)",
8280
- border: "1px solid var(--orion-admin-card-border)",
8281
- borderRadius: "var(--orion-admin-radius-sm)",
8282
- color: "var(--orion-admin-text)",
8283
- fontFamily: "ui-monospace, SFMono-Regular, SFMono-Regular, Menlo, monospace",
8284
- fontSize: "0.86rem",
8285
- lineHeight: 1.55,
8286
- margin: 0,
8287
- maxHeight: "28rem",
8288
- overflow: "auto",
8289
- padding: "0.9rem",
8290
- whiteSpace: "pre-wrap"
8291
- };
8292
8126
  var sectionGridStyle = {
8293
8127
  display: "grid",
8294
8128
  gap: "1rem",
8295
8129
  gridTemplateColumns: "repeat(auto-fit, minmax(320px, 1fr))"
8296
8130
  };
8131
+ var fieldTypeOptions = [
8132
+ "text",
8133
+ "textarea",
8134
+ "email",
8135
+ "phone",
8136
+ "url",
8137
+ "select",
8138
+ "radio",
8139
+ "checkbox",
8140
+ "checkbox-group",
8141
+ "date",
8142
+ "file"
8143
+ ];
8144
+ var getFields = (step) => Array.isArray(step.fields) ? step.fields.map(
8145
+ (field) => field && typeof field === "object" && !Array.isArray(field) ? field : {}
8146
+ ) : [];
8147
+ var toOptionsText = (value) => Array.isArray(value) ? value.map((entry) => {
8148
+ if (entry && typeof entry === "object" && !Array.isArray(entry)) {
8149
+ const record = entry;
8150
+ const label = typeof record.label === "string" ? record.label : "";
8151
+ const optionValue = typeof record.value === "string" ? record.value : label;
8152
+ return label && optionValue && label !== optionValue ? `${label} | ${optionValue}` : label || optionValue;
8153
+ }
8154
+ return typeof entry === "string" ? entry : "";
8155
+ }).filter(Boolean).join("\n") : "";
8156
+ var fromOptionsText = (value) => value.split("\n").map((entry) => entry.trim()).filter(Boolean).map((entry) => {
8157
+ const [label, optionValue] = entry.split("|").map((part) => part.trim());
8158
+ return {
8159
+ label,
8160
+ value: optionValue || label
8161
+ };
8162
+ });
8163
+ var slugifyFieldName = (value) => value.trim().replace(/[^a-z0-9]+/gi, " ").trim().replace(/\s+([a-z0-9])/gi, (_, char) => char.toUpperCase()).replace(/^./, (char) => char.toLowerCase());
8164
+ var blankField = () => ({
8165
+ label: "New field",
8166
+ name: "newField",
8167
+ required: false,
8168
+ type: "text"
8169
+ });
8170
+ var blankStep = () => ({
8171
+ fields: [blankField()],
8172
+ title: "New step"
8173
+ });
8174
+ var getStepTitle = (step, stepIndex) => getNonEmptyText(step.title, `Step ${stepIndex + 1}`);
8175
+ var getFieldLabel = (field, fieldIndex) => getNonEmptyText(field.label, getNonEmptyText(field.name, `Field ${fieldIndex + 1}`));
8176
+ var formatFieldTypeLabel = (value) => getNonEmptyText(value, "text").replace(/-/g, " ").replace(/^./, (char) => char.toUpperCase());
8177
+ var getStepDestination = (stepIndex, stepCount) => {
8178
+ if (stepIndex < stepCount - 1) {
8179
+ return `Next: Step ${stepIndex + 2}`;
8180
+ }
8181
+ return "Then: Submit form";
8182
+ };
8183
+ var moveItem = (items, fromIndex, toIndex) => {
8184
+ if (toIndex < 0 || toIndex >= items.length) {
8185
+ return items;
8186
+ }
8187
+ const nextItems = [...items];
8188
+ const [item] = nextItems.splice(fromIndex, 1);
8189
+ nextItems.splice(toIndex, 0, item);
8190
+ return nextItems;
8191
+ };
8297
8192
  function getFormIDFromPathname(pathname) {
8298
8193
  const marker = "/forms/";
8299
8194
  const raw = getIDFromPathname(pathname, marker);
@@ -8356,7 +8251,7 @@ function AdminStudioFormDetailView(props) {
8356
8251
  if (!submissionsResponse.ok) {
8357
8252
  throw new Error(`Failed to load submissions (${submissionsResponse.status}).`);
8358
8253
  }
8359
- const nextDoc = await formResponse.json();
8254
+ const nextDoc = normalizeFormResponse(await formResponse.json());
8360
8255
  const submissionsPayload = await submissionsResponse.json();
8361
8256
  const nextSubmissions = Array.isArray(submissionsPayload.docs) ? submissionsPayload.docs : [];
8362
8257
  setDoc(nextDoc);
@@ -8386,10 +8281,6 @@ function AdminStudioFormDetailView(props) {
8386
8281
  setError(null);
8387
8282
  setSavedMessage(null);
8388
8283
  try {
8389
- const parsedSteps = JSON.parse(editorState.stepsText);
8390
- if (!Array.isArray(parsedSteps)) {
8391
- throw new Error("Structure JSON must be an array of steps.");
8392
- }
8393
8284
  const payload = {
8394
8285
  emails: {
8395
8286
  adminRecipients: editorState.adminRecipientsText.split("\n").map((value) => value.trim()).filter(Boolean).map((email) => ({ email })),
@@ -8401,7 +8292,7 @@ function AdminStudioFormDetailView(props) {
8401
8292
  sendConfirmation: editorState.sendConfirmation
8402
8293
  },
8403
8294
  slug: editorState.slug.trim(),
8404
- steps: parsedSteps,
8295
+ steps: editorState.steps,
8405
8296
  submitLabel: editorState.submitLabel.trim(),
8406
8297
  successMessage: editorState.successMessage,
8407
8298
  title: editorState.title.trim()
@@ -8417,7 +8308,7 @@ function AdminStudioFormDetailView(props) {
8417
8308
  if (!response.ok) {
8418
8309
  throw new Error(`Failed to save form (${response.status}).`);
8419
8310
  }
8420
- const nextDoc = await response.json();
8311
+ const nextDoc = normalizeFormResponse(await response.json());
8421
8312
  setDoc(nextDoc);
8422
8313
  setEditorState(toEditorState(nextDoc));
8423
8314
  setSavedMessage("Saved.");
@@ -8447,8 +8338,52 @@ function AdminStudioFormDetailView(props) {
8447
8338
  const toneStyle = getFormToneStyle2(slug, title || formID || "form");
8448
8339
  const fieldLabels = doc ? buildFieldLabelMap2(doc) : /* @__PURE__ */ new Map();
8449
8340
  const latestSubmission = submissions[0];
8450
- const stepCount = doc && Array.isArray(doc.steps) ? doc.steps.length : 0;
8451
- const fieldCount = doc ? getFieldCount2(doc) : 0;
8341
+ const stepCount = editorState?.steps.length || 0;
8342
+ const fieldCount = editorState ? editorState.steps.reduce((count, step) => count + getFields(step).length, 0) : doc ? getFieldCount2(doc) : 0;
8343
+ const updateStep = (stepIndex, patch) => {
8344
+ setEditorState(
8345
+ (current) => current ? {
8346
+ ...current,
8347
+ steps: current.steps.map(
8348
+ (step, index) => index === stepIndex ? { ...step, ...patch } : step
8349
+ )
8350
+ } : current
8351
+ );
8352
+ };
8353
+ const updateField = (stepIndex, fieldIndex, patch) => {
8354
+ setEditorState((current) => {
8355
+ if (!current) return current;
8356
+ return {
8357
+ ...current,
8358
+ steps: current.steps.map((step, index) => {
8359
+ if (index !== stepIndex) return step;
8360
+ const fields = getFields(step).map(
8361
+ (field, currentFieldIndex) => currentFieldIndex === fieldIndex ? { ...field, ...patch } : field
8362
+ );
8363
+ return { ...step, fields };
8364
+ })
8365
+ };
8366
+ });
8367
+ };
8368
+ const moveStep = (stepIndex, direction) => {
8369
+ setEditorState(
8370
+ (current) => current ? {
8371
+ ...current,
8372
+ steps: moveItem(current.steps, stepIndex, stepIndex + direction)
8373
+ } : current
8374
+ );
8375
+ };
8376
+ const moveField = (stepIndex, fieldIndex, direction) => {
8377
+ setEditorState((current) => {
8378
+ if (!current) return current;
8379
+ return {
8380
+ ...current,
8381
+ steps: current.steps.map(
8382
+ (step, index) => index === stepIndex ? { ...step, fields: moveItem(getFields(step), fieldIndex, fieldIndex + direction) } : step
8383
+ )
8384
+ };
8385
+ });
8386
+ };
8452
8387
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(StudioSectionLayout, { navProps: props, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
8453
8388
  AdminPage,
8454
8389
  {
@@ -8527,10 +8462,9 @@ function AdminStudioFormDetailView(props) {
8527
8462
  /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { style: { display: "grid", gap: "1rem" }, children: [
8528
8463
  /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "orion-admin-card", children: [
8529
8464
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("strong", { children: "Step preview" }),
8530
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { children: "Review the configured workflow structure before editing the JSON source." }),
8531
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { style: { display: "grid", gap: "0.85rem", marginTop: "1rem" }, children: Array.isArray(doc.steps) && doc.steps.length > 0 ? doc.steps.map((step, stepIndex) => {
8532
- const record = step && typeof step === "object" && !Array.isArray(step) ? step : null;
8533
- const fields = Array.isArray(record?.fields) ? record.fields : [];
8465
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { children: "Review the public workflow as visitors will move through it." }),
8466
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { style: { display: "grid", gap: "0.85rem", marginTop: "1rem" }, children: editorState.steps.length > 0 ? editorState.steps.map((record, stepIndex) => {
8467
+ const fields = getFields(record);
8534
8468
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "orion-admin-form", children: [
8535
8469
  /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { children: [
8536
8470
  /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("strong", { children: [
@@ -8582,7 +8516,7 @@ function AdminStudioFormDetailView(props) {
8582
8516
  ] }, `step-${stepIndex}`);
8583
8517
  }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "orion-admin-empty-state", children: [
8584
8518
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("strong", { children: "No steps configured" }),
8585
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { children: "Add at least one step in the JSON editor to publish this form." })
8519
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { children: "Add at least one step in Form settings to publish this form." })
8586
8520
  ] }) })
8587
8521
  ] }),
8588
8522
  /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "orion-admin-card", children: [
@@ -8764,32 +8698,296 @@ function AdminStudioFormDetailView(props) {
8764
8698
  ] })
8765
8699
  ] })
8766
8700
  ] }),
8767
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("label", { children: [
8768
- "Structure JSON",
8769
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8770
- "textarea",
8771
- {
8772
- onChange: (event) => setEditorState(
8773
- (current) => current ? { ...current, stepsText: event.target.value } : current
8774
- ),
8775
- rows: 18,
8776
- spellCheck: false,
8777
- style: {
8778
- fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
8779
- minHeight: "24rem"
8780
- },
8781
- value: editorState.stepsText
8782
- }
8783
- )
8701
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "orion-admin-card orion-admin-workflow-editor", children: [
8702
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "orion-admin-workflow-editor-header", children: [
8703
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { children: [
8704
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("strong", { children: "Form steps" }),
8705
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { children: "Build the visitor flow, then edit each step's fields below." })
8706
+ ] }),
8707
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8708
+ "button",
8709
+ {
8710
+ className: "orion-admin-action-button orion-admin-action-button--ghost",
8711
+ onClick: () => setEditorState(
8712
+ (current) => current ? { ...current, steps: [...current.steps, blankStep()] } : current
8713
+ ),
8714
+ type: "button",
8715
+ children: "Add step"
8716
+ }
8717
+ )
8718
+ ] }),
8719
+ editorState.steps.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "orion-admin-empty-state", children: [
8720
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("strong", { children: "No steps configured" }),
8721
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { children: "Add a first step to start building the public form flow." })
8722
+ ] }) : null,
8723
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "orion-admin-step-editor-list", children: editorState.steps.map((step, stepIndex) => {
8724
+ const fields = getFields(step);
8725
+ const requiredCount = fields.filter((field) => field.required === true).length;
8726
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("details", { className: "orion-admin-step-editor", open: true, children: [
8727
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("summary", { className: "orion-admin-step-editor-summary", children: [
8728
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "orion-admin-workflow-node-number", children: stepIndex + 1 }),
8729
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "orion-admin-step-editor-title", children: getStepTitle(step, stepIndex) }),
8730
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "orion-admin-step-editor-meta", children: [
8731
+ fields.length,
8732
+ " field",
8733
+ fields.length === 1 ? "" : "s",
8734
+ requiredCount > 0 ? ` \xB7 ${requiredCount} required` : ""
8735
+ ] }),
8736
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "orion-admin-workflow-node-destination", children: getStepDestination(stepIndex, editorState.steps.length) })
8737
+ ] }),
8738
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "orion-admin-step-editor-body", children: [
8739
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "orion-admin-step-editor-actions", children: [
8740
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8741
+ "button",
8742
+ {
8743
+ className: "orion-admin-action-button orion-admin-action-button--ghost",
8744
+ disabled: stepIndex === 0,
8745
+ onClick: () => moveStep(stepIndex, -1),
8746
+ type: "button",
8747
+ children: "Move up"
8748
+ }
8749
+ ),
8750
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8751
+ "button",
8752
+ {
8753
+ className: "orion-admin-action-button orion-admin-action-button--ghost",
8754
+ disabled: stepIndex === editorState.steps.length - 1,
8755
+ onClick: () => moveStep(stepIndex, 1),
8756
+ type: "button",
8757
+ children: "Move down"
8758
+ }
8759
+ ),
8760
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8761
+ "button",
8762
+ {
8763
+ className: "orion-admin-action-button orion-admin-action-button--ghost",
8764
+ onClick: () => setEditorState(
8765
+ (current) => current ? {
8766
+ ...current,
8767
+ steps: current.steps.filter((_, index) => index !== stepIndex)
8768
+ } : current
8769
+ ),
8770
+ type: "button",
8771
+ children: "Remove step"
8772
+ }
8773
+ )
8774
+ ] }),
8775
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "orion-admin-step-settings-grid", children: [
8776
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("label", { children: [
8777
+ "Step title",
8778
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8779
+ "input",
8780
+ {
8781
+ onChange: (event) => updateStep(stepIndex, { title: event.target.value }),
8782
+ type: "text",
8783
+ value: getNonEmptyText(step.title)
8784
+ }
8785
+ )
8786
+ ] }),
8787
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("label", { children: [
8788
+ "Next button label",
8789
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8790
+ "input",
8791
+ {
8792
+ onChange: (event) => updateStep(stepIndex, { nextLabel: event.target.value }),
8793
+ placeholder: "Next",
8794
+ type: "text",
8795
+ value: getNonEmptyText(step.nextLabel)
8796
+ }
8797
+ )
8798
+ ] })
8799
+ ] }),
8800
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("label", { children: [
8801
+ "Step intro text",
8802
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8803
+ "textarea",
8804
+ {
8805
+ onChange: (event) => updateStep(stepIndex, { subtitle: event.target.value }),
8806
+ rows: 3,
8807
+ value: getNonEmptyText(step.subtitle)
8808
+ }
8809
+ )
8810
+ ] }),
8811
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("label", { style: checkboxLabelStyle, children: [
8812
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8813
+ "input",
8814
+ {
8815
+ checked: step.allowSkip === true,
8816
+ onChange: (event) => updateStep(stepIndex, { allowSkip: event.target.checked }),
8817
+ type: "checkbox"
8818
+ }
8819
+ ),
8820
+ "Allow visitors to skip this step"
8821
+ ] }),
8822
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "orion-admin-field-list-header", children: [
8823
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { children: [
8824
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("strong", { children: "Fields in this step" }),
8825
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { children: "These appear together before the visitor moves to the next step." })
8826
+ ] }),
8827
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8828
+ "button",
8829
+ {
8830
+ className: "orion-admin-action-button orion-admin-action-button--ghost",
8831
+ onClick: () => setEditorState(
8832
+ (current) => current ? {
8833
+ ...current,
8834
+ steps: current.steps.map(
8835
+ (currentStep, index) => index === stepIndex ? { ...currentStep, fields: [...getFields(currentStep), blankField()] } : currentStep
8836
+ )
8837
+ } : current
8838
+ ),
8839
+ type: "button",
8840
+ children: "Add field"
8841
+ }
8842
+ )
8843
+ ] }),
8844
+ fields.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "orion-admin-field-editor-list", children: fields.map((field, fieldIndex) => {
8845
+ const label = getFieldLabel(field, fieldIndex);
8846
+ const fieldType = getNonEmptyText(field.type, "text");
8847
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("details", { className: "orion-admin-field-editor", children: [
8848
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("summary", { className: "orion-admin-field-editor-summary", children: [
8849
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "orion-admin-field-order", children: fieldIndex + 1 }),
8850
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "orion-admin-field-editor-title", children: label }),
8851
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "orion-admin-field-editor-meta", children: [
8852
+ formatFieldTypeLabel(fieldType),
8853
+ field.required === true ? " \xB7 Required" : ""
8854
+ ] })
8855
+ ] }),
8856
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "orion-admin-field-editor-body", children: [
8857
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "orion-admin-step-editor-actions", children: [
8858
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8859
+ "button",
8860
+ {
8861
+ className: "orion-admin-action-button orion-admin-action-button--ghost",
8862
+ disabled: fieldIndex === 0,
8863
+ onClick: () => moveField(stepIndex, fieldIndex, -1),
8864
+ type: "button",
8865
+ children: "Move up"
8866
+ }
8867
+ ),
8868
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8869
+ "button",
8870
+ {
8871
+ className: "orion-admin-action-button orion-admin-action-button--ghost",
8872
+ disabled: fieldIndex === fields.length - 1,
8873
+ onClick: () => moveField(stepIndex, fieldIndex, 1),
8874
+ type: "button",
8875
+ children: "Move down"
8876
+ }
8877
+ ),
8878
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8879
+ "button",
8880
+ {
8881
+ className: "orion-admin-action-button orion-admin-action-button--ghost",
8882
+ onClick: () => setEditorState((current) => {
8883
+ if (!current) return current;
8884
+ return {
8885
+ ...current,
8886
+ steps: current.steps.map(
8887
+ (currentStep, index) => index === stepIndex ? {
8888
+ ...currentStep,
8889
+ fields: getFields(currentStep).filter(
8890
+ (_, currentFieldIndex) => currentFieldIndex !== fieldIndex
8891
+ )
8892
+ } : currentStep
8893
+ )
8894
+ };
8895
+ }),
8896
+ type: "button",
8897
+ children: "Remove field"
8898
+ }
8899
+ )
8900
+ ] }),
8901
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "orion-admin-step-settings-grid", children: [
8902
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("label", { children: [
8903
+ "Field label",
8904
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8905
+ "input",
8906
+ {
8907
+ onChange: (event) => {
8908
+ const nextLabel = event.target.value;
8909
+ const currentName = getNonEmptyText(field.name);
8910
+ updateField(stepIndex, fieldIndex, {
8911
+ label: nextLabel,
8912
+ name: currentName || slugifyFieldName(nextLabel)
8913
+ });
8914
+ },
8915
+ type: "text",
8916
+ value: label
8917
+ }
8918
+ )
8919
+ ] }),
8920
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("label", { children: [
8921
+ "Field type",
8922
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8923
+ "select",
8924
+ {
8925
+ onChange: (event) => updateField(stepIndex, fieldIndex, { type: event.target.value }),
8926
+ value: fieldType,
8927
+ children: fieldTypeOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("option", { value: option, children: formatFieldTypeLabel(option) }, option))
8928
+ }
8929
+ )
8930
+ ] })
8931
+ ] }),
8932
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("label", { children: [
8933
+ "Field key",
8934
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8935
+ "input",
8936
+ {
8937
+ onChange: (event) => updateField(stepIndex, fieldIndex, { name: event.target.value }),
8938
+ type: "text",
8939
+ value: getNonEmptyText(field.name)
8940
+ }
8941
+ )
8942
+ ] }),
8943
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("label", { style: checkboxLabelStyle, children: [
8944
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8945
+ "input",
8946
+ {
8947
+ checked: field.required === true,
8948
+ onChange: (event) => updateField(stepIndex, fieldIndex, { required: event.target.checked }),
8949
+ type: "checkbox"
8950
+ }
8951
+ ),
8952
+ "Required"
8953
+ ] }),
8954
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("label", { children: [
8955
+ "Help text",
8956
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8957
+ "textarea",
8958
+ {
8959
+ onChange: (event) => updateField(stepIndex, fieldIndex, { helpText: event.target.value }),
8960
+ rows: 2,
8961
+ value: getNonEmptyText(field.helpText)
8962
+ }
8963
+ )
8964
+ ] }),
8965
+ ["select", "radio", "checkbox-group"].includes(fieldType) ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("label", { children: [
8966
+ "Options",
8967
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
8968
+ "textarea",
8969
+ {
8970
+ onChange: (event) => updateField(stepIndex, fieldIndex, {
8971
+ options: fromOptionsText(event.target.value)
8972
+ }),
8973
+ placeholder: "Option one\nOption two",
8974
+ rows: 4,
8975
+ value: toOptionsText(field.options)
8976
+ }
8977
+ )
8978
+ ] }) : null
8979
+ ] })
8980
+ ] }, `edit-field-${stepIndex}-${fieldIndex}`);
8981
+ }) }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "orion-admin-empty-state", children: [
8982
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("strong", { children: "No fields in this step" }),
8983
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { children: "Add a field so visitors have something to answer here." })
8984
+ ] })
8985
+ ] })
8986
+ ] }, `edit-step-${stepIndex}`);
8987
+ }) })
8784
8988
  ] }),
8785
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "orion-admin-list-meta", children: "Edit the public workflow as JSON. The saved payload must remain an array of step objects compatible with the form collection schema." }),
8786
8989
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("button", { disabled: saving, type: "submit", children: saving ? "Saving..." : "Save Form" })
8787
8990
  ] })
8788
- ] }),
8789
- /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "orion-admin-card", children: [
8790
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("strong", { children: "Current structure payload" }),
8791
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { children: "Reference snapshot of the form steps that will be written back on save." }),
8792
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("pre", { style: { ...codeStyle, marginTop: "1rem" }, children: editorState.stepsText })
8793
8991
  ] })
8794
8992
  ] }) : null
8795
8993
  ]
@@ -8801,25 +8999,17 @@ function AdminStudioFormDetailView(props) {
8801
8999
  var import_link5 = __toESM(require("next/link"));
8802
9000
  var import_react33 = require("react");
8803
9001
  var import_jsx_runtime42 = require("react/jsx-runtime");
8804
- var codeStyle2 = {
8805
- background: "color-mix(in srgb, var(--orion-admin-card-bg) 82%, black)",
8806
- border: "1px solid var(--orion-admin-card-border)",
8807
- borderRadius: "var(--orion-admin-radius-sm)",
8808
- color: "var(--orion-admin-text)",
8809
- fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
8810
- fontSize: "0.86rem",
8811
- lineHeight: 1.55,
8812
- margin: 0,
8813
- maxHeight: "28rem",
8814
- overflow: "auto",
8815
- padding: "0.9rem",
8816
- whiteSpace: "pre-wrap"
8817
- };
8818
9002
  var sectionGridStyle2 = {
8819
9003
  display: "grid",
8820
9004
  gap: "1rem",
8821
9005
  gridTemplateColumns: "repeat(auto-fit, minmax(320px, 1fr))"
8822
9006
  };
9007
+ var nestedListStyle = {
9008
+ display: "grid",
9009
+ gap: "0.45rem",
9010
+ margin: 0,
9011
+ padding: 0
9012
+ };
8823
9013
  var renderFieldValue = (value) => {
8824
9014
  if (value === null || value === void 0) {
8825
9015
  return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "orion-admin-list-meta", children: "No value" });
@@ -8833,7 +9023,26 @@ var renderFieldValue = (value) => {
8833
9023
  if (typeof value === "string") {
8834
9024
  return value.trim().length > 0 ? value : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "orion-admin-list-meta", children: "No value" });
8835
9025
  }
8836
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("pre", { style: codeStyle2, children: JSON.stringify(value, null, 2) });
9026
+ if (Array.isArray(value)) {
9027
+ const entries = value.filter((entry) => entry !== null && entry !== void 0 && entry !== "");
9028
+ if (entries.length === 0) {
9029
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "orion-admin-list-meta", children: "No value" });
9030
+ }
9031
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("ul", { style: nestedListStyle, children: entries.map((entry, index) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("li", { children: renderFieldValue(entry) }, index)) });
9032
+ }
9033
+ if (typeof value === "object") {
9034
+ const entries = Object.entries(value).filter(
9035
+ ([, entryValue]) => entryValue !== null && entryValue !== void 0 && entryValue !== ""
9036
+ );
9037
+ if (entries.length === 0) {
9038
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "orion-admin-list-meta", children: "No value" });
9039
+ }
9040
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { style: { display: "grid", gap: "0.65rem" }, children: entries.map(([key, entryValue]) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "orion-admin-meta-row", children: [
9041
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "orion-admin-meta-label", children: humanizeKey2(key) }),
9042
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "orion-admin-meta-value", children: renderFieldValue(entryValue) })
9043
+ ] }, key)) });
9044
+ }
9045
+ return String(value);
8837
9046
  };
8838
9047
  function getSubmissionIDFromPathname(pathname) {
8839
9048
  return getIDFromPathname(pathname, "/forms/submissions/");
@@ -8985,31 +9194,24 @@ function AdminStudioFormSubmissionView(props) {
8985
9194
  /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("article", { className: "orion-admin-overview-stat", children: [
8986
9195
  /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "orion-admin-overview-stat-label", children: "Highlighted answers" }),
8987
9196
  /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("strong", { children: previewFields.length }),
8988
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { children: "Readable answer previews surfaced from the structured payload." })
9197
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { children: "Readable answer previews from this response." })
8989
9198
  ] })
8990
9199
  ] }),
8991
9200
  /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { style: sectionGridStyle2, children: [
8992
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { style: { display: "grid", gap: "1rem" }, children: [
8993
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "orion-admin-card", children: [
8994
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("strong", { children: "Response details" }),
8995
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { children: "Formatted answers using the current form field labels when available." }),
8996
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { style: { display: "grid", gap: "0.85rem", marginTop: "1rem" }, children: answerEntries.length > 0 ? answerEntries.map(([key, value]) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "orion-admin-form", children: [
8997
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { children: [
8998
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("strong", { children: fieldLabels.get(key) || humanizeKey2(key) }),
8999
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "orion-admin-list-meta", children: key })
9000
- ] }),
9001
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { children: renderFieldValue(value) })
9002
- ] }, key)) : /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "orion-admin-empty-state", children: [
9003
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("strong", { children: "No response payload" }),
9004
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { children: "This submission does not contain a structured data object." })
9005
- ] }) })
9006
- ] }),
9007
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "orion-admin-card", children: [
9008
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("strong", { children: "Raw payload" }),
9009
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { children: "Verbatim submission data for troubleshooting or export checks." }),
9010
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("pre", { style: { ...codeStyle2, marginTop: "1rem" }, children: JSON.stringify(doc.data ?? {}, null, 2) })
9011
- ] })
9012
- ] }),
9201
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { style: { display: "grid", gap: "1rem" }, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "orion-admin-card", children: [
9202
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("strong", { children: "Response details" }),
9203
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { children: "Formatted answers using the current form field labels when available." }),
9204
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { style: { display: "grid", gap: "0.85rem", marginTop: "1rem" }, children: answerEntries.length > 0 ? answerEntries.map(([key, value]) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "orion-admin-form", children: [
9205
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { children: [
9206
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("strong", { children: fieldLabels.get(key) || humanizeKey2(key) }),
9207
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "orion-admin-list-meta", children: key })
9208
+ ] }),
9209
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { children: renderFieldValue(value) })
9210
+ ] }, key)) : /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "orion-admin-empty-state", children: [
9211
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("strong", { children: "No answers available" }),
9212
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { children: "This submission does not contain visible response details." })
9213
+ ] }) })
9214
+ ] }) }),
9013
9215
  /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { style: { display: "grid", gap: "1rem" }, children: [
9014
9216
  /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "orion-admin-card orion-admin-meta-table", children: [
9015
9217
  /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "orion-admin-meta-row", children: [
@@ -9299,7 +9501,7 @@ var import_react35 = require("react");
9299
9501
  var import_ui12 = require("@payloadcms/ui");
9300
9502
  var import_jsx_runtime44 = require("react/jsx-runtime");
9301
9503
  var userRoles = ["admin", "developer", "editor", "client"];
9302
- var hasAdminAccess4 = (user) => {
9504
+ var hasAdminAccess3 = (user) => {
9303
9505
  if (!user || typeof user !== "object") return false;
9304
9506
  const role = user.role;
9305
9507
  return typeof role === "string" && (role === "admin" || role === "developer");
@@ -9314,7 +9516,7 @@ function AdminStudioToolsView(props) {
9314
9516
  const [savedMessage, setSavedMessage] = (0, import_react35.useState)(null);
9315
9517
  const [createSubmitting, setCreateSubmitting] = (0, import_react35.useState)(false);
9316
9518
  const [updatingUserID, setUpdatingUserID] = (0, import_react35.useState)(null);
9317
- if (!hasAdminAccess4(user)) {
9519
+ if (!hasAdminAccess3(user)) {
9318
9520
  return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(StudioSectionLayout, { navProps: props, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
9319
9521
  AdminPage,
9320
9522
  {