@orion-studios/cms 0.5.1 → 0.5.2
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/README.md +24 -1
- package/dist/{chunk-HVJCF2IZ.js → chunk-AYV6KYDP.js} +3 -3
- package/dist/content/index.js +1 -1
- package/dist/forms/react.d.ts +2 -2
- package/dist/server/index.d.ts +4 -3
- package/dist/server/index.js +171 -42
- package/dist/studio/index.d.ts +14 -5
- package/dist/studio/index.js +149 -67
- package/package.json +1 -1
- package/sql/bootstrap.sql +478 -60
- package/sql/migrations/20260820033957_sec_003_exact_version_publication.sql +575 -0
package/dist/studio/index.js
CHANGED
|
@@ -58,7 +58,11 @@ function createStudioApi(options) {
|
|
|
58
58
|
getPage: (id) => call("GET", `/pages/${id}`),
|
|
59
59
|
createPage: (input) => call("POST", "/pages", input),
|
|
60
60
|
saveDraft: (id, input) => call("PATCH", `/pages/${id}`, input),
|
|
61
|
-
publish: (id) => call("POST", `/pages/${id}/publish
|
|
61
|
+
publish: (id, expectedVersionId) => call("POST", `/pages/${id}/publish`, { expectedVersionId }),
|
|
62
|
+
schedulePublish: (id, publishAt, expectedVersionId) => call("PATCH", `/pages/${id}`, {
|
|
63
|
+
publishAt,
|
|
64
|
+
...expectedVersionId !== void 0 ? { expectedVersionId } : {}
|
|
65
|
+
}),
|
|
62
66
|
unpublish: (id) => call("POST", `/pages/${id}/unpublish`),
|
|
63
67
|
duplicatePage: (id) => call("POST", `/pages/${id}/duplicate`),
|
|
64
68
|
deletePage: (id) => call("DELETE", `/pages/${id}`),
|
|
@@ -129,6 +133,75 @@ function createStudioApi(options) {
|
|
|
129
133
|
};
|
|
130
134
|
}
|
|
131
135
|
|
|
136
|
+
// src/server/permissions.ts
|
|
137
|
+
var ROLE_RANK = {
|
|
138
|
+
content: 0,
|
|
139
|
+
editor: 1,
|
|
140
|
+
admin: 2,
|
|
141
|
+
developer: 3
|
|
142
|
+
};
|
|
143
|
+
var MIN_ROLE = {
|
|
144
|
+
"pages.read": "content",
|
|
145
|
+
"pages.saveDraft": "content",
|
|
146
|
+
"pages.changeStructure": "editor",
|
|
147
|
+
"pages.publish": "editor",
|
|
148
|
+
"pages.create": "editor",
|
|
149
|
+
"pages.delete": "admin",
|
|
150
|
+
"pages.restore": "editor",
|
|
151
|
+
"globals.read": "content",
|
|
152
|
+
"globals.write": "content",
|
|
153
|
+
"media.read": "content",
|
|
154
|
+
"media.upload": "content",
|
|
155
|
+
"media.update": "content",
|
|
156
|
+
"media.replace": "editor",
|
|
157
|
+
"media.delete": "editor",
|
|
158
|
+
"forms.read": "content",
|
|
159
|
+
"forms.write": "editor",
|
|
160
|
+
"forms.delete": "admin",
|
|
161
|
+
"submissions.read": "editor",
|
|
162
|
+
"submissions.manage": "editor",
|
|
163
|
+
"redirects.manage": "editor",
|
|
164
|
+
"activity.read": "editor",
|
|
165
|
+
"analytics.read": "editor",
|
|
166
|
+
"sync.run": "admin",
|
|
167
|
+
"users.manage": "admin"
|
|
168
|
+
};
|
|
169
|
+
function can(user, action) {
|
|
170
|
+
if (!user) return false;
|
|
171
|
+
return ROLE_RANK[user.role] >= ROLE_RANK[MIN_ROLE[action]];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// src/studio/access.ts
|
|
175
|
+
var SECTION_ACTION = {
|
|
176
|
+
dashboard: "pages.read",
|
|
177
|
+
pages: "pages.read",
|
|
178
|
+
media: "media.read",
|
|
179
|
+
forms: "forms.read",
|
|
180
|
+
submissions: "submissions.read",
|
|
181
|
+
analytics: "analytics.read",
|
|
182
|
+
globals: "globals.read",
|
|
183
|
+
redirects: "redirects.manage",
|
|
184
|
+
users: "users.manage"
|
|
185
|
+
};
|
|
186
|
+
var userForRole = (role) => ({ id: "", email: null, name: "", role });
|
|
187
|
+
var studioCan = (role, action) => can(userForRole(role), action);
|
|
188
|
+
var canViewStudioSection = (role, section) => studioCan(role, SECTION_ACTION[section]);
|
|
189
|
+
var studioAccess = (role) => ({
|
|
190
|
+
changePageStructure: studioCan(role, "pages.changeStructure"),
|
|
191
|
+
publishPages: studioCan(role, "pages.publish"),
|
|
192
|
+
createPages: studioCan(role, "pages.create"),
|
|
193
|
+
deletePages: studioCan(role, "pages.delete"),
|
|
194
|
+
replaceMedia: studioCan(role, "media.replace"),
|
|
195
|
+
deleteMedia: studioCan(role, "media.delete"),
|
|
196
|
+
writeForms: studioCan(role, "forms.write"),
|
|
197
|
+
deleteForms: studioCan(role, "forms.delete"),
|
|
198
|
+
readSubmissions: studioCan(role, "submissions.read"),
|
|
199
|
+
manageSubmissions: studioCan(role, "submissions.manage"),
|
|
200
|
+
readAnalytics: studioCan(role, "analytics.read"),
|
|
201
|
+
manageRedirects: studioCan(role, "redirects.manage"),
|
|
202
|
+
manageUsers: studioCan(role, "users.manage")
|
|
203
|
+
});
|
|
204
|
+
|
|
132
205
|
// src/studio/auth.tsx
|
|
133
206
|
import { createClient } from "@supabase/supabase-js";
|
|
134
207
|
import { useCallback, useEffect, useMemo, useState as useState2 } from "react";
|
|
@@ -196,14 +269,14 @@ function getBrowserSupabase() {
|
|
|
196
269
|
}
|
|
197
270
|
return browserClient;
|
|
198
271
|
}
|
|
199
|
-
var
|
|
272
|
+
var isStudioMemoryMode = () => (!process.env.NEXT_PUBLIC_SUPABASE_URL || !process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY) && process.env.NODE_ENV !== "production";
|
|
200
273
|
var MEMORY_DEV_TOKEN = "orion-dev-token";
|
|
201
274
|
var memorySession = {
|
|
202
275
|
access_token: MEMORY_DEV_TOKEN,
|
|
203
276
|
user: { id: "dev", email: "dev@local" }
|
|
204
277
|
};
|
|
205
278
|
function useStudioSession() {
|
|
206
|
-
const memory =
|
|
279
|
+
const memory = isStudioMemoryMode();
|
|
207
280
|
const supabase = useMemo(() => memory ? null : getBrowserSupabase(), [memory]);
|
|
208
281
|
const [session, setSession] = useState2(memory ? memorySession : null);
|
|
209
282
|
const [loading, setLoading] = useState2(!memory);
|
|
@@ -634,24 +707,28 @@ function DashboardView({
|
|
|
634
707
|
api,
|
|
635
708
|
siteName,
|
|
636
709
|
onOpenPage,
|
|
637
|
-
onOpenSubmissions
|
|
710
|
+
onOpenSubmissions,
|
|
711
|
+
canReadAnalytics,
|
|
712
|
+
canReadSubmissions
|
|
638
713
|
}) {
|
|
639
714
|
const [data, setData] = useState4(null);
|
|
640
715
|
const [week, setWeek] = useState4(null);
|
|
641
716
|
const [error, setError] = useState4("");
|
|
642
717
|
useEffect3(() => {
|
|
643
718
|
api.dashboard().then(setData, (e) => setError(e.message));
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
(
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
719
|
+
if (canReadAnalytics) {
|
|
720
|
+
const to = /* @__PURE__ */ new Date();
|
|
721
|
+
const from = new Date(to.getTime() - 7 * 864e5);
|
|
722
|
+
api.analytics({ from: from.toISOString(), to: to.toISOString() }).then(
|
|
723
|
+
(summary) => setWeek({
|
|
724
|
+
visitors: summary.kpis.visitors,
|
|
725
|
+
calls: summary.kpis.calls,
|
|
726
|
+
formSubmits: summary.kpis.formSubmits
|
|
727
|
+
}),
|
|
728
|
+
() => void 0
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
}, [api, canReadAnalytics]);
|
|
655
732
|
if (error) return /* @__PURE__ */ jsx4("div", { className: "ost-view", children: /* @__PURE__ */ jsx4("div", { className: "ost-error", children: error }) });
|
|
656
733
|
if (!data) return /* @__PURE__ */ jsx4("div", { className: "ost-view", children: /* @__PURE__ */ jsx4("div", { className: "ost-loading", children: "Loading\u2026" }) });
|
|
657
734
|
const { pages, submissions, activity } = data;
|
|
@@ -681,7 +758,7 @@ function DashboardView({
|
|
|
681
758
|
] }) }),
|
|
682
759
|
/* @__PURE__ */ jsxs4("div", { className: "ost-dash-columns", children: [
|
|
683
760
|
/* @__PURE__ */ jsxs4("div", { className: "ost-dash-column", children: [
|
|
684
|
-
/* @__PURE__ */ jsxs4("section", { className: "ost-card ost-dash-card", children: [
|
|
761
|
+
canReadSubmissions && submissions ? /* @__PURE__ */ jsxs4("section", { className: "ost-card ost-dash-card", children: [
|
|
685
762
|
/* @__PURE__ */ jsxs4("header", { className: "ost-dash-card-head", children: [
|
|
686
763
|
/* @__PURE__ */ jsx4("h3", { children: "Submissions" }),
|
|
687
764
|
/* @__PURE__ */ jsx4("button", { className: "ost-btn", onClick: onOpenSubmissions, type: "button", children: "Open inbox" })
|
|
@@ -706,7 +783,7 @@ function DashboardView({
|
|
|
706
783
|
},
|
|
707
784
|
submission.id
|
|
708
785
|
))
|
|
709
|
-
] }),
|
|
786
|
+
] }) : null,
|
|
710
787
|
/* @__PURE__ */ jsxs4("section", { className: "ost-card ost-dash-card", children: [
|
|
711
788
|
/* @__PURE__ */ jsx4("header", { className: "ost-dash-card-head", children: /* @__PURE__ */ jsx4("h3", { children: "Waiting to publish" }) }),
|
|
712
789
|
pages.pendingDrafts.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "ost-muted", children: "Every page is live and up to date." }) : pages.pendingDrafts.map((page) => /* @__PURE__ */ jsxs4("button", { className: "ost-dash-row", onClick: () => onOpenPage(page.id), type: "button", children: [
|
|
@@ -805,6 +882,7 @@ function FormsView({
|
|
|
805
882
|
api,
|
|
806
883
|
canWrite,
|
|
807
884
|
canDelete,
|
|
885
|
+
canReadSubmissions,
|
|
808
886
|
onOpenSubmissions
|
|
809
887
|
}) {
|
|
810
888
|
const [forms, setForms] = useState5(null);
|
|
@@ -870,19 +948,19 @@ function FormsView({
|
|
|
870
948
|
] })
|
|
871
949
|
] }),
|
|
872
950
|
/* @__PURE__ */ jsxs5("span", { className: "ost-row", children: [
|
|
873
|
-
/* @__PURE__ */ jsxs5("button", { className: "ost-btn", onClick: () => onOpenSubmissions(form.id), type: "button", children: [
|
|
951
|
+
canReadSubmissions ? /* @__PURE__ */ jsxs5("button", { className: "ost-btn", onClick: () => onOpenSubmissions(form.id), type: "button", children: [
|
|
874
952
|
form.submissionCount ?? 0,
|
|
875
953
|
" submission",
|
|
876
954
|
(form.submissionCount ?? 0) === 1 ? "" : "s",
|
|
877
955
|
form.unreadCount ? ` (${form.unreadCount} new)` : ""
|
|
878
|
-
] }),
|
|
956
|
+
] }) : null,
|
|
879
957
|
canDelete ? /* @__PURE__ */ jsx5(
|
|
880
958
|
"button",
|
|
881
959
|
{
|
|
882
960
|
className: "ost-btn ost-btn-danger",
|
|
883
961
|
onClick: async () => {
|
|
884
962
|
if (!window.confirm(
|
|
885
|
-
`Delete "${form.title}" and its ${form.submissionCount ?? 0} submissions? This cannot be undone.`
|
|
963
|
+
canReadSubmissions ? `Delete "${form.title}" and its ${form.submissionCount ?? 0} submissions? This cannot be undone.` : `Delete "${form.title}"? This cannot be undone.`
|
|
886
964
|
))
|
|
887
965
|
return;
|
|
888
966
|
await api.deleteForm(form.slug).catch(() => void 0);
|
|
@@ -2043,8 +2121,8 @@ function PageEditor({
|
|
|
2043
2121
|
const changedSincePublish = useMemo3(() => {
|
|
2044
2122
|
if (!page) return false;
|
|
2045
2123
|
if (page.status !== "published") return true;
|
|
2046
|
-
return dirty || JSON.stringify(layout) !== JSON.stringify(page.published_layout ?? []);
|
|
2047
|
-
}, [page, layout, dirty]);
|
|
2124
|
+
return dirty || title !== (page.published_title ?? "") || JSON.stringify(seo) !== JSON.stringify(page.published_seo ?? {}) || JSON.stringify(layout) !== JSON.stringify(page.published_layout ?? []);
|
|
2125
|
+
}, [page, title, seo, layout, dirty]);
|
|
2048
2126
|
const canvasRef = useRef(null);
|
|
2049
2127
|
const layoutRef = useRef(layout);
|
|
2050
2128
|
layoutRef.current = layout;
|
|
@@ -2094,22 +2172,24 @@ function PageEditor({
|
|
|
2094
2172
|
};
|
|
2095
2173
|
}, [layout, registry, commitInline]);
|
|
2096
2174
|
const save = useCallback2(async () => {
|
|
2097
|
-
const prior = savePromiseRef.current ?? Promise.resolve(
|
|
2098
|
-
const run = prior.catch(() =>
|
|
2175
|
+
const prior = savePromiseRef.current ?? Promise.resolve(null);
|
|
2176
|
+
const run = prior.catch(() => null).then(async () => {
|
|
2099
2177
|
const revAtStart = editRevRef.current;
|
|
2100
2178
|
setBusy("save");
|
|
2101
2179
|
setError("");
|
|
2102
2180
|
try {
|
|
2103
2181
|
const { page: saved } = await api.saveDraft(pageId, { title, seo, layout });
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
setMessage("Draft saved.");
|
|
2182
|
+
if (editRevRef.current !== revAtStart) {
|
|
2183
|
+
setError("The draft changed while saving. Save again before publishing or scheduling.");
|
|
2184
|
+
return null;
|
|
2108
2185
|
}
|
|
2109
|
-
|
|
2186
|
+
setPage(saved);
|
|
2187
|
+
setDirty(false);
|
|
2188
|
+
setMessage("Draft saved.");
|
|
2189
|
+
return saved;
|
|
2110
2190
|
} catch (saveError) {
|
|
2111
2191
|
setError(saveError instanceof Error ? saveError.message : "Save failed.");
|
|
2112
|
-
return
|
|
2192
|
+
return null;
|
|
2113
2193
|
} finally {
|
|
2114
2194
|
setBusy(null);
|
|
2115
2195
|
}
|
|
@@ -2122,13 +2202,17 @@ function PageEditor({
|
|
|
2122
2202
|
}
|
|
2123
2203
|
}, [api, pageId, title, seo, layout]);
|
|
2124
2204
|
const publish = async () => {
|
|
2125
|
-
|
|
2205
|
+
const reviewed = dirty ? await save() : page;
|
|
2206
|
+
if (!reviewed?.draft_version_id) {
|
|
2207
|
+
setError("Save the draft again before publishing.");
|
|
2208
|
+
return;
|
|
2209
|
+
}
|
|
2126
2210
|
setBusy("publish");
|
|
2127
2211
|
setError("");
|
|
2128
2212
|
try {
|
|
2129
|
-
const { page: published } = await api.publish(pageId);
|
|
2213
|
+
const { page: published } = await api.publish(pageId, reviewed.draft_version_id);
|
|
2130
2214
|
setPage(published);
|
|
2131
|
-
setMessage("Published
|
|
2215
|
+
setMessage("Published. The reviewed version is live on the site.");
|
|
2132
2216
|
} catch (publishError) {
|
|
2133
2217
|
setError(publishError instanceof Error ? publishError.message : "Publish failed.");
|
|
2134
2218
|
} finally {
|
|
@@ -2540,17 +2624,20 @@ function PageSettings({
|
|
|
2540
2624
|
const schedule = async (value) => {
|
|
2541
2625
|
setError("");
|
|
2542
2626
|
setNotice("");
|
|
2543
|
-
|
|
2627
|
+
const reviewed = value ? await onFlushDraft() : null;
|
|
2628
|
+
if (value && !reviewed?.draft_version_id) {
|
|
2544
2629
|
setError("Could not save the current draft before scheduling.");
|
|
2545
2630
|
return;
|
|
2546
2631
|
}
|
|
2547
2632
|
try {
|
|
2548
|
-
const { page: updated } = await api.
|
|
2549
|
-
|
|
2550
|
-
|
|
2633
|
+
const { page: updated } = await api.schedulePublish(
|
|
2634
|
+
page.id,
|
|
2635
|
+
value ? new Date(value).toISOString() : null,
|
|
2636
|
+
reviewed?.draft_version_id ?? void 0
|
|
2637
|
+
);
|
|
2551
2638
|
onPageChanged(updated);
|
|
2552
2639
|
setPublishAt(updated.publish_at ? updated.publish_at.slice(0, 16) : "");
|
|
2553
|
-
setNotice(value ?
|
|
2640
|
+
setNotice(value ? `Saved version ${updated.scheduled_version_id} is scheduled.` : "Schedule cleared.");
|
|
2554
2641
|
} catch (scheduleError) {
|
|
2555
2642
|
setError(scheduleError instanceof Error ? scheduleError.message : "Could not schedule.");
|
|
2556
2643
|
}
|
|
@@ -2650,7 +2737,7 @@ function PageSettings({
|
|
|
2650
2737
|
/* @__PURE__ */ jsx7("button", { className: "ost-btn", disabled: !publishAt, onClick: () => schedule(publishAt), type: "button", children: "Schedule" }),
|
|
2651
2738
|
page.publish_at ? /* @__PURE__ */ jsx7("button", { className: "ost-btn", onClick: () => schedule(null), type: "button", children: "Clear" }) : null
|
|
2652
2739
|
] }),
|
|
2653
|
-
/* @__PURE__ */ jsx7("p", { className: "ost-muted", children: "The
|
|
2740
|
+
/* @__PURE__ */ jsx7("p", { className: "ost-muted", children: "The saved version selected when you schedule goes live at the chosen time. Later draft edits stay private." })
|
|
2654
2741
|
] }) : null,
|
|
2655
2742
|
/* @__PURE__ */ jsxs7("section", { className: "ost-settings-section", children: [
|
|
2656
2743
|
/* @__PURE__ */ jsx7("h4", { children: "Actions" }),
|
|
@@ -3249,6 +3336,7 @@ function MediaView({
|
|
|
3249
3336
|
media,
|
|
3250
3337
|
mediaUrl,
|
|
3251
3338
|
canDelete,
|
|
3339
|
+
canReplace,
|
|
3252
3340
|
onUpload,
|
|
3253
3341
|
onChanged
|
|
3254
3342
|
}) {
|
|
@@ -3331,6 +3419,7 @@ function MediaView({
|
|
|
3331
3419
|
{
|
|
3332
3420
|
api,
|
|
3333
3421
|
canDelete,
|
|
3422
|
+
canReplace,
|
|
3334
3423
|
media: selected,
|
|
3335
3424
|
mediaUrl,
|
|
3336
3425
|
onChanged: () => {
|
|
@@ -3348,6 +3437,7 @@ function MediaDetail({
|
|
|
3348
3437
|
media,
|
|
3349
3438
|
mediaUrl,
|
|
3350
3439
|
canDelete,
|
|
3440
|
+
canReplace,
|
|
3351
3441
|
onChanged,
|
|
3352
3442
|
onClose,
|
|
3353
3443
|
onError
|
|
@@ -3402,7 +3492,7 @@ function MediaDetail({
|
|
|
3402
3492
|
children: saving ? "Saving\u2026" : "Save details"
|
|
3403
3493
|
}
|
|
3404
3494
|
),
|
|
3405
|
-
/* @__PURE__ */ jsxs11("label", { className: "ost-btn", title: "The new file keeps every existing reference. CDN caching can take up to an hour to show the new version everywhere.", children: [
|
|
3495
|
+
canReplace ? /* @__PURE__ */ jsxs11("label", { className: "ost-btn", title: "The new file keeps every existing reference. CDN caching can take up to an hour to show the new version everywhere.", children: [
|
|
3406
3496
|
replacing ? "Replacing\u2026" : "Replace file",
|
|
3407
3497
|
/* @__PURE__ */ jsx11(
|
|
3408
3498
|
"input",
|
|
@@ -3428,7 +3518,7 @@ function MediaDetail({
|
|
|
3428
3518
|
type: "file"
|
|
3429
3519
|
}
|
|
3430
3520
|
)
|
|
3431
|
-
] }),
|
|
3521
|
+
] }) : null,
|
|
3432
3522
|
canDelete ? /* @__PURE__ */ jsx11(
|
|
3433
3523
|
"button",
|
|
3434
3524
|
{
|
|
@@ -3590,8 +3680,8 @@ var NAV = [
|
|
|
3590
3680
|
{ key: "submissions", label: "Submissions" },
|
|
3591
3681
|
{ key: "analytics", label: "Analytics" },
|
|
3592
3682
|
{ key: "globals", label: "Globals" },
|
|
3593
|
-
{ key: "redirects", label: "Redirects"
|
|
3594
|
-
{ key: "users", label: "Users"
|
|
3683
|
+
{ key: "redirects", label: "Redirects" },
|
|
3684
|
+
{ key: "users", label: "Users" }
|
|
3595
3685
|
];
|
|
3596
3686
|
function Studio({ registry, globals, siteName, logoUrl, supabaseUrl }) {
|
|
3597
3687
|
const { session, loading, getToken, signOut } = useStudioSession();
|
|
@@ -3692,20 +3782,8 @@ function Studio({ registry, globals, siteName, logoUrl, supabaseUrl }) {
|
|
|
3692
3782
|
] }) });
|
|
3693
3783
|
}
|
|
3694
3784
|
if (!role) return /* @__PURE__ */ jsx12("div", { className: "ost-root ost-loading", children: "Loading Studio\u2026" });
|
|
3695
|
-
const
|
|
3696
|
-
const
|
|
3697
|
-
const canCreatePages = role !== "content";
|
|
3698
|
-
const canDeleteMedia = role !== "content";
|
|
3699
|
-
const canWriteForms = role !== "content";
|
|
3700
|
-
const canManageSubmissions = role !== "content";
|
|
3701
|
-
const canManageUsers = role === "admin" || role === "developer";
|
|
3702
|
-
const canManageRedirects = role !== "content";
|
|
3703
|
-
const canDeleteForms = role === "admin" || role === "developer";
|
|
3704
|
-
const nav = NAV.filter((item) => {
|
|
3705
|
-
if (item.minRole === "admin") return canManageUsers;
|
|
3706
|
-
if (item.minRole === "editor") return role !== "content";
|
|
3707
|
-
return true;
|
|
3708
|
-
});
|
|
3785
|
+
const access = studioAccess(role);
|
|
3786
|
+
const nav = NAV.filter((item) => canViewStudioSection(role, item.key));
|
|
3709
3787
|
const goTo = (key) => {
|
|
3710
3788
|
setSection(key);
|
|
3711
3789
|
setOpenPageId(null);
|
|
@@ -3748,8 +3826,8 @@ function Studio({ registry, globals, siteName, logoUrl, supabaseUrl }) {
|
|
|
3748
3826
|
PageEditor,
|
|
3749
3827
|
{
|
|
3750
3828
|
api,
|
|
3751
|
-
canChangeStructure,
|
|
3752
|
-
canPublish,
|
|
3829
|
+
canChangeStructure: access.changePageStructure,
|
|
3830
|
+
canPublish: access.publishPages,
|
|
3753
3831
|
media,
|
|
3754
3832
|
mediaUrl,
|
|
3755
3833
|
onBack: () => {
|
|
@@ -3763,11 +3841,12 @@ function Studio({ registry, globals, siteName, logoUrl, supabaseUrl }) {
|
|
|
3763
3841
|
role
|
|
3764
3842
|
},
|
|
3765
3843
|
openPageId
|
|
3766
|
-
) : section === "pages" ? /* @__PURE__ */ jsx12(PagesView, { api, canCreate:
|
|
3844
|
+
) : section === "pages" ? /* @__PURE__ */ jsx12(PagesView, { api, canCreate: access.createPages, onOpen: openPage }) : section === "media" ? /* @__PURE__ */ jsx12(
|
|
3767
3845
|
MediaView,
|
|
3768
3846
|
{
|
|
3769
3847
|
api,
|
|
3770
|
-
canDelete:
|
|
3848
|
+
canDelete: access.deleteMedia,
|
|
3849
|
+
canReplace: access.replaceMedia,
|
|
3771
3850
|
media,
|
|
3772
3851
|
mediaUrl,
|
|
3773
3852
|
onChanged: refreshMedia,
|
|
@@ -3777,23 +3856,24 @@ function Studio({ registry, globals, siteName, logoUrl, supabaseUrl }) {
|
|
|
3777
3856
|
FormsView,
|
|
3778
3857
|
{
|
|
3779
3858
|
api,
|
|
3780
|
-
canDelete:
|
|
3781
|
-
|
|
3859
|
+
canDelete: access.deleteForms,
|
|
3860
|
+
canReadSubmissions: access.readSubmissions,
|
|
3861
|
+
canWrite: access.writeForms,
|
|
3782
3862
|
onOpenSubmissions: (formId) => {
|
|
3783
3863
|
setSubmissionsFormId(formId);
|
|
3784
3864
|
setSection("submissions");
|
|
3785
3865
|
}
|
|
3786
3866
|
}
|
|
3787
|
-
) : section === "submissions" ? /* @__PURE__ */ jsx12(
|
|
3867
|
+
) : section === "submissions" && access.readSubmissions ? /* @__PURE__ */ jsx12(
|
|
3788
3868
|
SubmissionsView,
|
|
3789
3869
|
{
|
|
3790
3870
|
api,
|
|
3791
|
-
canManage:
|
|
3871
|
+
canManage: access.manageSubmissions,
|
|
3792
3872
|
initialFormId: submissionsFormId,
|
|
3793
3873
|
onUnreadChanged: refreshUnread
|
|
3794
3874
|
},
|
|
3795
3875
|
submissionsFormId || "all"
|
|
3796
|
-
) : section === "analytics" ? /* @__PURE__ */ jsx12(AnalyticsView, { api }) : section === "globals" ? /* @__PURE__ */ jsx12(
|
|
3876
|
+
) : section === "analytics" && access.readAnalytics ? /* @__PURE__ */ jsx12(AnalyticsView, { api }) : section === "globals" ? /* @__PURE__ */ jsx12(
|
|
3797
3877
|
GlobalsView,
|
|
3798
3878
|
{
|
|
3799
3879
|
api,
|
|
@@ -3803,10 +3883,12 @@ function Studio({ registry, globals, siteName, logoUrl, supabaseUrl }) {
|
|
|
3803
3883
|
onUploadMedia: uploadMedia,
|
|
3804
3884
|
pages
|
|
3805
3885
|
}
|
|
3806
|
-
) : section === "redirects" &&
|
|
3886
|
+
) : section === "redirects" && access.manageRedirects ? /* @__PURE__ */ jsx12(RedirectsView, { api }) : section === "users" && access.manageUsers ? /* @__PURE__ */ jsx12(UsersView, { api, meId: session.user.id }) : /* @__PURE__ */ jsx12(
|
|
3807
3887
|
DashboardView,
|
|
3808
3888
|
{
|
|
3809
3889
|
api,
|
|
3890
|
+
canReadAnalytics: access.readAnalytics,
|
|
3891
|
+
canReadSubmissions: access.readSubmissions,
|
|
3810
3892
|
onOpenPage: openPage,
|
|
3811
3893
|
onOpenSubmissions: () => goTo("submissions"),
|
|
3812
3894
|
siteName
|