@orion-studios/payload-studio 0.6.0-beta.6 → 0.6.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -42,7 +42,6 @@ module.exports = __toCommonJS(index_exports);
42
42
  // src/admin/index.ts
43
43
  var admin_exports = {};
44
44
  __export(admin_exports, {
45
- AdminStudioDashboard: () => AdminStudioDashboard,
46
45
  SOCIAL_MEDIA_DEFAULT_ICON_BY_PLATFORM: () => SOCIAL_MEDIA_DEFAULT_ICON_BY_PLATFORM,
47
46
  SOCIAL_MEDIA_ICON_OPTIONS: () => SOCIAL_MEDIA_ICON_OPTIONS,
48
47
  SOCIAL_MEDIA_PLATFORMS: () => SOCIAL_MEDIA_PLATFORMS,
@@ -110,7 +109,6 @@ var navItemIsActive = (pathname, item) => {
110
109
  // src/shared/studioSections.ts
111
110
  var studioRoles = /* @__PURE__ */ new Set(["admin", "editor", "client"]);
112
111
  var studioIcons = new Set(adminNavIcons);
113
- var dashboardSpans = /* @__PURE__ */ new Set(["full", "half"]);
114
112
  var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
115
113
  var isAbsoluteExternalURL = (value) => /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(value) || value.startsWith("//");
116
114
  var normalizePathLikeValue = (value) => {
@@ -152,22 +150,6 @@ var normalizeCard = (value) => {
152
150
  };
153
151
  };
154
152
  var normalizeIcon = (value) => typeof value === "string" && studioIcons.has(value) ? value : void 0;
155
- var normalizeComponent = (value) => {
156
- if (!isRecord(value)) {
157
- return void 0;
158
- }
159
- const componentPath = typeof value.path === "string" ? value.path.trim() : "";
160
- const exportName = typeof value.exportName === "string" ? value.exportName.trim() : "";
161
- if (!componentPath || !exportName) {
162
- return void 0;
163
- }
164
- return {
165
- exportName,
166
- path: componentPath,
167
- ...isRecord(value.clientProps) ? { clientProps: value.clientProps } : {}
168
- };
169
- };
170
- var normalizeDashboardSpan = (value) => typeof value === "string" && dashboardSpans.has(value) ? value : "half";
171
153
  var resolveStudioSections = (value) => {
172
154
  if (!Array.isArray(value)) {
173
155
  return [];
@@ -233,38 +215,6 @@ var resolveStudioSectionViews = (value) => {
233
215
  }
234
216
  return views;
235
217
  };
236
- var resolveStudioSectionDashboards = (value) => {
237
- if (!Array.isArray(value)) {
238
- return [];
239
- }
240
- const panels = [];
241
- const seen = /* @__PURE__ */ new Set();
242
- for (const entry of value) {
243
- if (!isRecord(entry) || typeof entry.id !== "string" || typeof entry.label !== "string") {
244
- continue;
245
- }
246
- const id = entry.id.trim();
247
- const label = entry.label.trim();
248
- const dashboard = isRecord(entry.dashboard) ? entry.dashboard : null;
249
- const component = dashboard ? normalizeComponent(dashboard.Component) : void 0;
250
- const href = typeof entry.href === "string" && entry.href.trim().length > 0 ? normalizePathLikeValue(entry.href) : isRecord(entry.view) && typeof entry.view.path === "string" ? normalizePathLikeValue(entry.view.path) : "";
251
- if (!id || !label || !dashboard || !component || !href || seen.has(id)) {
252
- continue;
253
- }
254
- const priority = typeof dashboard.priority === "number" && Number.isFinite(dashboard.priority) ? dashboard.priority : 100;
255
- panels.push({
256
- Component: component,
257
- href,
258
- id,
259
- label,
260
- priority,
261
- ...normalizeRoles(dashboard.roles ?? entry.roles) ? { roles: normalizeRoles(dashboard.roles ?? entry.roles) } : {},
262
- span: normalizeDashboardSpan(dashboard.span)
263
- });
264
- seen.add(id);
265
- }
266
- return panels.sort((a, b) => a.priority === b.priority ? a.label.localeCompare(b.label) : a.priority - b.priority);
267
- };
268
218
 
269
219
  // src/admin/helpers/configureAdmin.ts
270
220
  var import_meta = {};
@@ -324,7 +274,6 @@ function configureAdmin(config) {
324
274
  };
325
275
  });
326
276
  const studioSections = resolveStudioSections(config.studio?.sections || []);
327
- const studioDashboardPanels = resolveStudioSectionDashboards(config.studio?.sections || []);
328
277
  const studioSectionViews = resolveStudioSectionViews(config.studio?.sections || []);
329
278
  const sitePreview = config.studio?.sitePreview;
330
279
  let cssPath;
@@ -354,7 +303,6 @@ function configureAdmin(config) {
354
303
  cssPath = genPath;
355
304
  }
356
305
  const clientPath = "@orion-studios/payload-studio/admin/client";
357
- const adminPath = "@orion-studios/payload-studio/admin";
358
306
  const studioNavClientProps = {
359
307
  brandName,
360
308
  formSubmissionsCollectionSlug,
@@ -366,14 +314,8 @@ function configureAdmin(config) {
366
314
  logoUrl,
367
315
  mediaCollectionSlug,
368
316
  pagesCollectionSlug,
369
- dashboardPanels: studioDashboardPanels,
370
317
  sections: studioSections
371
318
  };
372
- const dashboardImportMapGenerator = studioDashboardPanels.length > 0 ? ({ addToImportMap }) => {
373
- addToImportMap(
374
- studioDashboardPanels.map((panel) => panel.Component)
375
- );
376
- } : void 0;
377
319
  const studioBackBreadcrumbComponent = {
378
320
  exportName: "StudioBackBreadcrumb",
379
321
  path: clientPath
@@ -408,11 +350,6 @@ function configureAdmin(config) {
408
350
  return {
409
351
  admin: {
410
352
  css: cssPath,
411
- ...dashboardImportMapGenerator ? {
412
- importMap: {
413
- generators: [dashboardImportMapGenerator]
414
- }
415
- } : {},
416
353
  components: {
417
354
  ...studioEnabled ? {
418
355
  Nav: {
@@ -443,7 +380,7 @@ function configureAdmin(config) {
443
380
  dashboard: {
444
381
  Component: {
445
382
  exportName: studioEnabled ? "AdminStudioDashboard" : "Dashboard",
446
- path: studioEnabled ? adminPath : clientPath,
383
+ path: clientPath,
447
384
  clientProps: studioNavClientProps
448
385
  }
449
386
  },
@@ -839,1339 +776,6 @@ function configureAdmin(config) {
839
776
  };
840
777
  }
841
778
 
842
- // src/admin-app/components/AdminBreadcrumbs.tsx
843
- var import_jsx_runtime = require("react/jsx-runtime");
844
- function AdminBreadcrumbs({ items }) {
845
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("nav", { "aria-label": "Breadcrumb", className: "orion-admin-breadcrumbs", children: items.map((item, index) => {
846
- const isLast = index === items.length - 1;
847
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
848
- item.href && !isLast ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: item.href, children: item.label }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: item.label }),
849
- !isLast ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "orion-admin-breadcrumb-sep", children: "/" }) : null
850
- ] }, `${item.label}-${index}`);
851
- }) });
852
- }
853
-
854
- // src/admin-app/components/AdminPage.tsx
855
- var import_jsx_runtime2 = require("react/jsx-runtime");
856
- function AdminPage({ title, description, breadcrumbs, actions, children }) {
857
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "orion-admin-page", children: [
858
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "orion-admin-page-header", children: [
859
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AdminBreadcrumbs, { items: breadcrumbs }),
860
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "orion-admin-page-title-row", children: [
861
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
862
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { children: title }),
863
- description ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: description }) : null
864
- ] }),
865
- actions ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "orion-admin-page-actions", children: actions }) : null
866
- ] })
867
- ] }),
868
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "orion-admin-page-content", children })
869
- ] });
870
- }
871
-
872
- // src/admin/components/studio/AdminStudioDashboardClient.tsx
873
- var import_react = require("react");
874
- var import_link = __toESM(require("next/link"));
875
- var import_jsx_runtime3 = require("react/jsx-runtime");
876
- var SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1e3;
877
- var isRole = (value) => value === "admin" || value === "editor" || value === "client";
878
- var canReviewForms = (role) => role === "admin" || role === "editor";
879
- var canCreatePages = (role) => role === "admin" || role === "editor";
880
- var canAccess = (role, roles) => {
881
- if (!roles || roles.length === 0) {
882
- return true;
883
- }
884
- if (!role) {
885
- return false;
886
- }
887
- return roles.includes(role);
888
- };
889
- var asText = (value, fallback) => typeof value === "string" && value.trim().length > 0 ? value.trim() : fallback;
890
- var asID = (value) => {
891
- if (typeof value === "string" || typeof value === "number") return String(value);
892
- return "";
893
- };
894
- var toTimestamp = (value) => {
895
- if (typeof value !== "string" || value.length === 0) {
896
- return Number.NaN;
897
- }
898
- const timestamp = Date.parse(value);
899
- return Number.isFinite(timestamp) ? timestamp : Number.NaN;
900
- };
901
- var isRecent = (value) => {
902
- const timestamp = toTimestamp(value);
903
- return Number.isFinite(timestamp) && Date.now() - timestamp <= SEVEN_DAYS_MS;
904
- };
905
- var formatDateTime = (value) => {
906
- if (typeof value !== "string" || value.length === 0) {
907
- return "Unknown time";
908
- }
909
- const date = new Date(value);
910
- if (Number.isNaN(date.getTime())) {
911
- return value;
912
- }
913
- return new Intl.DateTimeFormat(void 0, {
914
- dateStyle: "medium",
915
- timeStyle: "short"
916
- }).format(date);
917
- };
918
- var formatRelativeTime = (timestamp) => {
919
- if (!Number.isFinite(timestamp)) {
920
- return "Unknown time";
921
- }
922
- const diffMs = timestamp - Date.now();
923
- const diffMinutes = Math.round(diffMs / (60 * 1e3));
924
- const rtf = new Intl.RelativeTimeFormat(void 0, { numeric: "auto" });
925
- if (Math.abs(diffMinutes) < 60) {
926
- return rtf.format(diffMinutes, "minute");
927
- }
928
- const diffHours = Math.round(diffMinutes / 60);
929
- if (Math.abs(diffHours) < 24) {
930
- return rtf.format(diffHours, "hour");
931
- }
932
- const diffDays = Math.round(diffHours / 24);
933
- return rtf.format(diffDays, "day");
934
- };
935
- var readSubmissionIdentity = (value) => {
936
- if (!value || typeof value !== "object") {
937
- return "New submission";
938
- }
939
- const data = value;
940
- const firstName = typeof data.firstName === "string" ? data.firstName.trim() : "";
941
- const lastName = typeof data.lastName === "string" ? data.lastName.trim() : "";
942
- const name = typeof data.name === "string" ? data.name.trim() : "";
943
- const email = typeof data.email === "string" ? data.email.trim() : typeof data.contactEmail === "string" ? data.contactEmail.trim() : "";
944
- const fullName = [firstName, lastName].filter(Boolean).join(" ").trim();
945
- return fullName || name || email || "New submission";
946
- };
947
- var getFormID = (value) => {
948
- if (typeof value === "string" || typeof value === "number") return String(value);
949
- if (value && typeof value === "object") {
950
- const nestedID = value.id;
951
- if (typeof nestedID === "string" || typeof nestedID === "number") return String(nestedID);
952
- }
953
- return "";
954
- };
955
- var getFormTitle = (value) => {
956
- if (!value || typeof value !== "object") {
957
- return "";
958
- }
959
- const title = value.title;
960
- if (typeof title === "string" && title.trim().length > 0) {
961
- return title.trim();
962
- }
963
- const slug = value.slug;
964
- return typeof slug === "string" && slug.trim().length > 0 ? slug.trim() : "";
965
- };
966
- var buildSearchParams = (params) => new URLSearchParams(
967
- Object.entries(params).filter(([, value]) => typeof value === "string" && value.length > 0)
968
- ).toString();
969
- async function loadCollection(path2) {
970
- const response = await fetch(path2, {
971
- cache: "no-store",
972
- credentials: "include"
973
- });
974
- if (!response.ok) {
975
- const body = await response.text();
976
- throw new Error(body || `Request failed: ${response.status}`);
977
- }
978
- return await response.json();
979
- }
980
- async function loadPages(collectionSlug) {
981
- const params = buildSearchParams({
982
- depth: "0",
983
- draft: "true",
984
- limit: "200",
985
- sort: "-updatedAt"
986
- });
987
- const result = await loadCollection(`/api/${collectionSlug}?${params}`);
988
- const docs = Array.isArray(result.docs) ? result.docs : [];
989
- return {
990
- draftCount: docs.filter((doc) => doc._status === "draft").length,
991
- recent: docs.slice(0, 8),
992
- total: typeof result.totalDocs === "number" ? result.totalDocs : docs.length,
993
- updatedThisWeek: docs.filter((doc) => isRecent(doc.updatedAt)).length
994
- };
995
- }
996
- async function loadForms(formsCollectionSlug, submissionsCollectionSlug) {
997
- const [formsResult, submissionsResult] = await Promise.all([
998
- loadCollection(
999
- `/api/${formsCollectionSlug}?${buildSearchParams({
1000
- depth: "0",
1001
- draft: "true",
1002
- limit: "80",
1003
- sort: "-updatedAt"
1004
- })}`
1005
- ),
1006
- loadCollection(
1007
- `/api/${submissionsCollectionSlug}?${buildSearchParams({
1008
- depth: "1",
1009
- limit: "40",
1010
- sort: "-submittedAt"
1011
- })}`
1012
- )
1013
- ]);
1014
- const forms = Array.isArray(formsResult.docs) ? formsResult.docs : [];
1015
- const recentSubmissions = Array.isArray(submissionsResult.docs) ? submissionsResult.docs : [];
1016
- const submissionsByForm = /* @__PURE__ */ new Map();
1017
- for (const submission of recentSubmissions) {
1018
- const formID = getFormID(submission.form);
1019
- if (!formID) continue;
1020
- submissionsByForm.set(formID, (submissionsByForm.get(formID) || 0) + 1);
1021
- }
1022
- let busiestFormTitle = null;
1023
- let busiestFormCount = 0;
1024
- for (const form of forms) {
1025
- const formID = asID(form.id);
1026
- if (!formID) continue;
1027
- const submissionCount = submissionsByForm.get(formID) || 0;
1028
- if (submissionCount > busiestFormCount) {
1029
- busiestFormCount = submissionCount;
1030
- busiestFormTitle = asText(form.title, "Untitled form");
1031
- }
1032
- }
1033
- return {
1034
- busiestFormTitle,
1035
- forms,
1036
- recentSubmissions,
1037
- submissionsThisWeek: recentSubmissions.filter((submission) => isRecent(submission.submittedAt)).length,
1038
- totalForms: typeof formsResult.totalDocs === "number" ? formsResult.totalDocs : forms.length
1039
- };
1040
- }
1041
- async function loadMedia(collectionSlug) {
1042
- const params = buildSearchParams({
1043
- depth: "0",
1044
- limit: "24",
1045
- sort: "-updatedAt"
1046
- });
1047
- const result = await loadCollection(`/api/${collectionSlug}?${params}`);
1048
- const docs = Array.isArray(result.docs) ? result.docs : [];
1049
- return {
1050
- recent: docs.slice(0, 8),
1051
- total: typeof result.totalDocs === "number" ? result.totalDocs : docs.length,
1052
- uploadsThisWeek: docs.filter((doc) => isRecent(doc.updatedAt)).length
1053
- };
1054
- }
1055
- var loadingState = {
1056
- forms: null,
1057
- media: { status: "loading" },
1058
- pages: { status: "loading" }
1059
- };
1060
- function ModuleStatus({ message }) {
1061
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "orion-dashboard-inline-note", children: message });
1062
- }
1063
- function EmptyState({
1064
- body,
1065
- title
1066
- }) {
1067
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "orion-dashboard-empty-state", children: [
1068
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: title }),
1069
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: body })
1070
- ] });
1071
- }
1072
- function SnapshotMetric({ card }) {
1073
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: "orion-dashboard-snapshot-card", children: [
1074
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "orion-dashboard-snapshot-kicker", children: card.kicker }),
1075
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: card.value }),
1076
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: card.detail })
1077
- ] });
1078
- }
1079
- function AdminStudioDashboardClient({
1080
- formSubmissionsCollectionSlug,
1081
- formsCollectionSlug,
1082
- formsEnabled,
1083
- formsPath,
1084
- globalsBasePath,
1085
- mediaCollectionSlug,
1086
- mediaPath,
1087
- pagesCollectionSlug,
1088
- pagesPath,
1089
- sectionLinks,
1090
- toolsPath,
1091
- userRole,
1092
- children
1093
- }) {
1094
- const role = isRole(userRole) ? userRole : void 0;
1095
- const [state, setState] = (0, import_react.useState)(
1096
- () => formsEnabled && canReviewForms(role) ? {
1097
- forms: { status: "loading" },
1098
- media: { status: "loading" },
1099
- pages: { status: "loading" }
1100
- } : loadingState
1101
- );
1102
- (0, import_react.useEffect)(() => {
1103
- let cancelled = false;
1104
- const run = async () => {
1105
- const includeForms = formsEnabled && canReviewForms(role);
1106
- (0, import_react.startTransition)(() => {
1107
- setState({
1108
- forms: includeForms ? { status: "loading" } : null,
1109
- media: { status: "loading" },
1110
- pages: { status: "loading" }
1111
- });
1112
- });
1113
- const [pagesResult, formsResult, mediaResult] = await Promise.allSettled([
1114
- loadPages(pagesCollectionSlug),
1115
- includeForms ? loadForms(formsCollectionSlug, formSubmissionsCollectionSlug) : Promise.resolve(null),
1116
- loadMedia(mediaCollectionSlug)
1117
- ]);
1118
- if (cancelled) return;
1119
- (0, import_react.startTransition)(() => {
1120
- setState({
1121
- forms: includeForms && formsResult.status === "rejected" ? {
1122
- error: formsResult.reason instanceof Error ? formsResult.reason.message : "Unable to load form activity.",
1123
- status: "error"
1124
- } : includeForms && formsResult.status === "fulfilled" && formsResult.value ? {
1125
- data: formsResult.value,
1126
- status: "success"
1127
- } : null,
1128
- media: mediaResult.status === "rejected" ? {
1129
- error: mediaResult.reason instanceof Error ? mediaResult.reason.message : "Unable to load media activity.",
1130
- status: "error"
1131
- } : {
1132
- data: mediaResult.value,
1133
- status: "success"
1134
- },
1135
- pages: pagesResult.status === "rejected" ? {
1136
- error: pagesResult.reason instanceof Error ? pagesResult.reason.message : "Unable to load page activity.",
1137
- status: "error"
1138
- } : {
1139
- data: pagesResult.value,
1140
- status: "success"
1141
- }
1142
- });
1143
- });
1144
- };
1145
- void run();
1146
- return () => {
1147
- cancelled = true;
1148
- };
1149
- }, [
1150
- formSubmissionsCollectionSlug,
1151
- formsCollectionSlug,
1152
- formsEnabled,
1153
- mediaCollectionSlug,
1154
- pagesCollectionSlug,
1155
- role
1156
- ]);
1157
- const visibleWorkspaceLinks = (0, import_react.useMemo)(
1158
- () => sectionLinks.filter((section) => canAccess(role, section.roles)),
1159
- [role, sectionLinks]
1160
- );
1161
- const activityItems = (0, import_react.useMemo)(() => {
1162
- const items = [];
1163
- if (state.pages.status === "success") {
1164
- for (const doc of state.pages.data.recent) {
1165
- const id = asID(doc.id);
1166
- if (!id) continue;
1167
- const timestamp = toTimestamp(doc.updatedAt);
1168
- items.push({
1169
- href: `${pagesPath}/${id}`,
1170
- id: `page-${id}`,
1171
- kind: "page",
1172
- label: typeof doc._status === "string" ? doc._status : "page",
1173
- meta: Number.isFinite(timestamp) ? formatDateTime(doc.updatedAt) : "Update time unavailable",
1174
- timestamp,
1175
- title: asText(doc.title, "Untitled page")
1176
- });
1177
- }
1178
- }
1179
- if (state.forms?.status === "success") {
1180
- for (const submission of state.forms.data.recentSubmissions) {
1181
- const id = asID(submission.id);
1182
- const formID = getFormID(submission.form);
1183
- if (!id || !formID) continue;
1184
- const timestamp = toTimestamp(submission.submittedAt);
1185
- const formTitle = getFormTitle(submission.form) || "Form response";
1186
- items.push({
1187
- href: `${formsPath}?form=${encodeURIComponent(formID)}`,
1188
- id: `submission-${id}`,
1189
- kind: "submission",
1190
- label: formTitle,
1191
- meta: Number.isFinite(timestamp) ? `${readSubmissionIdentity(submission.data)} \xB7 ${formatDateTime(submission.submittedAt)}` : readSubmissionIdentity(submission.data),
1192
- timestamp,
1193
- title: "New submission"
1194
- });
1195
- }
1196
- }
1197
- if (state.media.status === "success") {
1198
- for (const doc of state.media.data.recent) {
1199
- const id = asID(doc.id);
1200
- if (!id) continue;
1201
- const timestamp = toTimestamp(doc.updatedAt);
1202
- items.push({
1203
- href: `${mediaPath}/${id}`,
1204
- id: `media-${id}`,
1205
- kind: "media",
1206
- label: asText(doc.mimeType, "Media asset"),
1207
- meta: Number.isFinite(timestamp) ? formatDateTime(doc.updatedAt) : "Update time unavailable",
1208
- timestamp,
1209
- title: asText(doc.filename, "Untitled asset")
1210
- });
1211
- }
1212
- }
1213
- return items.filter((item) => Number.isFinite(item.timestamp)).sort((a, b) => b.timestamp - a.timestamp).slice(0, 10);
1214
- }, [formsPath, mediaPath, pagesPath, state.forms, state.media, state.pages]);
1215
- const attentionItems = (0, import_react.useMemo)(() => {
1216
- if (role === "client") {
1217
- const items2 = [];
1218
- if (state.pages.status === "success" && state.pages.data.updatedThisWeek > 0) {
1219
- items2.push({
1220
- href: pagesPath,
1221
- id: "pages-updated",
1222
- label: `${state.pages.data.updatedThisWeek} page${state.pages.data.updatedThisWeek === 1 ? "" : "s"} changed in the last 7 days.`
1223
- });
1224
- }
1225
- if (state.media.status === "success" && state.media.data.uploadsThisWeek > 0) {
1226
- items2.push({
1227
- href: mediaPath,
1228
- id: "media-updated",
1229
- label: `${state.media.data.uploadsThisWeek} new media asset${state.media.data.uploadsThisWeek === 1 ? "" : "s"} landed this week.`
1230
- });
1231
- }
1232
- return items2;
1233
- }
1234
- const items = [];
1235
- if (state.pages.status === "success" && state.pages.data.draftCount > 0) {
1236
- items.push({
1237
- href: pagesPath,
1238
- id: "draft-pages",
1239
- label: `${state.pages.data.draftCount} page${state.pages.data.draftCount === 1 ? "" : "s"} still need publishing review.`,
1240
- tone: "accent"
1241
- });
1242
- }
1243
- if (state.forms?.status === "success" && state.forms.data.submissionsThisWeek > 0) {
1244
- items.push({
1245
- href: formsPath,
1246
- id: "recent-submissions",
1247
- label: `${state.forms.data.submissionsThisWeek} form submission${state.forms.data.submissionsThisWeek === 1 ? "" : "s"} arrived in the last 7 days.`,
1248
- tone: "accent"
1249
- });
1250
- }
1251
- if (state.media.status === "success" && state.media.data.total === 0) {
1252
- items.push({
1253
- href: mediaPath,
1254
- id: "empty-media",
1255
- label: "The media library is still empty. Add brand assets before content expands."
1256
- });
1257
- }
1258
- if (state.pages.status === "error") {
1259
- items.push({
1260
- href: pagesPath,
1261
- id: "pages-error",
1262
- label: "Page activity could not be loaded for the dashboard.",
1263
- tone: "muted"
1264
- });
1265
- }
1266
- if (state.forms?.status === "error") {
1267
- items.push({
1268
- href: formsPath,
1269
- id: "forms-error",
1270
- label: "Form activity could not be loaded for the dashboard.",
1271
- tone: "muted"
1272
- });
1273
- }
1274
- if (state.media.status === "error") {
1275
- items.push({
1276
- href: mediaPath,
1277
- id: "media-error",
1278
- label: "Media activity could not be loaded for the dashboard.",
1279
- tone: "muted"
1280
- });
1281
- }
1282
- return items;
1283
- }, [formsPath, mediaPath, pagesPath, role, state.forms, state.media, state.pages]);
1284
- const snapshotCards = (0, import_react.useMemo)(() => {
1285
- const cards = [];
1286
- if (state.pages.status === "success") {
1287
- cards.push(
1288
- role === "client" ? {
1289
- detail: `${state.pages.data.updatedThisWeek} updated this week`,
1290
- kicker: "Pages",
1291
- value: `${state.pages.data.total}`
1292
- } : {
1293
- detail: `${state.pages.data.draftCount} draft${state.pages.data.draftCount === 1 ? "" : "s"} waiting`,
1294
- kicker: "Pages",
1295
- value: `${state.pages.data.total}`
1296
- }
1297
- );
1298
- }
1299
- if (state.forms?.status === "success") {
1300
- cards.push({
1301
- detail: state.forms.data.busiestFormTitle ? `Most active: ${state.forms.data.busiestFormTitle}` : "Waiting on the first submission",
1302
- kicker: "Forms",
1303
- value: `${state.forms.data.submissionsThisWeek}`
1304
- });
1305
- }
1306
- if (state.media.status === "success") {
1307
- cards.push({
1308
- detail: `${state.media.data.uploadsThisWeek} uploaded this week`,
1309
- kicker: "Media",
1310
- value: `${state.media.data.total}`
1311
- });
1312
- }
1313
- cards.push({
1314
- detail: `${visibleWorkspaceLinks.length} section${visibleWorkspaceLinks.length === 1 ? "" : "s"} available`,
1315
- kicker: "Workspace",
1316
- value: role ? role.toUpperCase() : "CMS"
1317
- });
1318
- return cards.slice(0, 4);
1319
- }, [role, state.forms, state.media, state.pages, visibleWorkspaceLinks.length]);
1320
- const primaryActions = (0, import_react.useMemo)(() => {
1321
- const actions = [];
1322
- if (canCreatePages(role)) {
1323
- actions.push({
1324
- description: "Create or revise content in the visual builder.",
1325
- href: `${pagesPath}/new`,
1326
- label: "New Page"
1327
- });
1328
- }
1329
- actions.push({
1330
- description: "Review live pages and recent edits.",
1331
- href: pagesPath,
1332
- label: "Open Pages",
1333
- tone: "ghost"
1334
- });
1335
- if (formsEnabled && canReviewForms(role)) {
1336
- actions.push({
1337
- description: "Check submissions and form performance.",
1338
- href: formsPath,
1339
- label: "Review Forms",
1340
- tone: "soft"
1341
- });
1342
- }
1343
- actions.push({
1344
- description: "Update site settings, navigation, and footer content.",
1345
- href: globalsBasePath,
1346
- label: "Open Globals",
1347
- tone: "ghost"
1348
- });
1349
- actions.push({
1350
- description: "Upload or organize brand and campaign assets.",
1351
- href: mediaPath,
1352
- label: "Manage Media",
1353
- tone: "ghost"
1354
- });
1355
- if (role === "admin") {
1356
- actions.push({
1357
- description: "Manage users, roles, and fallback tools.",
1358
- href: toolsPath,
1359
- label: "Admin Tools",
1360
- tone: "ghost"
1361
- });
1362
- }
1363
- return actions;
1364
- }, [formsEnabled, formsPath, globalsBasePath, mediaPath, pagesPath, role, toolsPath]);
1365
- const attentionTitle = role === "client" ? "What Changed" : "Needs Attention";
1366
- const attentionDescription = role === "client" ? "A quick read on recent content and asset changes." : "The highest-signal items that still need review.";
1367
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "orion-dashboard-layout", children: [
1368
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "orion-dashboard-panel orion-dashboard-panel--attention", children: [
1369
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "orion-dashboard-panel-header", children: [
1370
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
1371
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "orion-dashboard-panel-kicker", children: attentionTitle }),
1372
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: attentionDescription })
1373
- ] }),
1374
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "orion-dashboard-panel-meta", children: "Last 7 days" })
1375
- ] }),
1376
- attentionItems.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "orion-dashboard-attention-list", children: attentionItems.map(
1377
- (item) => item.href ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1378
- import_link.default,
1379
- {
1380
- className: ["orion-dashboard-attention-item", item.tone ? `is-${item.tone}` : ""].filter(Boolean).join(" "),
1381
- href: item.href,
1382
- children: [
1383
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: item.label }),
1384
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "Open" })
1385
- ]
1386
- },
1387
- item.id
1388
- ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1389
- "div",
1390
- {
1391
- className: ["orion-dashboard-attention-item", item.tone ? `is-${item.tone}` : ""].filter(Boolean).join(" "),
1392
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: item.label })
1393
- },
1394
- item.id
1395
- )
1396
- ) }) : state.pages.status === "loading" || state.media.status === "loading" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ModuleStatus, { message: "Loading attention items..." }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1397
- EmptyState,
1398
- {
1399
- body: role === "client" ? "No major changes landed during the current review window." : "No urgent follow-up surfaced from content, forms, or media activity.",
1400
- title: "Everything looks steady"
1401
- }
1402
- )
1403
- ] }),
1404
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "orion-dashboard-panel orion-dashboard-panel--actions", children: [
1405
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "orion-dashboard-panel-header", children: [
1406
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
1407
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "orion-dashboard-panel-kicker", children: "Quick Actions" }),
1408
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: "Jump into the work that matters most." })
1409
- ] }),
1410
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "orion-dashboard-panel-meta", children: [
1411
- role || "studio",
1412
- " mode"
1413
- ] })
1414
- ] }),
1415
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "orion-dashboard-action-list", children: primaryActions.map((action) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1416
- import_link.default,
1417
- {
1418
- className: [
1419
- "orion-dashboard-action",
1420
- action.tone === "ghost" ? "is-ghost" : action.tone === "soft" ? "is-soft" : ""
1421
- ].filter(Boolean).join(" "),
1422
- href: action.href,
1423
- children: [
1424
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: action.label }),
1425
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: action.description })
1426
- ]
1427
- },
1428
- action.label
1429
- )) }),
1430
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "orion-dashboard-workspace-strip", children: [
1431
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "orion-dashboard-workspace-label", children: "Workspace" }),
1432
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "orion-dashboard-workspace-pills", children: visibleWorkspaceLinks.map((section) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_link.default, { className: "orion-dashboard-workspace-pill", href: section.href, children: section.label }, section.id)) })
1433
- ] })
1434
- ] }),
1435
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "orion-dashboard-panel orion-dashboard-panel--activity", children: [
1436
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "orion-dashboard-panel-header", children: [
1437
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
1438
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "orion-dashboard-panel-kicker", children: "Recent Activity" }),
1439
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: "The freshest edits, responses, and uploads across the studio." })
1440
- ] }),
1441
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "orion-dashboard-panel-meta", children: [
1442
- activityItems.length,
1443
- " items"
1444
- ] })
1445
- ] }),
1446
- activityItems.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "orion-dashboard-activity-list", children: activityItems.map((item) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_link.default, { className: `orion-dashboard-activity-item is-${item.kind}`, href: item.href, children: [
1447
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "orion-dashboard-activity-copy", children: [
1448
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "orion-dashboard-activity-topline", children: [
1449
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: item.title }),
1450
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: formatRelativeTime(item.timestamp) })
1451
- ] }),
1452
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: item.meta })
1453
- ] }),
1454
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "orion-dashboard-activity-pill", children: item.label })
1455
- ] }, item.id)) }) : state.pages.status === "loading" || state.media.status === "loading" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ModuleStatus, { message: "Loading activity feed..." }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1456
- EmptyState,
1457
- {
1458
- body: "Recent page edits, submissions, and uploads will start stacking here as soon as the team gets moving.",
1459
- title: "No recent activity yet"
1460
- }
1461
- )
1462
- ] }),
1463
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "orion-dashboard-panel orion-dashboard-panel--snapshot", children: [
1464
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "orion-dashboard-panel-header", children: [
1465
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { children: [
1466
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "orion-dashboard-panel-kicker", children: "Business Snapshot" }),
1467
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { children: "A compact read on content momentum and performance." })
1468
- ] }),
1469
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "orion-dashboard-panel-meta", children: "30-day lens" })
1470
- ] }),
1471
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "orion-dashboard-snapshot-grid", children: [
1472
- snapshotCards.map((card) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SnapshotMetric, { card }, card.kicker)),
1473
- children
1474
- ] }),
1475
- state.pages.status === "error" || state.media.status === "error" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "orion-dashboard-inline-note", children: "Some dashboard modules could not be loaded. The rest of the workspace remains available." }) : null
1476
- ] })
1477
- ] });
1478
- }
1479
-
1480
- // src/admin/components/studio/StudioSectionLayout.tsx
1481
- var import_react5 = require("react");
1482
- var import_navigation = require("next/navigation");
1483
- var import_ui = require("@payloadcms/ui");
1484
-
1485
- // src/admin-app/components/AdminShellClient.tsx
1486
- var import_react2 = require("react");
1487
- var import_jsx_runtime4 = require("react/jsx-runtime");
1488
- var iconSize = 20;
1489
- var iconStyle = { display: "block", flexShrink: 0 };
1490
- function NavIcon({ name }) {
1491
- const props = { width: iconSize, height: iconSize, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", style: iconStyle };
1492
- switch (name) {
1493
- case "dashboard":
1494
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { ...props, children: [
1495
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "3", y: "3", width: "7", height: "9", rx: "1" }),
1496
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "14", y: "3", width: "7", height: "5", rx: "1" }),
1497
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "14", y: "12", width: "7", height: "9", rx: "1" }),
1498
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "3", y: "16", width: "7", height: "5", rx: "1" })
1499
- ] });
1500
- case "pages":
1501
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { ...props, children: [
1502
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8Z" }),
1503
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("polyline", { points: "14 2 14 8 20 8" }),
1504
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "8", y1: "13", x2: "16", y2: "13" }),
1505
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "8", y1: "17", x2: "12", y2: "17" })
1506
- ] });
1507
- case "forms":
1508
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { ...props, children: [
1509
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M9 3h6" }),
1510
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M12 3v18" }),
1511
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M5 7h14a2 2 0 0 1 2 2v8a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4V9a2 2 0 0 1 2-2Z" }),
1512
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M7 11h10" }),
1513
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M7 15h6" })
1514
- ] });
1515
- case "globals":
1516
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { ...props, children: [
1517
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "12", cy: "12", r: "3" }),
1518
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z" })
1519
- ] });
1520
- case "media":
1521
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { ...props, children: [
1522
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
1523
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
1524
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("polyline", { points: "21 15 16 10 5 21" })
1525
- ] });
1526
- case "tools":
1527
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76Z" }) });
1528
- case "analytics":
1529
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { ...props, children: [
1530
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M4 19V5" }),
1531
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M10 19V10" }),
1532
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M16 19v-6" }),
1533
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M22 19V3" })
1534
- ] });
1535
- case "account":
1536
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { ...props, children: [
1537
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" }),
1538
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "12", cy: "7", r: "4" })
1539
- ] });
1540
- default:
1541
- return null;
1542
- }
1543
- }
1544
- function AdminShellClient({
1545
- children,
1546
- brandName,
1547
- logoUrl,
1548
- userEmail,
1549
- userRole,
1550
- pathname,
1551
- navItems,
1552
- onLogout,
1553
- storageKey = "orion-admin-shell-collapsed"
1554
- }) {
1555
- const [collapsed, setCollapsed] = (0, import_react2.useState)(false);
1556
- const [loggingOut, setLoggingOut] = (0, import_react2.useState)(false);
1557
- (0, import_react2.useEffect)(() => {
1558
- try {
1559
- const stored = window.localStorage.getItem(storageKey);
1560
- if (stored === "1") {
1561
- setCollapsed(true);
1562
- }
1563
- } catch {
1564
- }
1565
- }, [storageKey]);
1566
- const toggleSidebar = () => {
1567
- setCollapsed((current) => {
1568
- const next = !current;
1569
- try {
1570
- window.localStorage.setItem(storageKey, next ? "1" : "0");
1571
- } catch {
1572
- }
1573
- return next;
1574
- });
1575
- };
1576
- const handleLogout = async () => {
1577
- setLoggingOut(true);
1578
- try {
1579
- await onLogout();
1580
- } finally {
1581
- setLoggingOut(false);
1582
- }
1583
- };
1584
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `orion-admin-shell ${collapsed ? "is-collapsed" : ""}`, children: [
1585
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("aside", { className: "orion-admin-sidebar", children: [
1586
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1587
- "button",
1588
- {
1589
- "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
1590
- className: "orion-admin-sidebar-toggle",
1591
- onClick: toggleSidebar,
1592
- type: "button",
1593
- children: collapsed ? ">" : "<"
1594
- }
1595
- ),
1596
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "orion-admin-brand-wrap", children: [
1597
- logoUrl ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "orion-admin-brand-logo", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { alt: `${brandName} logo`, src: logoUrl }) }) : null,
1598
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "orion-admin-brand-text", children: [
1599
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "orion-admin-brand-name", title: brandName, children: collapsed ? brandName.slice(0, 1).toUpperCase() : brandName }),
1600
- !collapsed ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "orion-admin-brand-subtitle", children: "Studio" }) : null
1601
- ] })
1602
- ] }),
1603
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("nav", { className: "orion-admin-nav", children: navItems.filter((item) => roleCanAccessNav(userRole, item)).map((item) => {
1604
- const active = navItemIsActive(pathname, item);
1605
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1606
- "a",
1607
- {
1608
- className: `orion-admin-nav-link ${active ? "is-active" : ""}`,
1609
- href: item.href,
1610
- title: item.label,
1611
- children: item.icon ? collapsed ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(NavIcon, { name: item.icon }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: { alignItems: "center", display: "flex", gap: "0.6rem" }, children: [
1612
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(NavIcon, { name: item.icon }),
1613
- item.label
1614
- ] }) : collapsed ? item.label.slice(0, 1) : item.label
1615
- },
1616
- item.href
1617
- );
1618
- }) }),
1619
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "orion-admin-sidebar-footer", children: [
1620
- !collapsed ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1621
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "orion-admin-user-label", children: "Signed in as" }),
1622
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "orion-admin-user-email", children: userEmail })
1623
- ] }) : null,
1624
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "orion-admin-logout", disabled: loggingOut, onClick: handleLogout, type: "button", children: loggingOut ? "..." : "Log out" })
1625
- ] })
1626
- ] }),
1627
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("main", { className: "orion-admin-main", children })
1628
- ] });
1629
- }
1630
-
1631
- // src/admin/hooks/useSiteBranding.ts
1632
- var import_react3 = require("react");
1633
- var asRecord = (value) => {
1634
- if (!value || typeof value !== "object" || Array.isArray(value)) return null;
1635
- return value;
1636
- };
1637
- var pickString = (value) => {
1638
- if (typeof value !== "string") return null;
1639
- const next = value.trim();
1640
- return next.length > 0 ? next : null;
1641
- };
1642
- var resolveUploadUrl = (value) => {
1643
- const direct = pickString(value);
1644
- if (direct) return direct;
1645
- const record = asRecord(value);
1646
- if (!record) return null;
1647
- const fromUrl = pickString(record.url);
1648
- if (fromUrl) return fromUrl;
1649
- const fromFilename = pickString(record.filename);
1650
- if (fromFilename) return `/api/media/file/${fromFilename}`;
1651
- return null;
1652
- };
1653
- var resolveLogoUrl = (settings) => {
1654
- return resolveUploadUrl(settings.logo) || resolveUploadUrl(settings.adminLogo) || resolveUploadUrl(settings.brandLogo) || null;
1655
- };
1656
- var cachedBranding = null;
1657
- function useSiteBranding(defaultName, defaultLogoUrl) {
1658
- const [branding, setBranding] = (0, import_react3.useState)(() => {
1659
- if (cachedBranding) {
1660
- return {
1661
- logoUrl: cachedBranding.logoUrl || defaultLogoUrl || null,
1662
- siteName: cachedBranding.siteName || defaultName || null
1663
- };
1664
- }
1665
- return {
1666
- logoUrl: defaultLogoUrl || null,
1667
- siteName: defaultName || null
1668
- };
1669
- });
1670
- (0, import_react3.useEffect)(() => {
1671
- let cancelled = false;
1672
- const run = async () => {
1673
- try {
1674
- const res = await fetch("/api/globals/site-settings?depth=1", {
1675
- credentials: "include"
1676
- });
1677
- if (!res.ok) return;
1678
- const data = await res.json();
1679
- const record = asRecord(data);
1680
- if (!record) return;
1681
- const siteName = pickString(record.siteName);
1682
- const logoUrl = resolveLogoUrl(record);
1683
- cachedBranding = { logoUrl, siteName };
1684
- if (!cancelled) {
1685
- setBranding({
1686
- logoUrl: logoUrl || defaultLogoUrl || null,
1687
- siteName: siteName || defaultName || null
1688
- });
1689
- }
1690
- } catch {
1691
- }
1692
- };
1693
- void run();
1694
- return () => {
1695
- cancelled = true;
1696
- };
1697
- }, [defaultLogoUrl, defaultName]);
1698
- return branding;
1699
- }
1700
-
1701
- // src/admin/components/studio/adminPathUtils.ts
1702
- var import_react4 = require("react");
1703
- var DEFAULT_ADMIN_BASE_PATH = "/admin";
1704
- var normalizePath = (value) => {
1705
- if (!value || value === "/") return "/";
1706
- const withLeadingSlash = value.startsWith("/") ? value : `/${value}`;
1707
- const trimmed = withLeadingSlash.replace(/\/+$/, "");
1708
- return trimmed.length > 0 ? trimmed : "/";
1709
- };
1710
- var normalizeAdminBasePath = (value) => {
1711
- const normalized = normalizePath(value);
1712
- return normalized === "/" ? DEFAULT_ADMIN_BASE_PATH : normalized;
1713
- };
1714
- var detectAdminBasePath = (pathname, fallback = DEFAULT_ADMIN_BASE_PATH) => {
1715
- const normalizedPathname = normalizePath(pathname);
1716
- const normalizedFallback = normalizeAdminBasePath(fallback);
1717
- const markers = [
1718
- "/contact-form",
1719
- "/collections/",
1720
- "/globals/",
1721
- "/forms",
1722
- "/pages/",
1723
- "/tools",
1724
- "/media",
1725
- "/logout",
1726
- "/login"
1727
- ];
1728
- for (const marker of markers) {
1729
- const index = normalizedPathname.indexOf(marker);
1730
- if (index > 0) {
1731
- return normalizeAdminBasePath(normalizedPathname.slice(0, index));
1732
- }
1733
- }
1734
- if (normalizedPathname === normalizedFallback || normalizedPathname.startsWith(`${normalizedFallback}/`)) {
1735
- return normalizedFallback;
1736
- }
1737
- const segments = normalizedPathname.split("/").filter(Boolean);
1738
- if (segments.length > 0) {
1739
- return normalizeAdminBasePath(`/${segments[0]}`);
1740
- }
1741
- return normalizedFallback;
1742
- };
1743
- var isAbsoluteExternalURL2 = (value) => /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(value) || value.startsWith("//");
1744
- var resolveAdminPath = (adminBasePath, targetPath) => {
1745
- if (!targetPath) return adminBasePath;
1746
- if (isAbsoluteExternalURL2(targetPath)) return targetPath;
1747
- const normalizedBasePath = normalizeAdminBasePath(adminBasePath);
1748
- const normalizedTargetPath = normalizePath(targetPath);
1749
- if (normalizedTargetPath === "/admin") {
1750
- return normalizedBasePath;
1751
- }
1752
- if (normalizedTargetPath.startsWith("/admin/")) {
1753
- return `${normalizedBasePath}${normalizedTargetPath.slice("/admin".length)}`;
1754
- }
1755
- if (normalizedTargetPath === normalizedBasePath || normalizedTargetPath.startsWith(`${normalizedBasePath}/`)) {
1756
- return normalizedTargetPath;
1757
- }
1758
- if (normalizedTargetPath === "/") {
1759
- return normalizedBasePath;
1760
- }
1761
- return `${normalizedBasePath}${normalizedTargetPath}`;
1762
- };
1763
- var useAdminBasePath = (fallback = DEFAULT_ADMIN_BASE_PATH) => {
1764
- const [adminBasePath, setAdminBasePath] = (0, import_react4.useState)(normalizeAdminBasePath(fallback));
1765
- (0, import_react4.useEffect)(() => {
1766
- const update = () => {
1767
- setAdminBasePath(detectAdminBasePath(window.location.pathname, fallback));
1768
- };
1769
- update();
1770
- window.addEventListener("popstate", update);
1771
- return () => window.removeEventListener("popstate", update);
1772
- }, [fallback]);
1773
- return adminBasePath;
1774
- };
1775
-
1776
- // src/admin/components/studio/studioNavModel.ts
1777
- var getPropString = (props, key, fallback) => {
1778
- if (!props || typeof props !== "object") return fallback;
1779
- const direct = props[key];
1780
- if (typeof direct === "string" && direct.length > 0) return direct;
1781
- const clientProps = props.clientProps;
1782
- if (clientProps && typeof clientProps === "object") {
1783
- const nested = clientProps[key];
1784
- if (typeof nested === "string" && nested.length > 0) return nested;
1785
- }
1786
- return fallback;
1787
- };
1788
- var getPropBoolean = (props, key, fallback) => {
1789
- if (!props || typeof props !== "object") return fallback;
1790
- const direct = props[key];
1791
- if (typeof direct === "boolean") return direct;
1792
- const clientProps = props.clientProps;
1793
- if (clientProps && typeof clientProps === "object") {
1794
- const nested = clientProps[key];
1795
- if (typeof nested === "boolean") return nested;
1796
- }
1797
- return fallback;
1798
- };
1799
- var getPropStringArray = (props, key, fallback) => {
1800
- if (!props || typeof props !== "object") return fallback;
1801
- const read = (candidate) => Array.isArray(candidate) ? candidate.filter((value) => typeof value === "string" && value.length > 0) : null;
1802
- const direct = read(props[key]);
1803
- if (direct) return direct;
1804
- const clientProps = props.clientProps;
1805
- if (clientProps && typeof clientProps === "object") {
1806
- const nested = read(clientProps[key]);
1807
- if (nested) return nested;
1808
- }
1809
- return fallback;
1810
- };
1811
- var getPropSections = (props) => {
1812
- if (!props || typeof props !== "object") return [];
1813
- const direct = resolveStudioSections(props.sections);
1814
- if (direct.length > 0) return direct;
1815
- const clientProps = props.clientProps;
1816
- if (clientProps && typeof clientProps === "object") {
1817
- return resolveStudioSections(clientProps.sections);
1818
- }
1819
- return [];
1820
- };
1821
- var readUserRole = (user) => {
1822
- if (!user || typeof user !== "object") {
1823
- return void 0;
1824
- }
1825
- const role = user.role;
1826
- return typeof role === "string" ? role : void 0;
1827
- };
1828
- var buildStudioNavItems = (props, adminBasePath) => {
1829
- const formsEnabled = getPropBoolean(props, "formsEnabled", false);
1830
- const formsCollectionSlug = getPropString(props, "formsCollectionSlug", "forms");
1831
- const formSubmissionsCollectionSlug = getPropString(
1832
- props,
1833
- "formSubmissionsCollectionSlug",
1834
- "form-submissions"
1835
- );
1836
- const formUploadsCollectionSlug = getPropString(props, "formUploadsCollectionSlug", "form-uploads");
1837
- const mediaCollectionSlug = getPropString(props, "mediaCollectionSlug", "media");
1838
- const globalsBasePath = getPropString(props, "globalsBasePath", "/globals");
1839
- const globalsExtraMatchPrefixes = getPropStringArray(props, "globalsExtraMatchPrefixes", []);
1840
- const sections = getPropSections(props);
1841
- const pagesPath = resolveAdminPath(adminBasePath, "/pages");
1842
- const formsPath = resolveAdminPath(adminBasePath, "/forms");
1843
- const mediaPath = resolveAdminPath(adminBasePath, "/media");
1844
- const toolsPath = resolveAdminPath(adminBasePath, "/tools");
1845
- const resolvedGlobalsBasePath = resolveAdminPath(adminBasePath, globalsBasePath);
1846
- const resolvedGlobalsExtraMatchPrefixes = globalsExtraMatchPrefixes.map(
1847
- (prefix) => resolveAdminPath(adminBasePath, prefix)
1848
- );
1849
- const baseItemsBeforeTools = [
1850
- {
1851
- href: adminBasePath,
1852
- icon: "dashboard",
1853
- label: "Dashboard",
1854
- matchPrefixes: [adminBasePath]
1855
- },
1856
- {
1857
- href: pagesPath,
1858
- icon: "pages",
1859
- label: "Pages",
1860
- matchPrefixes: [pagesPath]
1861
- },
1862
- ...formsEnabled ? [
1863
- {
1864
- href: formsPath,
1865
- icon: "forms",
1866
- label: "Forms",
1867
- matchPrefixes: [
1868
- formsPath,
1869
- resolveAdminPath(adminBasePath, `/collections/${formsCollectionSlug}`),
1870
- resolveAdminPath(adminBasePath, `/collections/${formSubmissionsCollectionSlug}`),
1871
- resolveAdminPath(adminBasePath, `/collections/${formUploadsCollectionSlug}`)
1872
- ]
1873
- }
1874
- ] : [],
1875
- {
1876
- href: resolvedGlobalsBasePath,
1877
- icon: "globals",
1878
- label: "Globals",
1879
- matchPrefixes: [
1880
- resolvedGlobalsBasePath,
1881
- resolveAdminPath(adminBasePath, "/globals"),
1882
- ...resolvedGlobalsExtraMatchPrefixes
1883
- ]
1884
- },
1885
- {
1886
- href: mediaPath,
1887
- icon: "media",
1888
- label: "Media",
1889
- matchPrefixes: [mediaPath, resolveAdminPath(adminBasePath, `/collections/${mediaCollectionSlug}`)]
1890
- }
1891
- ];
1892
- const adminToolsItem = {
1893
- href: toolsPath,
1894
- icon: "tools",
1895
- label: "Admin Tools",
1896
- matchPrefixes: [toolsPath, resolveAdminPath(adminBasePath, "/collections/users")],
1897
- roles: ["admin"]
1898
- };
1899
- const extensionItems = sections.map((section) => ({
1900
- href: resolveAdminPath(adminBasePath, section.href),
1901
- ...section.icon ? { icon: section.icon } : {},
1902
- label: section.label,
1903
- matchPrefixes: section.matchPrefixes.map((prefix) => resolveAdminPath(adminBasePath, prefix)),
1904
- roles: section.roles
1905
- }));
1906
- return [...baseItemsBeforeTools, ...extensionItems, adminToolsItem];
1907
- };
1908
-
1909
- // src/admin/components/studio/StudioSectionLayout.tsx
1910
- var import_jsx_runtime5 = require("react/jsx-runtime");
1911
- function StudioSectionLayout({ children, navProps }) {
1912
- const { user } = (0, import_ui.useAuth)();
1913
- const pathname = (0, import_navigation.usePathname)() || "";
1914
- const router = (0, import_navigation.useRouter)();
1915
- const adminBasePath = useAdminBasePath();
1916
- const defaultBrandName = getPropString(navProps, "brandName", "Orion Studio");
1917
- const defaultLogoUrl = getPropString(navProps, "logoUrl", "");
1918
- const navItems = (0, import_react5.useMemo)(
1919
- () => buildStudioNavItems(navProps, adminBasePath),
1920
- [adminBasePath, navProps]
1921
- );
1922
- const branding = useSiteBranding(defaultBrandName, defaultLogoUrl || void 0);
1923
- (0, import_react5.useLayoutEffect)(() => {
1924
- document.body.classList.add("orion-studio-shell-active");
1925
- return () => {
1926
- document.body.classList.remove("orion-studio-shell-active");
1927
- };
1928
- }, []);
1929
- const logout = (0, import_react5.useMemo)(
1930
- () => async () => {
1931
- await fetch("/api/users/logout", {
1932
- credentials: "include",
1933
- method: "POST"
1934
- });
1935
- router.push(resolveAdminPath(adminBasePath, "/login"));
1936
- router.refresh();
1937
- },
1938
- [adminBasePath, router]
1939
- );
1940
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1941
- AdminShellClient,
1942
- {
1943
- brandName: branding.siteName || defaultBrandName,
1944
- logoUrl: branding.logoUrl || defaultLogoUrl || void 0,
1945
- navItems,
1946
- onLogout: logout,
1947
- pathname,
1948
- storageKey: "orion-admin-sidebar-collapsed-v1",
1949
- userEmail: typeof user?.email === "string" ? user.email : "user",
1950
- userRole: readUserRole(user),
1951
- children
1952
- }
1953
- );
1954
- }
1955
-
1956
- // src/admin/components/studio/AdminStudioDashboard.tsx
1957
- var import_jsx_runtime6 = require("react/jsx-runtime");
1958
- var DEFAULT_ADMIN_BASE_PATH2 = "/admin";
1959
- var normalizePath2 = (value) => {
1960
- if (!value || value === "/") return "/";
1961
- const withLeadingSlash = value.startsWith("/") ? value : `/${value}`;
1962
- const trimmed = withLeadingSlash.replace(/\/+$/, "");
1963
- return trimmed.length > 0 ? trimmed : "/";
1964
- };
1965
- var normalizeAdminBasePath2 = (value) => {
1966
- const normalized = normalizePath2(value);
1967
- return normalized === "/" ? DEFAULT_ADMIN_BASE_PATH2 : normalized;
1968
- };
1969
- var isAbsoluteExternalURL3 = (value) => /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(value) || value.startsWith("//");
1970
- var resolveAdminPath2 = (adminBasePath, targetPath) => {
1971
- if (!targetPath) return adminBasePath;
1972
- if (isAbsoluteExternalURL3(targetPath)) return targetPath;
1973
- const normalizedBasePath = normalizeAdminBasePath2(adminBasePath);
1974
- const normalizedTargetPath = normalizePath2(targetPath);
1975
- if (normalizedTargetPath === "/admin") {
1976
- return normalizedBasePath;
1977
- }
1978
- if (normalizedTargetPath.startsWith("/admin/")) {
1979
- return `${normalizedBasePath}${normalizedTargetPath.slice("/admin".length)}`;
1980
- }
1981
- if (normalizedTargetPath === normalizedBasePath || normalizedTargetPath.startsWith(`${normalizedBasePath}/`)) {
1982
- return normalizedTargetPath;
1983
- }
1984
- if (normalizedTargetPath === "/") {
1985
- return normalizedBasePath;
1986
- }
1987
- return `${normalizedBasePath}${normalizedTargetPath}`;
1988
- };
1989
- var getPropString2 = (props, key, fallback) => {
1990
- if (!props || typeof props !== "object") return fallback;
1991
- const direct = props[key];
1992
- if (typeof direct === "string" && direct.length > 0) return direct;
1993
- const clientProps = props.clientProps;
1994
- if (clientProps && typeof clientProps === "object") {
1995
- const nested = clientProps[key];
1996
- if (typeof nested === "string" && nested.length > 0) return nested;
1997
- }
1998
- return fallback;
1999
- };
2000
- var getPropBoolean2 = (props, key, fallback) => {
2001
- if (!props || typeof props !== "object") return fallback;
2002
- const direct = props[key];
2003
- if (typeof direct === "boolean") return direct;
2004
- const clientProps = props.clientProps;
2005
- if (clientProps && typeof clientProps === "object") {
2006
- const nested = clientProps[key];
2007
- if (typeof nested === "boolean") return nested;
2008
- }
2009
- return fallback;
2010
- };
2011
- var getPropSections2 = (props) => {
2012
- if (!props || typeof props !== "object") return [];
2013
- const direct = resolveStudioSections(props.sections);
2014
- if (direct.length > 0) return direct;
2015
- const clientProps = props.clientProps;
2016
- if (clientProps && typeof clientProps === "object") {
2017
- return resolveStudioSections(clientProps.sections);
2018
- }
2019
- return [];
2020
- };
2021
- var readUserRole2 = (props) => {
2022
- if (!props || typeof props !== "object") return void 0;
2023
- const user = props.user;
2024
- if (user && typeof user === "object") {
2025
- const role = user.role;
2026
- if (typeof role === "string") {
2027
- return role;
2028
- }
2029
- }
2030
- const initPageResult = props.initPageResult;
2031
- if (initPageResult && typeof initPageResult === "object") {
2032
- const req = initPageResult.req;
2033
- if (req && typeof req === "object") {
2034
- const nestedUser = req.user;
2035
- if (nestedUser && typeof nestedUser === "object") {
2036
- const role = nestedUser.role;
2037
- if (typeof role === "string") {
2038
- return role;
2039
- }
2040
- }
2041
- }
2042
- }
2043
- return void 0;
2044
- };
2045
- var readAdminBasePath = (props) => {
2046
- if (!props || typeof props !== "object") {
2047
- return DEFAULT_ADMIN_BASE_PATH2;
2048
- }
2049
- const payloadLike = props.payload;
2050
- if (payloadLike && typeof payloadLike === "object") {
2051
- const config = payloadLike.config;
2052
- const routes = config && typeof config === "object" ? config.routes : null;
2053
- const admin = routes && typeof routes === "object" ? routes.admin : null;
2054
- if (typeof admin === "string" && admin.length > 0) {
2055
- return normalizeAdminBasePath2(admin);
2056
- }
2057
- }
2058
- const initPageResult = props.initPageResult;
2059
- if (initPageResult && typeof initPageResult === "object") {
2060
- const req = initPageResult.req;
2061
- if (req && typeof req === "object") {
2062
- const nestedPayload = req.payload;
2063
- if (nestedPayload && typeof nestedPayload === "object") {
2064
- const config = nestedPayload.config;
2065
- const routes = config && typeof config === "object" ? config.routes : null;
2066
- const admin = routes && typeof routes === "object" ? routes.admin : null;
2067
- if (typeof admin === "string" && admin.length > 0) {
2068
- return normalizeAdminBasePath2(admin);
2069
- }
2070
- }
2071
- }
2072
- }
2073
- return DEFAULT_ADMIN_BASE_PATH2;
2074
- };
2075
- var buildSectionLinks = (adminBasePath, sections, formsEnabled, globalsBasePath) => {
2076
- const links = [
2077
- {
2078
- href: resolveAdminPath2(adminBasePath, "/pages"),
2079
- id: "pages",
2080
- label: "Pages"
2081
- },
2082
- ...formsEnabled ? [
2083
- {
2084
- href: resolveAdminPath2(adminBasePath, "/forms"),
2085
- id: "forms",
2086
- label: "Forms",
2087
- roles: ["admin", "editor"]
2088
- }
2089
- ] : [],
2090
- {
2091
- href: resolveAdminPath2(adminBasePath, globalsBasePath),
2092
- id: "globals",
2093
- label: "Globals"
2094
- },
2095
- {
2096
- href: resolveAdminPath2(adminBasePath, "/media"),
2097
- id: "media",
2098
- label: "Media"
2099
- },
2100
- ...sections.map((section) => ({
2101
- href: resolveAdminPath2(adminBasePath, section.href),
2102
- id: section.id,
2103
- label: section.label,
2104
- ...section.roles ? { roles: section.roles } : {}
2105
- })),
2106
- {
2107
- href: resolveAdminPath2(adminBasePath, "/tools"),
2108
- id: "admin-tools",
2109
- label: "Admin Tools",
2110
- roles: ["admin"]
2111
- }
2112
- ];
2113
- const seen = /* @__PURE__ */ new Set();
2114
- return links.filter((link) => {
2115
- if (seen.has(link.id)) {
2116
- return false;
2117
- }
2118
- seen.add(link.id);
2119
- return true;
2120
- });
2121
- };
2122
- function AdminStudioDashboard(rawProps) {
2123
- const props = rawProps || {};
2124
- const formsEnabled = getPropBoolean2(props, "formsEnabled", false);
2125
- const globalsBasePath = getPropString2(props, "globalsBasePath", "/globals");
2126
- const sections = getPropSections2(props);
2127
- const pagesCollectionSlug = getPropString2(props, "pagesCollectionSlug", "pages");
2128
- const formsCollectionSlug = getPropString2(props, "formsCollectionSlug", "forms");
2129
- const formSubmissionsCollectionSlug = getPropString2(
2130
- props,
2131
- "formSubmissionsCollectionSlug",
2132
- "form-submissions"
2133
- );
2134
- const mediaCollectionSlug = getPropString2(props, "mediaCollectionSlug", "media");
2135
- const adminBasePath = readAdminBasePath(props);
2136
- const userRole = readUserRole2(props);
2137
- const navProps = {
2138
- brandName: getPropString2(props, "brandName", "Orion Studio"),
2139
- formSubmissionsCollectionSlug,
2140
- formsCollectionSlug,
2141
- formsEnabled,
2142
- globalsBasePath,
2143
- logoUrl: getPropString2(props, "logoUrl", ""),
2144
- mediaCollectionSlug,
2145
- pagesCollectionSlug,
2146
- sections
2147
- };
2148
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StudioSectionLayout, { navProps, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2149
- AdminPage,
2150
- {
2151
- breadcrumbs: [{ label: "Dashboard" }],
2152
- description: "What needs attention, what changed recently, and how the site is performing.",
2153
- title: "Studio",
2154
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2155
- AdminStudioDashboardClient,
2156
- {
2157
- formSubmissionsCollectionSlug,
2158
- formsCollectionSlug,
2159
- formsEnabled,
2160
- formsPath: resolveAdminPath2(adminBasePath, "/forms"),
2161
- globalsBasePath: resolveAdminPath2(adminBasePath, globalsBasePath),
2162
- mediaCollectionSlug,
2163
- mediaPath: resolveAdminPath2(adminBasePath, "/media"),
2164
- pagesCollectionSlug,
2165
- pagesPath: resolveAdminPath2(adminBasePath, "/pages"),
2166
- sectionLinks: buildSectionLinks(adminBasePath, sections, formsEnabled, globalsBasePath),
2167
- toolsPath: resolveAdminPath2(adminBasePath, "/tools"),
2168
- userRole
2169
- }
2170
- )
2171
- }
2172
- ) });
2173
- }
2174
-
2175
779
  // src/admin/helpers/withTooltips.ts
