@orion-studios/payload-studio 0.6.0-beta.4 → 0.6.0-beta.40
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/admin/client.d.mts +1 -0
- package/dist/admin/client.d.ts +1 -0
- package/dist/admin/client.js +3924 -1355
- package/dist/admin/client.mjs +4090 -1533
- package/dist/admin/index.d.mts +2 -3
- package/dist/admin/index.d.ts +2 -3
- package/dist/admin/index.js +141 -1522
- package/dist/admin/index.mjs +2 -4
- package/dist/admin-app/client.js +11 -4
- package/dist/admin-app/client.mjs +1 -1
- package/dist/admin-app/index.d.mts +2 -2
- package/dist/admin-app/index.d.ts +2 -2
- package/dist/admin-app/index.mjs +4 -4
- package/dist/admin-app/styles.css +343 -41
- package/dist/admin.css +18 -2
- package/dist/{chunk-KPIX7OSV.mjs → chunk-2XH7X34N.mjs} +11 -4
- package/dist/{chunk-OTHERBGX.mjs → chunk-AWL7TIEI.mjs} +1 -1
- package/dist/{chunk-PF3EBZXF.mjs → chunk-CUPMTHGX.mjs} +19 -3
- package/dist/chunk-JC3UV74N.mjs +1033 -0
- package/dist/{chunk-XKUTZ7IU.mjs → chunk-LUF62ZWF.mjs} +31 -5
- package/dist/{chunk-EHUE4LCT.mjs → chunk-RKTIFEUY.mjs} +33 -3
- package/dist/chunk-W2UOCJDX.mjs +32 -0
- package/dist/{index-bbA3HSxa.d.ts → index-BV0vEGl6.d.ts} +6 -9
- package/dist/{index-52HdVLQq.d.ts → index-DAdN56fM.d.ts} +1 -1
- package/dist/{index-DEkV-sMs.d.mts → index-DLfPOqYA.d.mts} +6 -9
- package/dist/{index-Cv-6qnrw.d.mts → index-DRu4fKxf.d.mts} +1 -1
- package/dist/{index-DEQC3Dwj.d.mts → index-G_uTNffQ.d.mts} +1 -1
- package/dist/{index-Crx_MtPw.d.ts → index-vkEwwsoC.d.ts} +1 -1
- package/dist/index.d.mts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +249 -1542
- package/dist/index.mjs +10 -10
- package/dist/nextjs/index.js +19 -3
- package/dist/nextjs/index.mjs +2 -2
- package/dist/{sitePreviewTypes-BkHCWxNW.d.mts → sitePreviewTypes-BrJwGzJj.d.mts} +1 -1
- package/dist/{sitePreviewTypes-BkHCWxNW.d.ts → sitePreviewTypes-BrJwGzJj.d.ts} +1 -1
- package/dist/studio-pages/builder.css +24 -5
- package/dist/studio-pages/client.js +455 -63
- package/dist/studio-pages/client.mjs +455 -63
- package/dist/studio-pages/index.d.mts +1 -1
- package/dist/studio-pages/index.d.ts +1 -1
- package/dist/studio-pages/index.js +46 -4
- package/dist/studio-pages/index.mjs +3 -3
- package/package.json +1 -1
- package/dist/chunk-DYXSAVUQ.mjs +0 -2372
- package/dist/chunk-Z6L5K5MH.mjs +0 -64
|
@@ -0,0 +1,1033 @@
|
|
|
1
|
+
import {
|
|
2
|
+
adminNavIcons
|
|
3
|
+
} from "./chunk-W2UOCJDX.mjs";
|
|
4
|
+
import {
|
|
5
|
+
SOCIAL_MEDIA_DEFAULT_ICON_BY_PLATFORM,
|
|
6
|
+
SOCIAL_MEDIA_ICON_OPTIONS,
|
|
7
|
+
SOCIAL_MEDIA_PLATFORMS,
|
|
8
|
+
SOCIAL_MEDIA_PLATFORM_LABELS
|
|
9
|
+
} from "./chunk-ZTXJG4K5.mjs";
|
|
10
|
+
import {
|
|
11
|
+
__export,
|
|
12
|
+
__require
|
|
13
|
+
} from "./chunk-6BWS3CLP.mjs";
|
|
14
|
+
|
|
15
|
+
// src/admin/index.ts
|
|
16
|
+
var admin_exports = {};
|
|
17
|
+
__export(admin_exports, {
|
|
18
|
+
SOCIAL_MEDIA_DEFAULT_ICON_BY_PLATFORM: () => SOCIAL_MEDIA_DEFAULT_ICON_BY_PLATFORM,
|
|
19
|
+
SOCIAL_MEDIA_ICON_OPTIONS: () => SOCIAL_MEDIA_ICON_OPTIONS,
|
|
20
|
+
SOCIAL_MEDIA_PLATFORMS: () => SOCIAL_MEDIA_PLATFORMS,
|
|
21
|
+
SOCIAL_MEDIA_PLATFORM_LABELS: () => SOCIAL_MEDIA_PLATFORM_LABELS,
|
|
22
|
+
configureAdmin: () => configureAdmin,
|
|
23
|
+
createHeaderNavItemsField: () => createHeaderNavItemsField,
|
|
24
|
+
createSocialMediaConnectionsField: () => createSocialMediaConnectionsField,
|
|
25
|
+
createSocialMediaGlobal: () => createSocialMediaGlobal,
|
|
26
|
+
createThemePreferenceField: () => createThemePreferenceField,
|
|
27
|
+
socialMediaConnectionsField: () => socialMediaConnectionsField,
|
|
28
|
+
themePreferenceField: () => themePreferenceField,
|
|
29
|
+
withTooltips: () => withTooltips
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// src/admin/helpers/configureAdmin.ts
|
|
33
|
+
import fs from "fs";
|
|
34
|
+
import path from "path";
|
|
35
|
+
import { fileURLToPath } from "url";
|
|
36
|
+
|
|
37
|
+
// src/admin/fields/themePreference.ts
|
|
38
|
+
var createThemePreferenceField = (defaultTheme = "brand-light") => ({
|
|
39
|
+
name: "themePreference",
|
|
40
|
+
type: "select",
|
|
41
|
+
defaultValue: defaultTheme,
|
|
42
|
+
options: [
|
|
43
|
+
{ label: "Light", value: "light" },
|
|
44
|
+
{ label: "Dark", value: "dark" },
|
|
45
|
+
{ label: "Brand Light", value: "brand-light" },
|
|
46
|
+
{ label: "Brand Dark", value: "brand-dark" }
|
|
47
|
+
],
|
|
48
|
+
admin: {
|
|
49
|
+
position: "sidebar",
|
|
50
|
+
condition: () => false
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
var themePreferenceField = createThemePreferenceField("brand-light");
|
|
54
|
+
|
|
55
|
+
// src/shared/studioSections.ts
|
|
56
|
+
var studioRoles = /* @__PURE__ */ new Set(["admin", "developer", "editor", "client"]);
|
|
57
|
+
var studioIcons = new Set(adminNavIcons);
|
|
58
|
+
var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
59
|
+
var isAbsoluteExternalURL = (value) => /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(value) || value.startsWith("//");
|
|
60
|
+
var normalizePathLikeValue = (value) => {
|
|
61
|
+
const trimmed = value.trim();
|
|
62
|
+
if (!trimmed) {
|
|
63
|
+
return "";
|
|
64
|
+
}
|
|
65
|
+
if (isAbsoluteExternalURL(trimmed)) {
|
|
66
|
+
return trimmed;
|
|
67
|
+
}
|
|
68
|
+
const withLeadingSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
69
|
+
const normalized = withLeadingSlash.replace(/\/+$/, "");
|
|
70
|
+
return normalized || "/";
|
|
71
|
+
};
|
|
72
|
+
var normalizeStringArray = (value) => {
|
|
73
|
+
if (!Array.isArray(value)) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
return value.filter((entry) => typeof entry === "string").map((entry) => normalizePathLikeValue(entry)).filter((entry) => entry.length > 0);
|
|
77
|
+
};
|
|
78
|
+
var normalizeRoles = (value) => {
|
|
79
|
+
if (!Array.isArray(value)) {
|
|
80
|
+
return void 0;
|
|
81
|
+
}
|
|
82
|
+
const roles = value.filter((entry) => typeof entry === "string" && studioRoles.has(entry));
|
|
83
|
+
return roles.length > 0 ? roles : void 0;
|
|
84
|
+
};
|
|
85
|
+
var normalizeCard = (value) => {
|
|
86
|
+
if (!isRecord(value) || typeof value.title !== "string") {
|
|
87
|
+
return void 0;
|
|
88
|
+
}
|
|
89
|
+
const title = value.title.trim();
|
|
90
|
+
if (!title) {
|
|
91
|
+
return void 0;
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
title,
|
|
95
|
+
...typeof value.description === "string" && value.description.trim().length > 0 ? { description: value.description.trim() } : {}
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
var normalizeIcon = (value) => typeof value === "string" && studioIcons.has(value) ? value : void 0;
|
|
99
|
+
var resolveStudioSections = (value) => {
|
|
100
|
+
if (!Array.isArray(value)) {
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
const sections = [];
|
|
104
|
+
const seen = /* @__PURE__ */ new Set();
|
|
105
|
+
for (const entry of value) {
|
|
106
|
+
if (!isRecord(entry) || typeof entry.id !== "string" || typeof entry.label !== "string") {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const id = entry.id.trim();
|
|
110
|
+
const label = entry.label.trim();
|
|
111
|
+
if (!id || !label || seen.has(id)) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
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) : "";
|
|
115
|
+
if (!href) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const matchPrefixes = Array.from(/* @__PURE__ */ new Set([href, ...normalizeStringArray(entry.matchPrefixes)]));
|
|
119
|
+
sections.push({
|
|
120
|
+
id,
|
|
121
|
+
label,
|
|
122
|
+
href,
|
|
123
|
+
matchPrefixes,
|
|
124
|
+
...normalizeIcon(entry.icon) ? { icon: normalizeIcon(entry.icon) } : {},
|
|
125
|
+
...normalizeRoles(entry.roles) ? { roles: normalizeRoles(entry.roles) } : {},
|
|
126
|
+
...normalizeCard(entry.card) ? { card: normalizeCard(entry.card) } : {}
|
|
127
|
+
});
|
|
128
|
+
seen.add(id);
|
|
129
|
+
}
|
|
130
|
+
return sections;
|
|
131
|
+
};
|
|
132
|
+
var resolveStudioSectionViews = (value) => {
|
|
133
|
+
if (!Array.isArray(value)) {
|
|
134
|
+
return {};
|
|
135
|
+
}
|
|
136
|
+
const views = {};
|
|
137
|
+
for (const entry of value) {
|
|
138
|
+
if (!isRecord(entry) || typeof entry.id !== "string" || !isRecord(entry.view)) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const id = entry.id.trim();
|
|
142
|
+
const view = entry.view;
|
|
143
|
+
const component = isRecord(view.Component) ? view.Component : null;
|
|
144
|
+
if (!id || typeof view.path !== "string" || !component || typeof component.exportName !== "string" || typeof component.path !== "string") {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const path2 = normalizePathLikeValue(view.path);
|
|
148
|
+
const componentPath = component.path.trim();
|
|
149
|
+
const exportName = component.exportName.trim();
|
|
150
|
+
if (!path2 || !componentPath || !exportName) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
views[id] = {
|
|
154
|
+
path: path2,
|
|
155
|
+
Component: {
|
|
156
|
+
exportName,
|
|
157
|
+
path: componentPath,
|
|
158
|
+
...isRecord(component.clientProps) ? { clientProps: component.clientProps } : {}
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
return views;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// src/admin/helpers/configureAdmin.ts
|
|
166
|
+
function getPkgDistDir() {
|
|
167
|
+
try {
|
|
168
|
+
if (typeof import.meta?.url === "string") {
|
|
169
|
+
return path.dirname(fileURLToPath(import.meta.url));
|
|
170
|
+
}
|
|
171
|
+
} catch {
|
|
172
|
+
}
|
|
173
|
+
if (typeof __dirname === "string") {
|
|
174
|
+
return __dirname;
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
const pkgJson = __require.resolve("@orion-studios/payload-studio/package.json");
|
|
178
|
+
return path.resolve(path.dirname(pkgJson), "dist");
|
|
179
|
+
} catch {
|
|
180
|
+
return process.cwd();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function configureAdmin(config) {
|
|
184
|
+
const {
|
|
185
|
+
brandName,
|
|
186
|
+
brandPrimary = "#3b82f6",
|
|
187
|
+
brandSecondary = "#8b5cf6",
|
|
188
|
+
defaultTheme = "brand-light",
|
|
189
|
+
logoOnDarkUrl,
|
|
190
|
+
logoUrl,
|
|
191
|
+
allowThemePreference = false,
|
|
192
|
+
userSessionDurationSeconds = 60 * 60 * 24
|
|
193
|
+
} = config;
|
|
194
|
+
const studioEnabled = config.studio?.enabled ?? true;
|
|
195
|
+
const formsEnabled = config.studio?.forms?.enabled ?? false;
|
|
196
|
+
const formsCollectionSlug = config.studio?.forms?.collectionSlug || "forms";
|
|
197
|
+
const formSubmissionsCollectionSlug = config.studio?.forms?.submissionsCollectionSlug || "form-submissions";
|
|
198
|
+
const formUploadsCollectionSlug = config.studio?.forms?.uploadsCollectionSlug || "form-uploads";
|
|
199
|
+
const pagesCollectionSlug = config.studio?.pages?.collectionSlug || "pages";
|
|
200
|
+
const builderBasePath = config.studio?.pages?.builderBasePath || "/builder";
|
|
201
|
+
const mediaCollectionSlug = config.studio?.media?.collectionSlug || "media";
|
|
202
|
+
const globalsBasePath = "/site-globals";
|
|
203
|
+
const pagesBasePath = "/pages";
|
|
204
|
+
const formsBasePath = "/forms";
|
|
205
|
+
const mediaBasePath = "/media";
|
|
206
|
+
const toolsBasePath = "/tools";
|
|
207
|
+
const contactFormStudioPath = "/contact-form";
|
|
208
|
+
const configuredGlobals = config.studio?.globals || [
|
|
209
|
+
{ slug: "site-settings", label: "Website Settings" },
|
|
210
|
+
{ slug: "header", label: "Header & Navigation" },
|
|
211
|
+
{ slug: "footer", label: "Footer" },
|
|
212
|
+
{ slug: "social-media", label: "Social Media" }
|
|
213
|
+
];
|
|
214
|
+
const globals = configuredGlobals.map((global) => {
|
|
215
|
+
if (global.slug !== "contact-form" || global.href) {
|
|
216
|
+
return global;
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
...global,
|
|
220
|
+
href: contactFormStudioPath
|
|
221
|
+
};
|
|
222
|
+
});
|
|
223
|
+
const studioSections = resolveStudioSections(config.studio?.sections || []);
|
|
224
|
+
const studioSectionViews = resolveStudioSectionViews(config.studio?.sections || []);
|
|
225
|
+
const sitePreview = config.studio?.sitePreview;
|
|
226
|
+
let cssPath;
|
|
227
|
+
const pkgDist = getPkgDistDir();
|
|
228
|
+
const sourceCssPath = path.resolve(pkgDist, "admin.css");
|
|
229
|
+
const adminAppCssPath = path.resolve(pkgDist, "admin-app", "styles.css");
|
|
230
|
+
const cssSources = [sourceCssPath, adminAppCssPath].filter((filePath) => fs.existsSync(filePath));
|
|
231
|
+
if (cssSources.length === 0) {
|
|
232
|
+
cssPath = sourceCssPath;
|
|
233
|
+
} else {
|
|
234
|
+
let css = cssSources.map((filePath) => fs.readFileSync(filePath, "utf-8")).join("\n\n");
|
|
235
|
+
css = css.replace(
|
|
236
|
+
"--orion-cms-brand-primary-fallback: #3b82f6;",
|
|
237
|
+
`--orion-cms-brand-primary-fallback: ${brandPrimary};`
|
|
238
|
+
);
|
|
239
|
+
css = css.replace(
|
|
240
|
+
"--orion-cms-brand-secondary-fallback: #8b5cf6;",
|
|
241
|
+
`--orion-cms-brand-secondary-fallback: ${brandSecondary};`
|
|
242
|
+
);
|
|
243
|
+
const outputBasePath = config.basePath || process.cwd();
|
|
244
|
+
const genDir = path.resolve(outputBasePath, ".generated");
|
|
245
|
+
if (!fs.existsSync(genDir)) {
|
|
246
|
+
fs.mkdirSync(genDir, { recursive: true });
|
|
247
|
+
}
|
|
248
|
+
const genPath = path.resolve(genDir, "admin.css");
|
|
249
|
+
fs.writeFileSync(genPath, css);
|
|
250
|
+
cssPath = genPath;
|
|
251
|
+
}
|
|
252
|
+
const clientPath = "@orion-studios/payload-studio/admin/client";
|
|
253
|
+
const studioNavClientProps = {
|
|
254
|
+
brandName,
|
|
255
|
+
formSubmissionsCollectionSlug,
|
|
256
|
+
formsCollectionSlug,
|
|
257
|
+
formsEnabled,
|
|
258
|
+
formUploadsCollectionSlug,
|
|
259
|
+
globalsBasePath,
|
|
260
|
+
globalsExtraMatchPrefixes: [contactFormStudioPath],
|
|
261
|
+
logoUrl,
|
|
262
|
+
mediaCollectionSlug,
|
|
263
|
+
pagesCollectionSlug,
|
|
264
|
+
sections: studioSections
|
|
265
|
+
};
|
|
266
|
+
const studioBackBreadcrumbComponent = {
|
|
267
|
+
exportName: "StudioBackBreadcrumb",
|
|
268
|
+
path: clientPath
|
|
269
|
+
};
|
|
270
|
+
const hasMatchingComponent = (items, exportName) => Array.isArray(items) && items.some(
|
|
271
|
+
(item) => item && typeof item === "object" && item.exportName === exportName && item.path === clientPath
|
|
272
|
+
);
|
|
273
|
+
const appendComponent = (items, component, exportName) => hasMatchingComponent(items, exportName) ? items || [] : [...items || [], component];
|
|
274
|
+
const attachStudioBackBreadcrumbToCollection = (collection) => {
|
|
275
|
+
if (!studioEnabled) {
|
|
276
|
+
return collection;
|
|
277
|
+
}
|
|
278
|
+
const existingBeforeDocumentControls = collection.admin?.components?.edit?.beforeDocumentControls;
|
|
279
|
+
return {
|
|
280
|
+
...collection,
|
|
281
|
+
admin: {
|
|
282
|
+
...collection.admin,
|
|
283
|
+
components: {
|
|
284
|
+
...collection.admin?.components,
|
|
285
|
+
edit: {
|
|
286
|
+
...collection.admin?.components?.edit,
|
|
287
|
+
beforeDocumentControls: appendComponent(
|
|
288
|
+
existingBeforeDocumentControls,
|
|
289
|
+
studioBackBreadcrumbComponent,
|
|
290
|
+
"StudioBackBreadcrumb"
|
|
291
|
+
)
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
};
|
|
297
|
+
const attachStudioEditRedirectToCollection = (collection, options) => {
|
|
298
|
+
if (!studioEnabled) {
|
|
299
|
+
return collection;
|
|
300
|
+
}
|
|
301
|
+
const collectionWithBreadcrumb = attachStudioBackBreadcrumbToCollection(collection);
|
|
302
|
+
const existingViews = collectionWithBreadcrumb.admin?.components?.views;
|
|
303
|
+
const existingEditViews = existingViews?.edit;
|
|
304
|
+
const hasCustomEditView = Boolean(
|
|
305
|
+
existingEditViews?.root || existingEditViews?.default && typeof existingEditViews.default === "object" && existingEditViews.default.Component
|
|
306
|
+
);
|
|
307
|
+
if (hasCustomEditView) {
|
|
308
|
+
return collectionWithBreadcrumb;
|
|
309
|
+
}
|
|
310
|
+
return {
|
|
311
|
+
...collectionWithBreadcrumb,
|
|
312
|
+
admin: {
|
|
313
|
+
...collectionWithBreadcrumb.admin,
|
|
314
|
+
components: {
|
|
315
|
+
...collectionWithBreadcrumb.admin?.components,
|
|
316
|
+
views: {
|
|
317
|
+
...existingViews,
|
|
318
|
+
edit: {
|
|
319
|
+
...existingEditViews,
|
|
320
|
+
default: {
|
|
321
|
+
...typeof existingEditViews?.default === "object" ? existingEditViews.default : {},
|
|
322
|
+
Component: {
|
|
323
|
+
exportName: "StudioDocumentRedirect",
|
|
324
|
+
path: clientPath,
|
|
325
|
+
clientProps: {
|
|
326
|
+
description: options.description,
|
|
327
|
+
...options.emptyHref ? { emptyHref: options.emptyHref } : {},
|
|
328
|
+
...options.emptyLabel ? { emptyLabel: options.emptyLabel } : {},
|
|
329
|
+
pathBase: options.pathBase,
|
|
330
|
+
title: options.title
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
};
|
|
340
|
+
return {
|
|
341
|
+
admin: {
|
|
342
|
+
css: cssPath,
|
|
343
|
+
components: {
|
|
344
|
+
...studioEnabled ? {
|
|
345
|
+
Nav: {
|
|
346
|
+
exportName: "AdminStudioNav",
|
|
347
|
+
path: clientPath,
|
|
348
|
+
clientProps: studioNavClientProps
|
|
349
|
+
}
|
|
350
|
+
} : {},
|
|
351
|
+
graphics: {
|
|
352
|
+
Logo: {
|
|
353
|
+
exportName: "Logo",
|
|
354
|
+
path: clientPath,
|
|
355
|
+
clientProps: {
|
|
356
|
+
brandName,
|
|
357
|
+
logoOnDarkUrl,
|
|
358
|
+
logoUrl
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
Icon: {
|
|
362
|
+
exportName: "Icon",
|
|
363
|
+
path: clientPath,
|
|
364
|
+
clientProps: {
|
|
365
|
+
brandName,
|
|
366
|
+
logoOnDarkUrl,
|
|
367
|
+
logoUrl
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
views: {
|
|
372
|
+
dashboard: {
|
|
373
|
+
Component: {
|
|
374
|
+
exportName: studioEnabled ? "AdminStudioDashboard" : "Dashboard",
|
|
375
|
+
path: clientPath,
|
|
376
|
+
clientProps: studioNavClientProps
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
...studioEnabled ? {
|
|
380
|
+
studioGlobals: {
|
|
381
|
+
exact: true,
|
|
382
|
+
path: globalsBasePath,
|
|
383
|
+
Component: {
|
|
384
|
+
exportName: "AdminStudioGlobalsView",
|
|
385
|
+
path: clientPath,
|
|
386
|
+
clientProps: {
|
|
387
|
+
...studioNavClientProps,
|
|
388
|
+
globals,
|
|
389
|
+
globalsBasePath
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
studioPageNew: {
|
|
394
|
+
exact: true,
|
|
395
|
+
path: `${pagesBasePath}/new`,
|
|
396
|
+
Component: {
|
|
397
|
+
exportName: "AdminStudioNewPageView",
|
|
398
|
+
path: clientPath,
|
|
399
|
+
clientProps: {
|
|
400
|
+
...studioNavClientProps,
|
|
401
|
+
pagesCollectionSlug
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
studioPageEditor: {
|
|
406
|
+
exact: true,
|
|
407
|
+
path: `${pagesBasePath}/:id`,
|
|
408
|
+
Component: {
|
|
409
|
+
exportName: "AdminStudioPageEditView",
|
|
410
|
+
path: clientPath,
|
|
411
|
+
clientProps: {
|
|
412
|
+
...studioNavClientProps,
|
|
413
|
+
builderBasePath
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
},
|
|
417
|
+
studioPages: {
|
|
418
|
+
exact: true,
|
|
419
|
+
path: pagesBasePath,
|
|
420
|
+
Component: {
|
|
421
|
+
exportName: "AdminStudioPagesListView",
|
|
422
|
+
path: clientPath,
|
|
423
|
+
clientProps: {
|
|
424
|
+
...studioNavClientProps,
|
|
425
|
+
pagesCollectionSlug
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
studioContactForm: {
|
|
430
|
+
exact: true,
|
|
431
|
+
path: contactFormStudioPath,
|
|
432
|
+
Component: {
|
|
433
|
+
exportName: "AdminStudioContactFormView",
|
|
434
|
+
path: clientPath,
|
|
435
|
+
clientProps: {
|
|
436
|
+
...studioNavClientProps,
|
|
437
|
+
globalSlug: "contact-form",
|
|
438
|
+
globalsBasePath
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
...formsEnabled ? {
|
|
443
|
+
studioFormSubmission: {
|
|
444
|
+
exact: true,
|
|
445
|
+
path: `${formsBasePath}/submissions/:id`,
|
|
446
|
+
Component: {
|
|
447
|
+
exportName: "AdminStudioFormSubmissionView",
|
|
448
|
+
path: clientPath,
|
|
449
|
+
clientProps: {
|
|
450
|
+
...studioNavClientProps,
|
|
451
|
+
formsCollectionSlug,
|
|
452
|
+
formSubmissionsCollectionSlug,
|
|
453
|
+
formUploadsCollectionSlug
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
},
|
|
457
|
+
studioFormUpload: {
|
|
458
|
+
exact: true,
|
|
459
|
+
path: `${formsBasePath}/uploads/:id`,
|
|
460
|
+
Component: {
|
|
461
|
+
exportName: "AdminStudioFormUploadView",
|
|
462
|
+
path: clientPath,
|
|
463
|
+
clientProps: {
|
|
464
|
+
...studioNavClientProps,
|
|
465
|
+
formUploadsCollectionSlug
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
studioFormDetail: {
|
|
470
|
+
exact: true,
|
|
471
|
+
path: `${formsBasePath}/:id`,
|
|
472
|
+
Component: {
|
|
473
|
+
exportName: "AdminStudioFormDetailView",
|
|
474
|
+
path: clientPath,
|
|
475
|
+
clientProps: {
|
|
476
|
+
...studioNavClientProps,
|
|
477
|
+
formsCollectionSlug,
|
|
478
|
+
formSubmissionsCollectionSlug
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
},
|
|
482
|
+
studioForms: {
|
|
483
|
+
exact: true,
|
|
484
|
+
path: formsBasePath,
|
|
485
|
+
Component: {
|
|
486
|
+
exportName: "AdminStudioFormsView",
|
|
487
|
+
path: clientPath,
|
|
488
|
+
clientProps: {
|
|
489
|
+
...studioNavClientProps,
|
|
490
|
+
formsCollectionSlug,
|
|
491
|
+
formSubmissionsCollectionSlug,
|
|
492
|
+
formUploadsCollectionSlug
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
} : {},
|
|
497
|
+
studioMediaItem: {
|
|
498
|
+
exact: true,
|
|
499
|
+
path: `${mediaBasePath}/:id`,
|
|
500
|
+
Component: {
|
|
501
|
+
exportName: "AdminStudioMediaItemView",
|
|
502
|
+
path: clientPath,
|
|
503
|
+
clientProps: {
|
|
504
|
+
...studioNavClientProps,
|
|
505
|
+
mediaCollectionSlug
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
},
|
|
509
|
+
studioMedia: {
|
|
510
|
+
exact: true,
|
|
511
|
+
path: mediaBasePath,
|
|
512
|
+
Component: {
|
|
513
|
+
exportName: "AdminStudioMediaView",
|
|
514
|
+
path: clientPath,
|
|
515
|
+
clientProps: {
|
|
516
|
+
...studioNavClientProps,
|
|
517
|
+
mediaCollectionSlug
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
},
|
|
521
|
+
studioTools: {
|
|
522
|
+
exact: true,
|
|
523
|
+
path: toolsBasePath,
|
|
524
|
+
Component: {
|
|
525
|
+
exportName: "AdminStudioToolsView",
|
|
526
|
+
path: clientPath,
|
|
527
|
+
clientProps: {
|
|
528
|
+
...studioNavClientProps,
|
|
529
|
+
mediaCollectionSlug,
|
|
530
|
+
pagesCollectionSlug
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
},
|
|
534
|
+
...Object.fromEntries(
|
|
535
|
+
Object.entries(studioSectionViews).map(([id, view]) => [
|
|
536
|
+
id,
|
|
537
|
+
{
|
|
538
|
+
path: view.path,
|
|
539
|
+
Component: {
|
|
540
|
+
...view.Component,
|
|
541
|
+
clientProps: {
|
|
542
|
+
...studioNavClientProps,
|
|
543
|
+
...view.Component.clientProps || {}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
])
|
|
548
|
+
)
|
|
549
|
+
} : {}
|
|
550
|
+
},
|
|
551
|
+
providers: [
|
|
552
|
+
{
|
|
553
|
+
exportName: "ThemeProvider",
|
|
554
|
+
path: clientPath,
|
|
555
|
+
clientProps: {
|
|
556
|
+
allowThemePreference,
|
|
557
|
+
defaultTheme
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
],
|
|
561
|
+
beforeLogin: [
|
|
562
|
+
{
|
|
563
|
+
exportName: "AdminLoginIntro",
|
|
564
|
+
path: clientPath,
|
|
565
|
+
clientProps: {
|
|
566
|
+
brandName,
|
|
567
|
+
logoOnDarkUrl,
|
|
568
|
+
logoUrl
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
],
|
|
572
|
+
afterLogin: [
|
|
573
|
+
{
|
|
574
|
+
exportName: "AdminLoginPasswordToggle",
|
|
575
|
+
path: clientPath
|
|
576
|
+
}
|
|
577
|
+
],
|
|
578
|
+
...allowThemePreference ? {
|
|
579
|
+
afterNavLinks: [
|
|
580
|
+
{
|
|
581
|
+
exportName: "ThemeSwitcher",
|
|
582
|
+
path: clientPath,
|
|
583
|
+
clientProps: {
|
|
584
|
+
allowThemePreference,
|
|
585
|
+
defaultTheme
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
]
|
|
589
|
+
} : {}
|
|
590
|
+
},
|
|
591
|
+
meta: {
|
|
592
|
+
titleSuffix: ` \u2014 ${brandName}`
|
|
593
|
+
}
|
|
594
|
+
},
|
|
595
|
+
brandName,
|
|
596
|
+
brandPrimary,
|
|
597
|
+
brandSecondary,
|
|
598
|
+
defaultTheme,
|
|
599
|
+
wrapUsers(usersCollection) {
|
|
600
|
+
const existingFields = usersCollection.fields || [];
|
|
601
|
+
const hasThemePreference = existingFields.some(
|
|
602
|
+
(field) => typeof field === "object" && field !== null && "name" in field && field.name === "themePreference"
|
|
603
|
+
);
|
|
604
|
+
const normalizedAuth = usersCollection.auth === true ? {
|
|
605
|
+
tokenExpiration: userSessionDurationSeconds,
|
|
606
|
+
useSessions: true
|
|
607
|
+
} : usersCollection.auth && typeof usersCollection.auth === "object" ? {
|
|
608
|
+
...usersCollection.auth,
|
|
609
|
+
tokenExpiration: usersCollection.auth.tokenExpiration ?? userSessionDurationSeconds,
|
|
610
|
+
useSessions: usersCollection.auth.useSessions ?? true
|
|
611
|
+
} : usersCollection.auth;
|
|
612
|
+
const nextCollection = {
|
|
613
|
+
...usersCollection,
|
|
614
|
+
auth: normalizedAuth,
|
|
615
|
+
fields: !allowThemePreference || hasThemePreference ? existingFields : [...existingFields, createThemePreferenceField(defaultTheme)]
|
|
616
|
+
};
|
|
617
|
+
return attachStudioBackBreadcrumbToCollection(nextCollection);
|
|
618
|
+
},
|
|
619
|
+
wrapPagesCollection(pagesCollection) {
|
|
620
|
+
if (!studioEnabled) {
|
|
621
|
+
return pagesCollection;
|
|
622
|
+
}
|
|
623
|
+
const collectionWithBreadcrumb = attachStudioBackBreadcrumbToCollection(pagesCollection);
|
|
624
|
+
const existingEditMenuItems = collectionWithBreadcrumb.admin?.components?.edit?.editMenuItems;
|
|
625
|
+
const existingViews = collectionWithBreadcrumb.admin?.components?.views;
|
|
626
|
+
const existingEditViews = existingViews?.edit;
|
|
627
|
+
const hasCustomEditView = Boolean(
|
|
628
|
+
existingEditViews?.root || existingEditViews?.default && typeof existingEditViews.default === "object" && existingEditViews.default.Component
|
|
629
|
+
);
|
|
630
|
+
return {
|
|
631
|
+
...collectionWithBreadcrumb,
|
|
632
|
+
admin: {
|
|
633
|
+
...collectionWithBreadcrumb.admin,
|
|
634
|
+
components: {
|
|
635
|
+
...collectionWithBreadcrumb.admin?.components,
|
|
636
|
+
edit: {
|
|
637
|
+
...collectionWithBreadcrumb.admin?.components?.edit,
|
|
638
|
+
editMenuItems: appendComponent(
|
|
639
|
+
existingEditMenuItems,
|
|
640
|
+
{
|
|
641
|
+
exportName: "OpenInStudioMenuItem",
|
|
642
|
+
path: clientPath,
|
|
643
|
+
clientProps: {
|
|
644
|
+
pagesPathBase: pagesBasePath
|
|
645
|
+
}
|
|
646
|
+
},
|
|
647
|
+
"OpenInStudioMenuItem"
|
|
648
|
+
)
|
|
649
|
+
},
|
|
650
|
+
views: {
|
|
651
|
+
...existingViews,
|
|
652
|
+
...hasCustomEditView ? {} : {
|
|
653
|
+
edit: {
|
|
654
|
+
...existingEditViews,
|
|
655
|
+
default: {
|
|
656
|
+
...typeof existingEditViews?.default === "object" ? existingEditViews.default : {},
|
|
657
|
+
Component: {
|
|
658
|
+
exportName: "PageEditRedirectToStudio",
|
|
659
|
+
path: clientPath,
|
|
660
|
+
clientProps: {
|
|
661
|
+
pagesPathBase: pagesBasePath
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
},
|
|
672
|
+
wrapMediaCollection(mediaCollection) {
|
|
673
|
+
return attachStudioBackBreadcrumbToCollection(mediaCollection);
|
|
674
|
+
},
|
|
675
|
+
wrapFormsCollection(formsCollection) {
|
|
676
|
+
return attachStudioEditRedirectToCollection(formsCollection, {
|
|
677
|
+
description: "Redirecting to the Studio form workspace.",
|
|
678
|
+
emptyHref: formsBasePath,
|
|
679
|
+
emptyLabel: "Open Forms",
|
|
680
|
+
pathBase: formsBasePath,
|
|
681
|
+
title: "Opening Form..."
|
|
682
|
+
});
|
|
683
|
+
},
|
|
684
|
+
wrapFormSubmissionsCollection(formSubmissionsCollection) {
|
|
685
|
+
return attachStudioEditRedirectToCollection(formSubmissionsCollection, {
|
|
686
|
+
description: "Redirecting to the Studio submission workspace.",
|
|
687
|
+
emptyHref: formsBasePath,
|
|
688
|
+
emptyLabel: "Open Forms",
|
|
689
|
+
pathBase: `${formsBasePath}/submissions`,
|
|
690
|
+
title: "Opening Submission..."
|
|
691
|
+
});
|
|
692
|
+
},
|
|
693
|
+
wrapFormUploadsCollection(formUploadsCollection) {
|
|
694
|
+
return attachStudioEditRedirectToCollection(formUploadsCollection, {
|
|
695
|
+
description: "Redirecting to the Studio upload workspace.",
|
|
696
|
+
emptyHref: formsBasePath,
|
|
697
|
+
emptyLabel: "Open Forms",
|
|
698
|
+
pathBase: `${formsBasePath}/uploads`,
|
|
699
|
+
title: "Opening Upload..."
|
|
700
|
+
});
|
|
701
|
+
},
|
|
702
|
+
wrapGlobals(globals2) {
|
|
703
|
+
const labelMap = {
|
|
704
|
+
header: { group: "Site Design", label: "Header & Navigation" },
|
|
705
|
+
footer: { group: "Site Design", label: "Footer" },
|
|
706
|
+
"site-settings": { group: "Site Design", label: "Website Settings" },
|
|
707
|
+
"social-media": { group: "Site Design", label: "Social Media" },
|
|
708
|
+
"contact-form": { group: "Lead Forms", label: "Contact Form" }
|
|
709
|
+
};
|
|
710
|
+
return globals2.map((global) => {
|
|
711
|
+
const mapping = labelMap[global.slug];
|
|
712
|
+
if (!mapping) return global;
|
|
713
|
+
const shouldAttachSiteSettingsEditView = studioEnabled && global.slug === "site-settings";
|
|
714
|
+
const shouldAttachSocialMediaEditView = studioEnabled && global.slug === "social-media";
|
|
715
|
+
const shouldAttachContactFormRedirect = studioEnabled && global.slug === "contact-form";
|
|
716
|
+
const shouldAttachHeaderEditView = studioEnabled && global.slug === "header";
|
|
717
|
+
const shouldAttachFooterEditView = studioEnabled && global.slug === "footer";
|
|
718
|
+
const existingViews = global.admin?.components?.views;
|
|
719
|
+
const existingEditViews = existingViews?.edit;
|
|
720
|
+
const hasCustomEditView = Boolean(
|
|
721
|
+
existingEditViews?.root || existingEditViews?.default && typeof existingEditViews.default === "object" && existingEditViews.default.Component
|
|
722
|
+
);
|
|
723
|
+
const nextEditViews = (() => {
|
|
724
|
+
if (shouldAttachSiteSettingsEditView && !hasCustomEditView) {
|
|
725
|
+
return {
|
|
726
|
+
...existingEditViews,
|
|
727
|
+
default: {
|
|
728
|
+
...typeof existingEditViews?.default === "object" ? existingEditViews.default : {},
|
|
729
|
+
Component: {
|
|
730
|
+
exportName: "AdminStudioSiteSettingsGlobalView",
|
|
731
|
+
path: clientPath,
|
|
732
|
+
clientProps: {
|
|
733
|
+
...studioNavClientProps,
|
|
734
|
+
globalSlug: global.slug,
|
|
735
|
+
mediaCollectionSlug
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
if (shouldAttachSocialMediaEditView && !hasCustomEditView) {
|
|
742
|
+
return {
|
|
743
|
+
...existingEditViews,
|
|
744
|
+
default: {
|
|
745
|
+
...typeof existingEditViews?.default === "object" ? existingEditViews.default : {},
|
|
746
|
+
Component: {
|
|
747
|
+
exportName: "AdminStudioSocialMediaGlobalView",
|
|
748
|
+
path: clientPath,
|
|
749
|
+
clientProps: {
|
|
750
|
+
...studioNavClientProps,
|
|
751
|
+
globalSlug: global.slug
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
if (shouldAttachHeaderEditView && !hasCustomEditView) {
|
|
758
|
+
return {
|
|
759
|
+
...existingEditViews,
|
|
760
|
+
default: {
|
|
761
|
+
...typeof existingEditViews?.default === "object" ? existingEditViews.default : {},
|
|
762
|
+
Component: {
|
|
763
|
+
exportName: "AdminStudioHeaderGlobalView",
|
|
764
|
+
path: clientPath,
|
|
765
|
+
clientProps: {
|
|
766
|
+
...studioNavClientProps,
|
|
767
|
+
actionHref: sitePreview?.header?.actionHref,
|
|
768
|
+
actionLabel: sitePreview?.header?.actionLabel,
|
|
769
|
+
globalSlug: global.slug,
|
|
770
|
+
locationSummary: sitePreview?.locationSummary,
|
|
771
|
+
pagesCollectionSlug
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
if (shouldAttachFooterEditView && !hasCustomEditView) {
|
|
778
|
+
return {
|
|
779
|
+
...existingEditViews,
|
|
780
|
+
default: {
|
|
781
|
+
...typeof existingEditViews?.default === "object" ? existingEditViews.default : {},
|
|
782
|
+
Component: {
|
|
783
|
+
exportName: "AdminStudioFooterGlobalView",
|
|
784
|
+
path: clientPath,
|
|
785
|
+
clientProps: {
|
|
786
|
+
...studioNavClientProps,
|
|
787
|
+
builtByHref: sitePreview?.footer?.builtByHref,
|
|
788
|
+
builtByLabel: sitePreview?.footer?.builtByLabel,
|
|
789
|
+
description: sitePreview?.footer?.description,
|
|
790
|
+
footerCategories: sitePreview?.footer?.footerCategories,
|
|
791
|
+
footerLinks: sitePreview?.footer?.footerLinks,
|
|
792
|
+
globalSlug: global.slug,
|
|
793
|
+
locationSummary: sitePreview?.locationSummary
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
if (shouldAttachContactFormRedirect && !hasCustomEditView) {
|
|
800
|
+
return {
|
|
801
|
+
...existingEditViews,
|
|
802
|
+
default: {
|
|
803
|
+
...typeof existingEditViews?.default === "object" ? existingEditViews.default : {},
|
|
804
|
+
Component: {
|
|
805
|
+
exportName: "StudioContactFormRedirect",
|
|
806
|
+
path: clientPath,
|
|
807
|
+
clientProps: {
|
|
808
|
+
studioContactFormPath: contactFormStudioPath
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
return existingEditViews;
|
|
815
|
+
})();
|
|
816
|
+
const existingBeforeDocumentControls = global.admin?.components?.elements?.beforeDocumentControls;
|
|
817
|
+
return {
|
|
818
|
+
...global,
|
|
819
|
+
admin: {
|
|
820
|
+
...global.admin,
|
|
821
|
+
group: mapping.group,
|
|
822
|
+
components: {
|
|
823
|
+
...global.admin?.components,
|
|
824
|
+
elements: {
|
|
825
|
+
...global.admin?.components?.elements,
|
|
826
|
+
beforeDocumentControls: studioEnabled ? appendComponent(
|
|
827
|
+
existingBeforeDocumentControls,
|
|
828
|
+
studioBackBreadcrumbComponent,
|
|
829
|
+
"StudioBackBreadcrumb"
|
|
830
|
+
) : existingBeforeDocumentControls
|
|
831
|
+
},
|
|
832
|
+
...nextEditViews ? {
|
|
833
|
+
views: {
|
|
834
|
+
...existingViews,
|
|
835
|
+
edit: nextEditViews
|
|
836
|
+
}
|
|
837
|
+
} : {}
|
|
838
|
+
}
|
|
839
|
+
},
|
|
840
|
+
label: mapping.label
|
|
841
|
+
};
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// src/admin/helpers/withTooltips.ts
|
|
848
|
+
var defaultTooltips = {
|
|
849
|
+
title: "The main title displayed on this page.",
|
|
850
|
+
slug: 'The URL-friendly name for this page (e.g., "about-us"). This appears in the web address.',
|
|
851
|
+
template: "Choose a layout template. This controls the overall structure of the page.",
|
|
852
|
+
parent: "Select a parent page to nest this page under. This affects the URL path.",
|
|
853
|
+
path: "The full URL path for this page. This is automatically generated from the slug and parent.",
|
|
854
|
+
layout: "Add and arrange content sections on your page. Each section is a building block.",
|
|
855
|
+
metaTitle: "The title shown in search engine results and browser tabs. Keep under 60 characters.",
|
|
856
|
+
metaDescription: "A brief summary shown in search results. Keep under 160 characters for best results.",
|
|
857
|
+
canonicalUrl: "The preferred URL for this page. Used to prevent duplicate content in search engines.",
|
|
858
|
+
ogImage: "The image shown when this page is shared on social media (Facebook, LinkedIn, etc.).",
|
|
859
|
+
noIndex: "When enabled, search engines will not list this page in results.",
|
|
860
|
+
noFollow: "When enabled, search engines will not follow links on this page.",
|
|
861
|
+
publishedAt: "The date and time this page was first published.",
|
|
862
|
+
alt: "Describe this image for screen readers and search engines. Be specific and concise.",
|
|
863
|
+
navItems: "The links shown in your website's navigation menu.",
|
|
864
|
+
copyright: "The copyright text displayed in your website footer.",
|
|
865
|
+
contactEmail: "The email address displayed in your footer and contact sections.",
|
|
866
|
+
contactPhone: "The phone number displayed in your footer and contact sections.",
|
|
867
|
+
siteName: "Your website's name. Appears in browser tabs and search results.",
|
|
868
|
+
tagline: "A short phrase describing your business. Used in SEO and site metadata.",
|
|
869
|
+
canonicalBaseUrl: 'The base URL of your website (e.g., "https://example.com"). Used for generating canonical URLs.'
|
|
870
|
+
};
|
|
871
|
+
function withTooltips(fields, customTooltips) {
|
|
872
|
+
const tooltips = { ...defaultTooltips, ...customTooltips };
|
|
873
|
+
return fields.map((field) => addTooltipToField(field, tooltips));
|
|
874
|
+
}
|
|
875
|
+
function addTooltipToField(field, tooltips) {
|
|
876
|
+
if ("name" in field && field.name && tooltips[field.name]) {
|
|
877
|
+
const tooltip = tooltips[field.name];
|
|
878
|
+
const admin = field.admin;
|
|
879
|
+
if (!admin?.description) {
|
|
880
|
+
return {
|
|
881
|
+
...field,
|
|
882
|
+
admin: {
|
|
883
|
+
...admin,
|
|
884
|
+
description: tooltip
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
if ("fields" in field && Array.isArray(field.fields)) {
|
|
890
|
+
return {
|
|
891
|
+
...field,
|
|
892
|
+
fields: field.fields.map((f) => addTooltipToField(f, tooltips))
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
if ("tabs" in field && Array.isArray(field.tabs)) {
|
|
896
|
+
return {
|
|
897
|
+
...field,
|
|
898
|
+
tabs: field.tabs.map((tab) => ({
|
|
899
|
+
...tab,
|
|
900
|
+
fields: tab.fields ? tab.fields.map((f) => addTooltipToField(f, tooltips)) : tab.fields
|
|
901
|
+
}))
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
return field;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// src/admin/fields/headerNav.ts
|
|
908
|
+
var createHeaderNavItemsField = () => ({
|
|
909
|
+
name: "navItems",
|
|
910
|
+
type: "array",
|
|
911
|
+
labels: { singular: "Navigation Link", plural: "Navigation Links" },
|
|
912
|
+
admin: {
|
|
913
|
+
description: "The links displayed in your website's main navigation menu."
|
|
914
|
+
},
|
|
915
|
+
fields: [
|
|
916
|
+
{
|
|
917
|
+
name: "label",
|
|
918
|
+
type: "text",
|
|
919
|
+
required: true,
|
|
920
|
+
admin: {
|
|
921
|
+
description: "The text shown for this navigation link."
|
|
922
|
+
}
|
|
923
|
+
},
|
|
924
|
+
{
|
|
925
|
+
name: "href",
|
|
926
|
+
type: "text",
|
|
927
|
+
required: true,
|
|
928
|
+
admin: {
|
|
929
|
+
description: 'The URL this link points to (e.g., "/about" or "https://example.com").'
|
|
930
|
+
}
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
name: "parentHref",
|
|
934
|
+
type: "text",
|
|
935
|
+
admin: {
|
|
936
|
+
description: "Optional parent link URL. If set to another nav item href, this item appears in that dropdown."
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
]
|
|
940
|
+
});
|
|
941
|
+
|
|
942
|
+
// src/admin/fields/socialMedia.ts
|
|
943
|
+
var DEFAULT_DESCRIPTION = "Add profile URLs and choose one of two icon styles for each platform. Leave a URL blank to hide that profile on your site.";
|
|
944
|
+
function validateOptionalHttpsUrl(value) {
|
|
945
|
+
if (value === null || value === void 0 || value === "") {
|
|
946
|
+
return true;
|
|
947
|
+
}
|
|
948
|
+
if (typeof value !== "string") {
|
|
949
|
+
return "Enter a valid URL.";
|
|
950
|
+
}
|
|
951
|
+
try {
|
|
952
|
+
const url = new URL(value);
|
|
953
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
954
|
+
return "Use an http:// or https:// URL.";
|
|
955
|
+
}
|
|
956
|
+
return true;
|
|
957
|
+
} catch {
|
|
958
|
+
return "Enter a valid URL (for example: https://instagram.com/yourbrand).";
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
function createPlatformField(platform) {
|
|
962
|
+
const platformLabel = SOCIAL_MEDIA_PLATFORM_LABELS[platform];
|
|
963
|
+
const iconOptions = SOCIAL_MEDIA_ICON_OPTIONS[platform].map((option) => ({
|
|
964
|
+
label: option.label,
|
|
965
|
+
value: option.value
|
|
966
|
+
}));
|
|
967
|
+
return {
|
|
968
|
+
name: platform,
|
|
969
|
+
type: "group",
|
|
970
|
+
label: platformLabel,
|
|
971
|
+
fields: [
|
|
972
|
+
{
|
|
973
|
+
name: "url",
|
|
974
|
+
type: "text",
|
|
975
|
+
admin: {
|
|
976
|
+
description: `Full ${platformLabel} profile URL. Leave blank to hide ${platformLabel}.`
|
|
977
|
+
},
|
|
978
|
+
validate: validateOptionalHttpsUrl
|
|
979
|
+
},
|
|
980
|
+
{
|
|
981
|
+
name: "icon",
|
|
982
|
+
type: "select",
|
|
983
|
+
defaultValue: SOCIAL_MEDIA_DEFAULT_ICON_BY_PLATFORM[platform],
|
|
984
|
+
options: iconOptions,
|
|
985
|
+
admin: {
|
|
986
|
+
description: "Pick which icon style to display for this platform."
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
]
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
var createSocialMediaConnectionsField = (options = {}) => {
|
|
993
|
+
const selectedPlatforms = Array.isArray(options.platforms) && options.platforms.length > 0 ? options.platforms : SOCIAL_MEDIA_PLATFORMS;
|
|
994
|
+
return {
|
|
995
|
+
name: options.name || "profiles",
|
|
996
|
+
type: "group",
|
|
997
|
+
label: options.label || "Social Media Profiles",
|
|
998
|
+
admin: {
|
|
999
|
+
description: options.description || DEFAULT_DESCRIPTION
|
|
1000
|
+
},
|
|
1001
|
+
fields: selectedPlatforms.map((platform) => createPlatformField(platform))
|
|
1002
|
+
};
|
|
1003
|
+
};
|
|
1004
|
+
var socialMediaConnectionsField = createSocialMediaConnectionsField();
|
|
1005
|
+
|
|
1006
|
+
// src/admin/globals/socialMedia.ts
|
|
1007
|
+
var createSocialMediaGlobal = (options = {}) => ({
|
|
1008
|
+
slug: options.slug || "social-media",
|
|
1009
|
+
label: "Social Media",
|
|
1010
|
+
admin: {
|
|
1011
|
+
description: options.description || "Manage social profile links and icon variants used across your website.",
|
|
1012
|
+
group: "Site Design"
|
|
1013
|
+
},
|
|
1014
|
+
fields: [
|
|
1015
|
+
createSocialMediaConnectionsField({
|
|
1016
|
+
...options.fieldOptions,
|
|
1017
|
+
label: "Profiles",
|
|
1018
|
+
name: "profiles"
|
|
1019
|
+
})
|
|
1020
|
+
]
|
|
1021
|
+
});
|
|
1022
|
+
|
|
1023
|
+
export {
|
|
1024
|
+
createThemePreferenceField,
|
|
1025
|
+
themePreferenceField,
|
|
1026
|
+
configureAdmin,
|
|
1027
|
+
withTooltips,
|
|
1028
|
+
createHeaderNavItemsField,
|
|
1029
|
+
createSocialMediaConnectionsField,
|
|
1030
|
+
socialMediaConnectionsField,
|
|
1031
|
+
createSocialMediaGlobal,
|
|
1032
|
+
admin_exports
|
|
1033
|
+
};
|