2176
780
  var defaultTooltips = {
2177
781
  title: "The main title displayed on this page.",
@@ -2442,6 +1046,36 @@ __export(admin_app_exports, {
2442
1046
  roleCanAccessNav: () => roleCanAccessNav
2443
1047
  });
2444
1048
 
1049
+ // src/admin-app/components/AdminBreadcrumbs.tsx
1050
+ var import_jsx_runtime = require("react/jsx-runtime");
1051
+ function AdminBreadcrumbs({ items }) {
1052
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("nav", { "aria-label": "Breadcrumb", className: "orion-admin-breadcrumbs", children: items.map((item, index) => {
1053
+ const isLast = index === items.length - 1;
1054
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
1055
+ item.href && !isLast ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: item.href, children: item.label }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: item.label }),
1056
+ !isLast ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "orion-admin-breadcrumb-sep", children: "/" }) : null
1057
+ ] }, `${item.label}-${index}`);
1058
+ }) });
1059
+ }
1060
+
1061
+ // src/admin-app/components/AdminPage.tsx
1062
+ var import_jsx_runtime2 = require("react/jsx-runtime");
1063
+ function AdminPage({ title, description, breadcrumbs, actions, children }) {
1064
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "orion-admin-page", children: [
1065
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "orion-admin-page-header", children: [
1066
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AdminBreadcrumbs, { items: breadcrumbs }),
1067
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "orion-admin-page-title-row", children: [
1068
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
1069
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { children: title }),
1070
+ description ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: description }) : null
1071
+ ] }),
1072
+ actions ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "orion-admin-page-actions", children: actions }) : null
1073
+ ] })
1074
+ ] }),
1075
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "orion-admin-page-content", children })
1076
+ ] });
1077
+ }
1078
+
2445
1079
  // src/admin-app/nestedNavigation.ts
2446
1080
  var normalizeNestedNavItems = (items) => {
2447
1081
  const deduped = [];
@@ -4238,7 +2872,7 @@ var TestimonialsBlock = {
4238
2872
  };
4239
2873
 
4240
2874
  // src/blocks/components/BuilderBlockLabel.tsx
4241
- var import_jsx_runtime7 = require("react/jsx-runtime");
2875
+ var import_jsx_runtime3 = require("react/jsx-runtime");
4242
2876
  var blockTitles = {
4243
2877
  bookingEmbed: "Booking Embed",
4244
2878
  cta: "Call to Action",
@@ -4265,8 +2899,8 @@ function BuilderBlockLabel({ blockType, rowLabel, rowNumber }) {
4265
2899
  const fallbackTitle = blockTitles[blockType] || blockType;
4266
2900
  const title = rowLabel && rowLabel.trim().length > 0 ? rowLabel : fallbackTitle;
4267
2901
  const tag = blockEmojis[blockType] || "Section";
4268
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { alignItems: "center", display: "flex", gap: 8 }, children: [
4269
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2902
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { alignItems: "center", display: "flex", gap: 8 }, children: [
2903
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
4270
2904
  "span",
4271
2905
  {
4272
2906
  style: {
@@ -4281,7 +2915,7 @@ function BuilderBlockLabel({ blockType, rowLabel, rowNumber }) {
4281
2915
  children: tag
4282
2916
  }
4283
2917
  ),
4284
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { style: { fontWeight: 700 }, children: [
2918
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: { fontWeight: 700 }, children: [
4285
2919
  typeof rowNumber === "number" ? `${rowNumber + 1}. ` : "",
4286
2920
  title
4287
2921
  ] })
@@ -4828,18 +3462,18 @@ function createStudioRegistry(modules) {
4828
3462
  listNodeTypes: () => [...nodeTypes]
4829
3463
  };
4830
3464
  }
4831
- function validateStudioDocument(document2, modules) {
3465
+ function validateStudioDocument(document, modules) {
4832
3466
  const issues = [];
4833
- if (document2.schemaVersion !== 1) {
3467
+ if (document.schemaVersion !== 1) {
4834
3468
  issues.push(makeIssue("Unsupported schema version", "schemaVersion", "studio.schemaVersion"));
4835
3469
  }
4836
- if (!Array.isArray(document2.nodes)) {
3470
+ if (!Array.isArray(document.nodes)) {
4837
3471
  issues.push(makeIssue("Nodes must be an array", "nodes", "studio.nodes"));
4838
3472
  return issues;
4839
3473
  }
4840
3474
  const registry = createStudioRegistry(modules);
4841
3475
  const nodeIDs = /* @__PURE__ */ new Set();
4842
- document2.nodes.forEach((node, index) => {
3476
+ document.nodes.forEach((node, index) => {
4843
3477
  if (!node.id) {
4844
3478
  issues.push(makeIssue("Node id is required", `nodes.${index}.id`, "studio.node.id"));
4845
3479
  }
@@ -4853,15 +3487,15 @@ function validateStudioDocument(document2, modules) {
4853
3487
  });
4854
3488
  for (const module2 of modules) {
4855
3489
  for (const validate of module2.validators) {
4856
- issues.push(...validate(document2));
3490
+ issues.push(...validate(document));
4857
3491
  }
4858
3492
  }
4859
3493
  return issues;
4860
3494
  }
4861
- function compileStudioDocument(document2, modules) {
4862
- const issues = validateStudioDocument(document2, modules);
3495
+ function compileStudioDocument(document, modules) {
3496
+ const issues = validateStudioDocument(document, modules);
4863
3497
  const compilerEntries = modules.filter((mod) => typeof mod.compiler?.compileNode === "function").map((mod) => mod.compiler?.compileNode);
4864
- const layout = document2.nodes.map((node) => {
3498
+ const layout = document.nodes.map((node) => {
4865
3499
  for (const compileNode of compilerEntries) {
4866
3500
  if (!compileNode) {
4867
3501
  continue;
@@ -5198,7 +3832,7 @@ var layoutToStudioDocument = (layout, title, metadata) => {
5198
3832
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
5199
3833
  };
5200
3834
  };
5201
- var studioDocumentToLayout = (document2) => document2.nodes.map(
3835
+ var studioDocumentToLayout = (document) => document.nodes.map(
5202
3836
  (node) => migrateBlockToSettingsV2({
5203
3837
  id: node.id,
5204
3838
  blockType: node.type,
@@ -5232,7 +3866,7 @@ function withStudioDocumentLayout(page) {
5232
3866
  }
5233
3867
  return page;
5234
3868
  }
5235
- function normalizePath3(segments) {
3869
+ function normalizePath(segments) {
5236
3870
  if (!segments || segments.length === 0) {
5237
3871
  return "/";
5238
3872
  }
@@ -5289,7 +3923,7 @@ function createPageQueries(getPayloadClient, contentTag = "website-content") {
5289
3923
  { tags: [contentTag] }
5290
3924
  );
5291
3925
  async function getPageBySegments(segments, draft = false) {
5292
- const path2 = normalizePath3(segments);
3926
+ const path2 = normalizePath(segments);
5293
3927
  const payload = await getPayloadClient();
5294
3928
  if (draft) {
5295
3929
  return queryPageByPath(payload, path2, true);
@@ -5901,8 +4535,8 @@ var hydrateRelationship = (valueFromStudio, valueFromLayout) => {
5901
4535
  }
5902
4536
  return valueFromLayout;
5903
4537
  };
5904
- var hydrateDocumentWithLayoutRelations = (document2, layout) => {
5905
- const nextNodes = document2.nodes.map((node, index) => {
4538
+ var hydrateDocumentWithLayoutRelations = (document, layout) => {
4539
+ const nextNodes = document.nodes.map((node, index) => {
5906
4540
  const layoutBlock = layout[index];
5907
4541
  if (!isRecord5(layoutBlock)) {
5908
4542
  return node;
@@ -5941,17 +4575,17 @@ var hydrateDocumentWithLayoutRelations = (document2, layout) => {
5941
4575
  };
5942
4576
  });
5943
4577
  return {
5944
- ...document2,
4578
+ ...document,
5945
4579
  nodes: nextNodes
5946
4580
  };
5947
4581
  };
5948
- var normalizeDocument = (document2) => ({
5949
- ...document2,
4582
+ var normalizeDocument = (document) => ({
4583
+ ...document,
5950
4584
  schemaVersion: 1,
5951
4585
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
5952
4586
  });
5953
- var normalizeLegacyHeroDefaults = (document2) => {
5954
- const nodes = document2.nodes.map((node) => {
4587
+ var normalizeLegacyHeroDefaults = (document) => {
4588
+ const nodes = document.nodes.map((node) => {
5955
4589
  if (node.type !== "hero" || !isRecord5(node.data)) {
5956
4590
  return node;
5957
4591
  }
@@ -5969,7 +4603,7 @@ var normalizeLegacyHeroDefaults = (document2) => {
5969
4603
  };
5970
4604
  });
5971
4605
  return {
5972
- ...document2,
4606
+ ...document,
5973
4607
  nodes
5974
4608
  };
5975
4609
  };
@@ -6018,9 +4652,9 @@ var createStudioPageService = ({
6018
4652
  title: typeof page.title === "string" ? page.title : "Untitled Page"
6019
4653
  };
6020
4654
  },
6021
- validateStudioDocument: (document2) => validateStudioDocument(document2, modules),
6022
- saveDraft: async (pageID, document2, _metadata) => {
6023
- const normalizedDocument = normalizeLegacyHeroDefaults(normalizeDocument(document2));
4655
+ validateStudioDocument: (document) => validateStudioDocument(document, modules),
4656
+ saveDraft: async (pageID, document, _metadata) => {
4657
+ const normalizedDocument = normalizeLegacyHeroDefaults(normalizeDocument(document));
6024
4658
  const compileResult = compileStudioDocument(normalizedDocument, modules);
6025
4659
  await payload.update({
6026
4660
  collection: collectionSlug,
@@ -6040,8 +4674,8 @@ var createStudioPageService = ({
6040
4674
  status: "draft"
6041
4675
  };
6042
4676
  },
6043
- publish: async (pageID, document2, _metadata) => {
6044
- const normalizedDocument = normalizeLegacyHeroDefaults(normalizeDocument(document2));
4677
+ publish: async (pageID, document, _metadata) => {
4678
+ const normalizedDocument = normalizeLegacyHeroDefaults(normalizeDocument(document));
6045
4679
  const compileResult = compileStudioDocument(normalizedDocument, modules);
6046
4680
  assertCanPublish(compileResult.issues);
6047
4681
  await payload.update({
@@ -6321,9 +4955,9 @@ var pageNodeTypes = Object.keys(defaultNodeData).map((type) => ({
6321
4955
  return data;
6322
4956
  }
6323
4957
  }));
6324
- var validatePageDocument = (document2) => {
4958
+ var validatePageDocument = (document) => {
6325
4959
  const issues = [];
6326
- if (!document2.title || document2.title.trim().length === 0) {
4960
+ if (!document.title || document.title.trim().length === 0) {
6327
4961
  issues.push({
6328
4962
  code: "pages.title.required",
6329
4963
  message: "Page title is required before publishing.",
@@ -6331,7 +4965,7 @@ var validatePageDocument = (document2) => {
6331
4965
  severity: "error"
6332
4966
  });
6333
4967
  }
6334
- if (document2.nodes.length === 0) {
4968
+ if (document.nodes.length === 0) {
6335
4969
  issues.push({
6336
4970
  code: "pages.nodes.required",
6337
4971
  message: "At least one section is required.",
@@ -6339,7 +4973,7 @@ var validatePageDocument = (document2) => {
6339
4973
  severity: "error"
6340
4974
  });
6341
4975
  }
6342
- document2.nodes.forEach((node, index) => {
4976
+ document.nodes.forEach((node, index) => {
6343
4977
  if (node.type === "hero" && typeof node.data.headline !== "string") {
6344
4978
  issues.push({
6345
4979
  code: "pages.hero.headline",