@aphexcms/cms-core 2.0.10 → 2.1.0
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/api/api-keys.d.ts +4 -0
- package/dist/api/api-keys.d.ts.map +1 -1
- package/dist/api/assets.d.ts.map +1 -1
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +1 -0
- package/dist/api/roles.d.ts +22 -0
- package/dist/api/roles.d.ts.map +1 -0
- package/dist/api/roles.js +26 -0
- package/dist/api/schemas/api-keys.d.ts +50 -4
- package/dist/api/schemas/api-keys.d.ts.map +1 -1
- package/dist/api/schemas/api-keys.js +22 -3
- package/dist/api/schemas/organizations.d.ts +4 -22
- package/dist/api/schemas/organizations.d.ts.map +1 -1
- package/dist/api/schemas/organizations.js +13 -2
- package/dist/api/schemas/roles.d.ts +77 -0
- package/dist/api/schemas/roles.d.ts.map +1 -0
- package/dist/api/schemas/roles.js +42 -0
- package/dist/app-augment.d.ts +32 -0
- package/dist/app-augment.d.ts.map +1 -0
- package/dist/app-augment.js +18 -0
- package/dist/auth/auth-hooks.d.ts +2 -1
- package/dist/auth/auth-hooks.d.ts.map +1 -1
- package/dist/auth/auth-hooks.js +22 -2
- package/dist/client/index.d.ts +4 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +6 -2
- package/dist/components/AdminApp.svelte +300 -140
- package/dist/components/AdminApp.svelte.d.ts +8 -0
- package/dist/components/AdminApp.svelte.d.ts.map +1 -1
- package/dist/components/admin/DocumentEditor.svelte +633 -350
- package/dist/components/admin/DocumentEditor.svelte.d.ts +2 -0
- package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -1
- package/dist/components/admin/DocumentVersionPanel.svelte +46 -20
- package/dist/components/admin/DocumentVersionPanel.svelte.d.ts.map +1 -1
- package/dist/components/admin/MediaBrowser.svelte +70 -30
- package/dist/components/admin/MediaBrowser.svelte.d.ts.map +1 -1
- package/dist/components/admin/ObjectModal.svelte +70 -95
- package/dist/components/admin/ObjectModal.svelte.d.ts +1 -2
- package/dist/components/admin/ObjectModal.svelte.d.ts.map +1 -1
- package/dist/components/admin/PermissionsDebug.svelte +119 -0
- package/dist/components/admin/PermissionsDebug.svelte.d.ts +4 -0
- package/dist/components/admin/PermissionsDebug.svelte.d.ts.map +1 -0
- package/dist/components/admin/SchemaField.svelte +3 -1
- package/dist/components/admin/SchemaField.svelte.d.ts.map +1 -1
- package/dist/components/admin/confirm-dialog/ConfirmDialogHost.svelte +8 -10
- package/dist/components/admin/confirm-dialog/ConfirmDialogHost.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/ArrayField.svelte +104 -108
- package/dist/components/admin/fields/ArrayField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/FileField.svelte +22 -17
- package/dist/components/admin/fields/FileField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/ImageField.svelte +21 -15
- package/dist/components/admin/fields/ImageField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/ReferenceField.svelte +142 -112
- package/dist/components/admin/fields/ReferenceField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/TextareaField.svelte +4 -3
- package/dist/components/admin/fields/TextareaField.svelte.d.ts +2 -2
- package/dist/components/admin/fields/TextareaField.svelte.d.ts.map +1 -1
- package/dist/components/layout/Sidebar.svelte +21 -11
- package/dist/components/layout/Sidebar.svelte.d.ts.map +1 -1
- package/dist/components/layout/sidebar/NavMain.svelte +1 -0
- package/dist/db/interfaces/document.d.ts.map +1 -1
- package/dist/db/interfaces/index.d.ts +3 -1
- package/dist/db/interfaces/index.d.ts.map +1 -1
- package/dist/db/interfaces/organization.d.ts +1 -1
- package/dist/db/interfaces/organization.d.ts.map +1 -1
- package/dist/db/interfaces/role.d.ts +25 -0
- package/dist/db/interfaces/role.d.ts.map +1 -0
- package/dist/db/interfaces/role.js +1 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +5 -0
- package/dist/field-access.d.ts +30 -0
- package/dist/field-access.d.ts.map +1 -0
- package/dist/field-access.js +79 -0
- package/dist/hooks.d.ts +2 -0
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +19 -13
- package/dist/lib/api/api-keys.d.ts +4 -0
- package/dist/lib/api/api-keys.d.ts.map +1 -1
- package/dist/lib/api/api-keys.js.map +1 -1
- package/dist/lib/api/assets.d.ts.map +1 -1
- package/dist/lib/api/assets.js.map +1 -1
- package/dist/lib/api/index.d.ts +1 -0
- package/dist/lib/api/index.d.ts.map +1 -1
- package/dist/lib/api/index.js +1 -0
- package/dist/lib/api/index.js.map +1 -1
- package/dist/lib/api/roles.d.ts +22 -0
- package/dist/lib/api/roles.d.ts.map +1 -0
- package/dist/lib/api/roles.js +27 -0
- package/dist/lib/api/roles.js.map +1 -0
- package/dist/lib/api/schemas/api-keys.d.ts +50 -4
- package/dist/lib/api/schemas/api-keys.d.ts.map +1 -1
- package/dist/lib/api/schemas/api-keys.js +22 -3
- package/dist/lib/api/schemas/api-keys.js.map +1 -1
- package/dist/lib/api/schemas/organizations.d.ts +4 -22
- package/dist/lib/api/schemas/organizations.d.ts.map +1 -1
- package/dist/lib/api/schemas/organizations.js +13 -2
- package/dist/lib/api/schemas/organizations.js.map +1 -1
- package/dist/lib/api/schemas/roles.d.ts +77 -0
- package/dist/lib/api/schemas/roles.d.ts.map +1 -0
- package/dist/lib/api/schemas/roles.js +43 -0
- package/dist/lib/api/schemas/roles.js.map +1 -0
- package/dist/lib/app-augment.d.ts +32 -0
- package/dist/lib/app-augment.d.ts.map +1 -0
- package/dist/lib/app-augment.js +19 -0
- package/dist/lib/app-augment.js.map +1 -0
- package/dist/lib/auth/auth-hooks.d.ts +2 -1
- package/dist/lib/auth/auth-hooks.d.ts.map +1 -1
- package/dist/lib/auth/auth-hooks.js +22 -2
- package/dist/lib/auth/auth-hooks.js.map +1 -1
- package/dist/lib/client/index.d.ts +4 -2
- package/dist/lib/client/index.d.ts.map +1 -1
- package/dist/lib/client/index.js +6 -2
- package/dist/lib/client/index.js.map +1 -1
- package/dist/lib/db/interfaces/document.d.ts.map +1 -1
- package/dist/lib/db/interfaces/index.d.ts +3 -1
- package/dist/lib/db/interfaces/index.d.ts.map +1 -1
- package/dist/lib/db/interfaces/organization.d.ts +1 -1
- package/dist/lib/db/interfaces/organization.d.ts.map +1 -1
- package/dist/lib/db/interfaces/role.d.ts +25 -0
- package/dist/lib/db/interfaces/role.d.ts.map +1 -0
- package/dist/lib/db/interfaces/role.js +2 -0
- package/dist/lib/db/interfaces/role.js.map +1 -0
- package/dist/lib/engine.d.ts.map +1 -1
- package/dist/lib/engine.js +5 -0
- package/dist/lib/engine.js.map +1 -1
- package/dist/lib/field-access.d.ts +30 -0
- package/dist/lib/field-access.d.ts.map +1 -0
- package/dist/lib/field-access.js +80 -0
- package/dist/lib/field-access.js.map +1 -0
- package/dist/lib/hooks.d.ts +2 -0
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/hooks.js +19 -13
- package/dist/lib/hooks.js.map +1 -1
- package/dist/lib/local-api/collection-api.d.ts +2 -0
- package/dist/lib/local-api/collection-api.d.ts.map +1 -1
- package/dist/lib/local-api/collection-api.js +98 -30
- package/dist/lib/local-api/collection-api.js.map +1 -1
- package/dist/lib/local-api/index.d.ts.map +1 -1
- package/dist/lib/local-api/index.js.map +1 -1
- package/dist/lib/local-api/permissions.d.ts +26 -26
- package/dist/lib/local-api/permissions.d.ts.map +1 -1
- package/dist/lib/local-api/permissions.js +122 -80
- package/dist/lib/local-api/permissions.js.map +1 -1
- package/dist/lib/permissions-context.svelte.d.ts +44 -0
- package/dist/lib/permissions-context.svelte.d.ts.map +1 -0
- package/dist/lib/permissions-context.svelte.js +80 -0
- package/dist/lib/permissions-context.svelte.js.map +1 -0
- package/dist/lib/routes/assets.d.ts.map +1 -1
- package/dist/lib/routes/assets.js.map +1 -1
- package/dist/lib/routes/document-versions.d.ts.map +1 -1
- package/dist/lib/routes/document-versions.js.map +1 -1
- package/dist/lib/routes/organizations-by-id.d.ts.map +1 -1
- package/dist/lib/routes/organizations-by-id.js +6 -3
- package/dist/lib/routes/organizations-by-id.js.map +1 -1
- package/dist/lib/routes/organizations-invitations.d.ts.map +1 -1
- package/dist/lib/routes/organizations-invitations.js +17 -7
- package/dist/lib/routes/organizations-invitations.js.map +1 -1
- package/dist/lib/routes/organizations-members.d.ts.map +1 -1
- package/dist/lib/routes/organizations-members.js +15 -7
- package/dist/lib/routes/organizations-members.js.map +1 -1
- package/dist/lib/routes/organizations.d.ts.map +1 -1
- package/dist/lib/routes/organizations.js +3 -0
- package/dist/lib/routes/organizations.js.map +1 -1
- package/dist/lib/routes/roles-by-name.d.ts +4 -0
- package/dist/lib/routes/roles-by-name.d.ts.map +1 -0
- package/dist/lib/routes/roles-by-name.js +112 -0
- package/dist/lib/routes/roles-by-name.js.map +1 -0
- package/dist/lib/routes/roles.d.ts +4 -0
- package/dist/lib/routes/roles.d.ts.map +1 -0
- package/dist/lib/routes/roles.js +91 -0
- package/dist/lib/routes/roles.js.map +1 -0
- package/dist/lib/routes-exports.d.ts +2 -0
- package/dist/lib/routes-exports.d.ts.map +1 -1
- package/dist/lib/routes-exports.js +3 -0
- package/dist/lib/routes-exports.js.map +1 -1
- package/dist/lib/save-state-context.svelte.d.ts +8 -0
- package/dist/lib/save-state-context.svelte.d.ts.map +1 -0
- package/dist/lib/save-state-context.svelte.js +9 -0
- package/dist/lib/save-state-context.svelte.js.map +1 -0
- package/dist/lib/schema-utils/validator.d.ts.map +1 -1
- package/dist/lib/schema-utils/validator.js +14 -0
- package/dist/lib/schema-utils/validator.js.map +1 -1
- package/dist/lib/server/index.d.ts +1 -0
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js +1 -0
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/services/index.d.ts +1 -0
- package/dist/lib/services/index.d.ts.map +1 -1
- package/dist/lib/services/index.js +1 -0
- package/dist/lib/services/index.js.map +1 -1
- package/dist/lib/services/roles-service.d.ts +35 -0
- package/dist/lib/services/roles-service.d.ts.map +1 -0
- package/dist/lib/services/roles-service.js +91 -0
- package/dist/lib/services/roles-service.js.map +1 -0
- package/dist/lib/services/version-service.d.ts.map +1 -1
- package/dist/lib/services/version-service.js.map +1 -1
- package/dist/lib/types/auth.d.ts +43 -9
- package/dist/lib/types/auth.d.ts.map +1 -1
- package/dist/lib/types/auth.js +31 -26
- package/dist/lib/types/auth.js.map +1 -1
- package/dist/lib/types/capabilities.d.ts +104 -0
- package/dist/lib/types/capabilities.d.ts.map +1 -0
- package/dist/lib/types/capabilities.js +217 -0
- package/dist/lib/types/capabilities.js.map +1 -0
- package/dist/lib/types/index.d.ts +1 -0
- package/dist/lib/types/index.d.ts.map +1 -1
- package/dist/lib/types/index.js +1 -0
- package/dist/lib/types/index.js.map +1 -1
- package/dist/lib/types/organization.d.ts +13 -4
- package/dist/lib/types/organization.d.ts.map +1 -1
- package/dist/lib/types/schemas.d.ts +71 -0
- package/dist/lib/types/schemas.d.ts.map +1 -1
- package/dist/lib/types/sidebar.d.ts +1 -1
- package/dist/lib/types/sidebar.d.ts.map +1 -1
- package/dist/lib/utils/mime-detect.d.ts.map +1 -1
- package/dist/lib/utils/mime-detect.js +28 -6
- package/dist/lib/utils/mime-detect.js.map +1 -1
- package/dist/local-api/collection-api.d.ts +2 -0
- package/dist/local-api/collection-api.d.ts.map +1 -1
- package/dist/local-api/collection-api.js +98 -30
- package/dist/local-api/index.d.ts.map +1 -1
- package/dist/local-api/permissions.d.ts +26 -26
- package/dist/local-api/permissions.d.ts.map +1 -1
- package/dist/local-api/permissions.js +122 -80
- package/dist/permissions-context.svelte.d.ts +44 -0
- package/dist/permissions-context.svelte.d.ts.map +1 -0
- package/dist/permissions-context.svelte.js +79 -0
- package/dist/plugins/README.md +20 -20
- package/dist/routes/assets.d.ts.map +1 -1
- package/dist/routes/document-versions.d.ts.map +1 -1
- package/dist/routes/organizations-by-id.d.ts.map +1 -1
- package/dist/routes/organizations-by-id.js +6 -3
- package/dist/routes/organizations-invitations.d.ts.map +1 -1
- package/dist/routes/organizations-invitations.js +17 -7
- package/dist/routes/organizations-members.d.ts.map +1 -1
- package/dist/routes/organizations-members.js +15 -7
- package/dist/routes/organizations.d.ts.map +1 -1
- package/dist/routes/organizations.js +3 -0
- package/dist/routes/roles-by-name.d.ts +4 -0
- package/dist/routes/roles-by-name.d.ts.map +1 -0
- package/dist/routes/roles-by-name.js +111 -0
- package/dist/routes/roles.d.ts +4 -0
- package/dist/routes/roles.d.ts.map +1 -0
- package/dist/routes/roles.js +90 -0
- package/dist/routes-exports.d.ts +2 -0
- package/dist/routes-exports.d.ts.map +1 -1
- package/dist/routes-exports.js +3 -0
- package/dist/save-state-context.svelte.d.ts +8 -0
- package/dist/save-state-context.svelte.d.ts.map +1 -0
- package/dist/save-state-context.svelte.js +8 -0
- package/dist/schema-utils/validator.d.ts.map +1 -1
- package/dist/schema-utils/validator.js +14 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -0
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +1 -0
- package/dist/services/roles-service.d.ts +35 -0
- package/dist/services/roles-service.d.ts.map +1 -0
- package/dist/services/roles-service.js +90 -0
- package/dist/services/version-service.d.ts.map +1 -1
- package/dist/types/auth.d.ts +43 -9
- package/dist/types/auth.d.ts.map +1 -1
- package/dist/types/auth.js +31 -26
- package/dist/types/capabilities.d.ts +104 -0
- package/dist/types/capabilities.d.ts.map +1 -0
- package/dist/types/capabilities.js +216 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/organization.d.ts +13 -4
- package/dist/types/organization.d.ts.map +1 -1
- package/dist/types/schemas.d.ts +71 -0
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/types/sidebar.d.ts +1 -1
- package/dist/types/sidebar.d.ts.map +1 -1
- package/dist/utils/mime-detect.d.ts.map +1 -1
- package/dist/utils/mime-detect.js +28 -6
- package/package.json +6 -2
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
import { pluralize } from '../utils/pluralize';
|
|
37
37
|
import { toast } from 'svelte-sonner';
|
|
38
38
|
import { resolve } from '$app/paths';
|
|
39
|
+
import { setPermissionsContext } from '../permissions-context.svelte';
|
|
39
40
|
|
|
40
41
|
interface Props {
|
|
41
42
|
schemas: SchemaType[];
|
|
@@ -44,6 +45,14 @@
|
|
|
44
45
|
title?: string;
|
|
45
46
|
graphqlSettings?: { endpoint: string; enableGraphiQL: boolean } | null;
|
|
46
47
|
isReadOnly?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Capabilities resolved for the current session. Used for per-action UI
|
|
50
|
+
* gating. When absent, all actions are shown and the server remains the
|
|
51
|
+
* enforcement surface.
|
|
52
|
+
*/
|
|
53
|
+
capabilities?: string[];
|
|
54
|
+
/** Effective organization role name, for role-list style checks. */
|
|
55
|
+
rbacRole?: string | null;
|
|
47
56
|
activeTab?: { value: 'structure' | 'vision' | 'media' };
|
|
48
57
|
handleTabChange: (value: string) => void;
|
|
49
58
|
userPreferences?: UserSessionPreferences | null;
|
|
@@ -56,27 +65,68 @@
|
|
|
56
65
|
title = 'Aphex CMS',
|
|
57
66
|
graphqlSettings = null,
|
|
58
67
|
isReadOnly = false,
|
|
68
|
+
capabilities = [],
|
|
69
|
+
rbacRole = null,
|
|
59
70
|
activeTab = { value: 'structure' } as { value: 'structure' | 'vision' | 'media' },
|
|
60
71
|
handleTabChange = () => {},
|
|
61
72
|
userPreferences = null
|
|
62
73
|
}: Props = $props();
|
|
63
74
|
|
|
75
|
+
// Publish capabilities to every descendant (DocumentEditor, fields, etc)
|
|
76
|
+
// via Svelte context. Using a getter closure so prop reactivity propagates:
|
|
77
|
+
// if the parent swaps the capabilities array (e.g. role change mid-session)
|
|
78
|
+
// every `perms.can()` call in the subtree picks it up on next read.
|
|
79
|
+
const perms = setPermissionsContext(
|
|
80
|
+
() => capabilities,
|
|
81
|
+
() => rbacRole
|
|
82
|
+
);
|
|
83
|
+
|
|
64
84
|
// Keep a state of the organization id
|
|
65
85
|
let currentOrgId = $state<string | null>(page.url.searchParams.get('orgId'));
|
|
66
86
|
|
|
67
|
-
// Merge document types with schema icons (schemas have icons, server data doesn't)
|
|
87
|
+
// Merge document types with schema icons (schemas have icons, server data doesn't),
|
|
88
|
+
// then filter out any schemas the caller can't read. Mirrors the server check:
|
|
89
|
+
// an `access.read` list on a schema is an allowlist of role names (built-in
|
|
90
|
+
// or custom); absence of a list means "whoever can read documents at all".
|
|
68
91
|
const documentTypes = $derived(
|
|
69
|
-
documentTypesFromServer
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
92
|
+
documentTypesFromServer
|
|
93
|
+
.map((docType) => {
|
|
94
|
+
const schema = schemas.find((s) => s.name === docType.name);
|
|
95
|
+
return {
|
|
96
|
+
...docType,
|
|
97
|
+
icon: schema?.icon,
|
|
98
|
+
group: schema?.group,
|
|
99
|
+
access: schema?.access
|
|
100
|
+
};
|
|
101
|
+
})
|
|
102
|
+
.filter((docType) => {
|
|
103
|
+
const readList = docType.access?.read;
|
|
104
|
+
if (!readList) return true; // no list = open to any reader
|
|
105
|
+
// Functions (policy-based access) can't be evaluated on the client
|
|
106
|
+
// without a target doc — show them and let the server enforce.
|
|
107
|
+
if (typeof readList === 'function') return true;
|
|
108
|
+
const role = perms.role;
|
|
109
|
+
return role !== null && readList.includes(role);
|
|
110
|
+
})
|
|
76
111
|
);
|
|
77
112
|
|
|
78
113
|
const hasDocumentTypes = $derived(documentTypes.length > 0);
|
|
79
114
|
|
|
115
|
+
// Bucket document types by their `group` property. Ungrouped types sit in a
|
|
116
|
+
// leading null bucket; named groups follow in first-seen order.
|
|
117
|
+
const groupedDocumentTypes = $derived.by(() => {
|
|
118
|
+
const buckets = new Map<string | null, typeof documentTypes>();
|
|
119
|
+
buckets.set(null, []);
|
|
120
|
+
for (const dt of documentTypes) {
|
|
121
|
+
const key = dt.group ?? null;
|
|
122
|
+
if (!buckets.has(key)) buckets.set(key, []);
|
|
123
|
+
buckets.get(key)!.push(dt);
|
|
124
|
+
}
|
|
125
|
+
return Array.from(buckets.entries())
|
|
126
|
+
.filter(([, items]) => items.length > 0)
|
|
127
|
+
.map(([name, items]) => ({ name, items }));
|
|
128
|
+
});
|
|
129
|
+
|
|
80
130
|
// Client-side routing state
|
|
81
131
|
let currentView = $state<'dashboard' | 'documents' | 'editor'>('dashboard');
|
|
82
132
|
let selectedDocumentType = $state<string | null>(null);
|
|
@@ -109,7 +159,11 @@
|
|
|
109
159
|
// Version history panel state
|
|
110
160
|
let showVersionPanel = $state(false);
|
|
111
161
|
let versionPanelDocId = $state<string | null>(null);
|
|
112
|
-
let versionPreviewData = $state<{
|
|
162
|
+
let versionPreviewData = $state<{
|
|
163
|
+
versionNumber: number;
|
|
164
|
+
data: Record<string, any>;
|
|
165
|
+
eventType: string;
|
|
166
|
+
} | null>(null);
|
|
113
167
|
|
|
114
168
|
// Documents list sorting state
|
|
115
169
|
let sortDropdownOpen = $state(false);
|
|
@@ -171,14 +225,15 @@
|
|
|
171
225
|
let activeEditorIndex = $state<number>(0);
|
|
172
226
|
|
|
173
227
|
// Calculate how many editors can be shown expanded based on available space
|
|
174
|
-
const MIN_EDITOR_WIDTH =
|
|
228
|
+
const MIN_EDITOR_WIDTH = 700; // Minimum width for ANY expanded editor
|
|
175
229
|
const COLLAPSED_WIDTH = 60; // Width of collapsed panels
|
|
176
230
|
const TYPES_EXPANDED = 350;
|
|
177
231
|
const DOCS_EXPANDED = 350;
|
|
178
232
|
|
|
179
233
|
let layoutConfig = $derived.by(() => {
|
|
180
234
|
const start = performance.now();
|
|
181
|
-
const totalEditors =
|
|
235
|
+
const totalEditors =
|
|
236
|
+
(currentView === 'editor' ? 1 : 0) + editorStack.length + (showVersionPanel ? 1 : 0);
|
|
182
237
|
|
|
183
238
|
if (totalEditors === 0) {
|
|
184
239
|
return {
|
|
@@ -433,10 +488,19 @@
|
|
|
433
488
|
cmsLogger.debug('[URL Effect]', 'Branch: CREATE');
|
|
434
489
|
currentView = 'editor';
|
|
435
490
|
mobileView = 'editor';
|
|
436
|
-
selectedDocumentType = docType;
|
|
437
491
|
isCreatingDocument = true;
|
|
438
492
|
editingDocumentId = null;
|
|
439
493
|
editorStack = [];
|
|
494
|
+
if (selectedDocumentType !== docType) {
|
|
495
|
+
docCurrentPage = 1;
|
|
496
|
+
selectedDocumentType = docType;
|
|
497
|
+
fetchDocuments(docType);
|
|
498
|
+
}
|
|
499
|
+
// Note: no fallback refetch when docsList is empty — empty is a valid
|
|
500
|
+
// steady state (type has no records). fetchDocuments reassigns the
|
|
501
|
+
// list (new identity), which retriggers this effect; any "refetch if
|
|
502
|
+
// empty" guard loops forever. If you need a manual refresh, use a
|
|
503
|
+
// button, not an effect.
|
|
440
504
|
} else if (docId) {
|
|
441
505
|
cmsLogger.debug('[URL Effect]', 'Branch: EDIT (docId)');
|
|
442
506
|
currentView = 'editor';
|
|
@@ -478,8 +542,8 @@
|
|
|
478
542
|
}
|
|
479
543
|
|
|
480
544
|
if (docType) {
|
|
481
|
-
selectedDocumentType
|
|
482
|
-
|
|
545
|
+
if (selectedDocumentType !== docType) {
|
|
546
|
+
selectedDocumentType = docType;
|
|
483
547
|
fetchDocuments(docType);
|
|
484
548
|
}
|
|
485
549
|
} else {
|
|
@@ -523,6 +587,9 @@
|
|
|
523
587
|
});
|
|
524
588
|
|
|
525
589
|
async function navigateToDocumentType(docType: string) {
|
|
590
|
+
if (activeTab.value !== 'structure') {
|
|
591
|
+
handleTabChange('structure');
|
|
592
|
+
}
|
|
526
593
|
const params = new SvelteURLSearchParams(page.url.searchParams);
|
|
527
594
|
params.set('docType', docType);
|
|
528
595
|
params.delete('docId');
|
|
@@ -627,7 +694,19 @@
|
|
|
627
694
|
return;
|
|
628
695
|
}
|
|
629
696
|
|
|
630
|
-
// On desktop, add to editor stack
|
|
697
|
+
// On desktop, add to editor stack — unless the document is already open somewhere
|
|
698
|
+
if (editingDocumentId === documentId) {
|
|
699
|
+
// Already open as the primary editor — just focus it
|
|
700
|
+
activeEditorIndex = 0;
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
const existingIndex = editorStack.findIndex((item) => item.documentId === documentId);
|
|
704
|
+
if (existingIndex !== -1) {
|
|
705
|
+
// Already open in the stack — focus that editor (stack index 0 = activeEditorIndex 1)
|
|
706
|
+
activeEditorIndex = existingIndex + 1;
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
|
|
631
710
|
const newStack = [...editorStack, { documentId, documentType, isCreating: false }];
|
|
632
711
|
|
|
633
712
|
// Build stack param string: type1:id1,type2:id2,...
|
|
@@ -818,8 +897,10 @@
|
|
|
818
897
|
</button>
|
|
819
898
|
<span class="text-muted-foreground mx-2">/</span>
|
|
820
899
|
<span class="text-sm font-medium">
|
|
821
|
-
{pluralize(
|
|
822
|
-
selectedDocumentType)
|
|
900
|
+
{pluralize(
|
|
901
|
+
documentTypes.find((t) => t.name === selectedDocumentType)?.title ||
|
|
902
|
+
selectedDocumentType
|
|
903
|
+
)}
|
|
823
904
|
</span>
|
|
824
905
|
{:else if mobileView === 'editor'}
|
|
825
906
|
<Button
|
|
@@ -877,7 +958,7 @@
|
|
|
877
958
|
{:else}
|
|
878
959
|
<!-- Types Panel -->
|
|
879
960
|
<div
|
|
880
|
-
class="border-r transition-all duration-200 {windowWidth < 620
|
|
961
|
+
class="border-rule border-r transition-all duration-200 {windowWidth < 620
|
|
881
962
|
? typesPanel === 'hidden'
|
|
882
963
|
? 'hidden'
|
|
883
964
|
: 'h-full w-screen'
|
|
@@ -888,12 +969,12 @@
|
|
|
888
969
|
{#if typesPanel === 'w-[60px]'}
|
|
889
970
|
<button
|
|
890
971
|
onclick={() => setActiveEditor(-1)}
|
|
891
|
-
class="hover:bg-muted/30 flex h-full w-full flex-col transition-colors"
|
|
972
|
+
class="hover:bg-muted/30 flex h-full w-full cursor-pointer flex-col transition-colors"
|
|
892
973
|
title="Click to expand content types"
|
|
893
974
|
>
|
|
894
975
|
<div class="flex flex-1 items-start justify-center p-2 pt-8 text-left">
|
|
895
976
|
<div
|
|
896
|
-
class="text-foreground
|
|
977
|
+
class="text-foreground -mt-2 text-sm font-medium whitespace-nowrap [writing-mode:vertical-rl]"
|
|
897
978
|
>
|
|
898
979
|
Content
|
|
899
980
|
</div>
|
|
@@ -903,46 +984,55 @@
|
|
|
903
984
|
<div class="h-full overflow-y-auto p-3">
|
|
904
985
|
{#if hasDocumentTypes}
|
|
905
986
|
<h2
|
|
906
|
-
class="text-muted-foreground
|
|
987
|
+
class="text-muted-foreground border-rule mt-2 mb-3 hidden px-2 pb-3 text-sm font-medium sm:block sm:border-b"
|
|
907
988
|
>
|
|
908
989
|
Content
|
|
909
990
|
</h2>
|
|
910
|
-
{#each
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
: ''}"
|
|
917
|
-
title={docType.description || ''}
|
|
918
|
-
>
|
|
919
|
-
<div class="flex items-center gap-2">
|
|
920
|
-
<div
|
|
921
|
-
class="text-muted-foreground flex h-5 w-5 items-center justify-center"
|
|
922
|
-
>
|
|
923
|
-
{#if docType.icon}
|
|
924
|
-
{@const Icon = docType.icon}
|
|
925
|
-
<Icon class="h-4 w-4" />
|
|
926
|
-
{:else}
|
|
927
|
-
<FileText class="h-4 w-4" />
|
|
928
|
-
{/if}
|
|
929
|
-
</div>
|
|
930
|
-
<span class="text-sm">{pluralize(docType.title)}</span>
|
|
991
|
+
{#each groupedDocumentTypes as bucket (bucket.name ?? '__ungrouped__')}
|
|
992
|
+
{#if bucket.name}
|
|
993
|
+
<div
|
|
994
|
+
class="text-muted-foreground mt-3 mb-1 px-2 text-xs font-semibold tracking-wide uppercase first:mt-0"
|
|
995
|
+
>
|
|
996
|
+
{bucket.name}
|
|
931
997
|
</div>
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
998
|
+
{/if}
|
|
999
|
+
{#each bucket.items as docType (docType.name)}
|
|
1000
|
+
<button
|
|
1001
|
+
onclick={() => navigateToDocumentType(docType.name)}
|
|
1002
|
+
class="hover:bg-muted/50 group flex w-full cursor-pointer items-center justify-between rounded-md px-2 py-2.5 text-left transition-colors {selectedDocumentType ===
|
|
1003
|
+
docType.name
|
|
1004
|
+
? 'bg-muted/50'
|
|
1005
|
+
: ''}"
|
|
1006
|
+
title={docType.description || ''}
|
|
937
1007
|
>
|
|
938
|
-
<
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1008
|
+
<div class="flex items-center gap-2">
|
|
1009
|
+
<div
|
|
1010
|
+
class="text-muted-foreground flex h-5 w-5 items-center justify-center"
|
|
1011
|
+
>
|
|
1012
|
+
{#if docType.icon}
|
|
1013
|
+
{@const Icon = docType.icon}
|
|
1014
|
+
<Icon class="h-4 w-4" />
|
|
1015
|
+
{:else}
|
|
1016
|
+
<FileText class="h-4 w-4" />
|
|
1017
|
+
{/if}
|
|
1018
|
+
</div>
|
|
1019
|
+
<span class="text-sm">{pluralize(docType.title)}</span>
|
|
1020
|
+
</div>
|
|
1021
|
+
<svg
|
|
1022
|
+
class="text-muted-foreground h-4 w-4 opacity-0 transition-opacity group-hover:opacity-100"
|
|
1023
|
+
fill="none"
|
|
1024
|
+
viewBox="0 0 24 24"
|
|
1025
|
+
stroke="currentColor"
|
|
1026
|
+
>
|
|
1027
|
+
<path
|
|
1028
|
+
stroke-linecap="round"
|
|
1029
|
+
stroke-linejoin="round"
|
|
1030
|
+
stroke-width="2"
|
|
1031
|
+
d="M9 5l7 7-7 7"
|
|
1032
|
+
/>
|
|
1033
|
+
</svg>
|
|
1034
|
+
</button>
|
|
1035
|
+
{/each}
|
|
946
1036
|
{/each}
|
|
947
1037
|
{:else}
|
|
948
1038
|
<div class="p-6 text-center">
|
|
@@ -969,7 +1059,7 @@
|
|
|
969
1059
|
<!-- Documents Panel -->
|
|
970
1060
|
{#if selectedDocumentType}
|
|
971
1061
|
<div
|
|
972
|
-
class="flex h-full flex-col overflow-hidden border-r transition-all duration-200
|
|
1062
|
+
class="border-rule flex h-full flex-col overflow-hidden border-r transition-all duration-200
|
|
973
1063
|
{!documentsPanelState.visible ? 'hidden' : ''}
|
|
974
1064
|
{windowWidth < 620 ? (documentsPanelState.visible ? 'w-screen' : 'hidden') : ''}
|
|
975
1065
|
{windowWidth >= 620 && documentsPanelState.width === 'full' ? 'w-full' : ''}
|
|
@@ -981,13 +1071,17 @@
|
|
|
981
1071
|
{#if documentsPanelState.width === 'compact'}
|
|
982
1072
|
<button
|
|
983
1073
|
onclick={() => setActiveEditor(-2)}
|
|
984
|
-
class="hover:bg-muted/30 flex h-full w-full flex-col transition-colors"
|
|
1074
|
+
class="hover:bg-muted/30 flex h-full w-full cursor-pointer flex-col transition-colors"
|
|
985
1075
|
title="Click to expand documents list"
|
|
986
1076
|
>
|
|
987
1077
|
<div class="flex flex-1 items-start justify-center p-2 pt-8 text-left">
|
|
988
|
-
<div
|
|
989
|
-
|
|
990
|
-
|
|
1078
|
+
<div
|
|
1079
|
+
class="text-foreground -mt-2 text-sm font-medium whitespace-nowrap [writing-mode:vertical-rl]"
|
|
1080
|
+
>
|
|
1081
|
+
{pluralize(
|
|
1082
|
+
documentTypes.find((t) => t.name === selectedDocumentType)?.title ||
|
|
1083
|
+
selectedDocumentType
|
|
1084
|
+
)}
|
|
991
1085
|
</div>
|
|
992
1086
|
</div>
|
|
993
1087
|
</button>
|
|
@@ -1019,7 +1113,7 @@
|
|
|
1019
1113
|
</div>
|
|
1020
1114
|
</div>
|
|
1021
1115
|
<div class="flex items-center gap-1">
|
|
1022
|
-
{#if
|
|
1116
|
+
{#if perms.can('document.create')}
|
|
1023
1117
|
<Button
|
|
1024
1118
|
size="sm"
|
|
1025
1119
|
variant="ghost"
|
|
@@ -1051,7 +1145,7 @@
|
|
|
1051
1145
|
{...props}
|
|
1052
1146
|
size="sm"
|
|
1053
1147
|
variant="ghost"
|
|
1054
|
-
class="h-8 w-8 p-0"
|
|
1148
|
+
class="h-8 w-8 cursor-pointer p-0"
|
|
1055
1149
|
title="Sort documents"
|
|
1056
1150
|
>
|
|
1057
1151
|
<Ellipsis class="h-4 w-4" />
|
|
@@ -1164,7 +1258,7 @@
|
|
|
1164
1258
|
{@const isActive = editingDocumentId === doc.id}
|
|
1165
1259
|
<button
|
|
1166
1260
|
onclick={() => navigateToEditDocument(doc.id, selectedDocumentType!)}
|
|
1167
|
-
class="hover:bg-muted/50 border-border group flex w-full items-center justify-between border-b p-3 text-left transition-colors {isActive
|
|
1261
|
+
class="hover:bg-muted/50 border-border group flex w-full cursor-pointer items-center justify-between border-b p-3 text-left transition-colors {isActive
|
|
1168
1262
|
? 'bg-muted/50'
|
|
1169
1263
|
: ''}"
|
|
1170
1264
|
>
|
|
@@ -1190,13 +1284,37 @@
|
|
|
1190
1284
|
</p>
|
|
1191
1285
|
{:else if doc.slug}
|
|
1192
1286
|
<p class="text-muted-foreground text-xs">/{doc.slug}</p>
|
|
1193
|
-
{:else if doc.status}
|
|
1194
|
-
<p class="text-muted-foreground text-xs">{doc.status}</p>
|
|
1195
1287
|
{/if}
|
|
1196
1288
|
</div>
|
|
1197
1289
|
</div>
|
|
1198
|
-
<div class="
|
|
1199
|
-
|
|
1290
|
+
<div class="flex items-center gap-2">
|
|
1291
|
+
<span class="text-muted-foreground text-xs">
|
|
1292
|
+
{doc.updatedAt?.toLocaleDateString() || ''}
|
|
1293
|
+
</span>
|
|
1294
|
+
<div class="flex items-center gap-1">
|
|
1295
|
+
{#if doc.status === 'published'}
|
|
1296
|
+
{#if doc.hasChanges}
|
|
1297
|
+
<span
|
|
1298
|
+
class="h-1.5 w-1.5 shrink-0 rounded-full bg-amber-500"
|
|
1299
|
+
title="Unpublished changes"
|
|
1300
|
+
></span>
|
|
1301
|
+
{/if}
|
|
1302
|
+
<span
|
|
1303
|
+
class="h-1.5 w-1.5 shrink-0 rounded-full bg-green-500"
|
|
1304
|
+
title="Published"
|
|
1305
|
+
></span>
|
|
1306
|
+
{:else if doc.status === 'unpublished'}
|
|
1307
|
+
<span
|
|
1308
|
+
class="bg-muted-foreground/60 h-1.5 w-1.5 shrink-0 rounded-full"
|
|
1309
|
+
title="Unpublished"
|
|
1310
|
+
></span>
|
|
1311
|
+
{:else}
|
|
1312
|
+
<span
|
|
1313
|
+
class="h-1.5 w-1.5 shrink-0 rounded-full bg-amber-500"
|
|
1314
|
+
title="Draft"
|
|
1315
|
+
></span>
|
|
1316
|
+
{/if}
|
|
1317
|
+
</div>
|
|
1200
1318
|
</div>
|
|
1201
1319
|
</button>
|
|
1202
1320
|
{/each}
|
|
@@ -1289,75 +1407,100 @@
|
|
|
1289
1407
|
|
|
1290
1408
|
<!-- Primary Editor Panel -->
|
|
1291
1409
|
{#if primaryEditorState.visible}
|
|
1292
|
-
|
|
1293
|
-
<
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
{
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1410
|
+
<div
|
|
1411
|
+
class="transition-all duration-200 {windowWidth < 620
|
|
1412
|
+
? 'w-screen'
|
|
1413
|
+
: 'flex-1'} h-full overflow-x-hidden overflow-y-auto {primaryEditorState.expanded
|
|
1414
|
+
? ''
|
|
1415
|
+
: 'hidden'}"
|
|
1416
|
+
style={windowWidth >= 620 ? 'min-width: 0;' : ''}
|
|
1417
|
+
>
|
|
1418
|
+
<DocumentEditor
|
|
1419
|
+
{schemas}
|
|
1420
|
+
documentType={selectedDocumentType!}
|
|
1421
|
+
documentId={editingDocumentId}
|
|
1422
|
+
isCreating={isCreatingDocument}
|
|
1423
|
+
onBack={navigateBack}
|
|
1424
|
+
onOpenReference={handleOpenReference}
|
|
1425
|
+
onOpenVersionHistory={handleOpenVersionHistory}
|
|
1426
|
+
externalVersionPreview={versionPanelDocId === editingDocumentId
|
|
1427
|
+
? versionPreviewData
|
|
1428
|
+
: null}
|
|
1429
|
+
onSaved={async (docId) => {
|
|
1430
|
+
if (selectedDocumentType) {
|
|
1431
|
+
await fetchDocuments(selectedDocumentType);
|
|
1432
|
+
}
|
|
1433
|
+
// For first-time creation, update URL and local state.
|
|
1434
|
+
// Use goto with replaceState:true so page.url stays in sync —
|
|
1435
|
+
// raw replaceState() from $app/navigation updates the URL bar
|
|
1436
|
+
// but leaves page.url.searchParams stale, which breaks any
|
|
1437
|
+
// subsequent navigation that reads from it (e.g. opening a
|
|
1438
|
+
// stacked reference editor) because the old ?action=create
|
|
1439
|
+
// gets carried forward.
|
|
1440
|
+
if (isCreatingDocument) {
|
|
1441
|
+
// Update local state FIRST so the URL Effect's EDIT branch
|
|
1442
|
+
// sees consistent values when goto fires it.
|
|
1443
|
+
isCreatingDocument = false;
|
|
1444
|
+
editingDocumentId = docId;
|
|
1445
|
+
|
|
1446
|
+
const params = new SvelteURLSearchParams(page.url.searchParams);
|
|
1447
|
+
params.set('docId', docId);
|
|
1448
|
+
if (selectedDocumentType) params.set('docType', selectedDocumentType);
|
|
1449
|
+
params.delete('action');
|
|
1450
|
+
await goto(`/admin?${params.toString()}`, {
|
|
1451
|
+
replaceState: true,
|
|
1452
|
+
keepFocus: true,
|
|
1453
|
+
noScroll: true
|
|
1454
|
+
});
|
|
1455
|
+
} else {
|
|
1456
|
+
// For subsequent saves, use normal navigation
|
|
1457
|
+
navigateToEditDocument(docId, selectedDocumentType!);
|
|
1458
|
+
}
|
|
1459
|
+
}}
|
|
1460
|
+
onAutoSaved={handleAutoSave}
|
|
1461
|
+
onPublished={async (_) => {
|
|
1462
|
+
if (selectedDocumentType) {
|
|
1463
|
+
await fetchDocuments(selectedDocumentType);
|
|
1464
|
+
}
|
|
1465
|
+
}}
|
|
1466
|
+
onUnpublished={async (_) => {
|
|
1467
|
+
if (selectedDocumentType) {
|
|
1468
|
+
await fetchDocuments(selectedDocumentType);
|
|
1469
|
+
}
|
|
1470
|
+
}}
|
|
1471
|
+
onRestored={async (_) => {
|
|
1472
|
+
if (selectedDocumentType) {
|
|
1473
|
+
await fetchDocuments(selectedDocumentType);
|
|
1474
|
+
}
|
|
1475
|
+
}}
|
|
1476
|
+
onDeleted={async () => {
|
|
1477
|
+
if (selectedDocumentType) {
|
|
1478
|
+
await fetchDocuments(selectedDocumentType);
|
|
1479
|
+
const params = new SvelteURLSearchParams(page.url.searchParams);
|
|
1480
|
+
params.set('docType', selectedDocumentType);
|
|
1481
|
+
params.delete('docId');
|
|
1482
|
+
params.delete('action');
|
|
1483
|
+
await goto(`/admin?${params.toString()}`, { replaceState: false });
|
|
1484
|
+
} else {
|
|
1485
|
+
const orgId = page.url.searchParams.get('orgId');
|
|
1486
|
+
const url = orgId ? `/admin?orgId=${orgId}` : '/admin';
|
|
1487
|
+
await goto(url, { replaceState: false });
|
|
1488
|
+
}
|
|
1489
|
+
}}
|
|
1490
|
+
{isReadOnly}
|
|
1491
|
+
/>
|
|
1492
|
+
</div>
|
|
1493
|
+
{#if !primaryEditorState.expanded}
|
|
1353
1494
|
<!-- Collapsed Primary Editor Strip -->
|
|
1354
1495
|
<button
|
|
1355
1496
|
onclick={() => setActiveEditor(0)}
|
|
1356
|
-
class="hover:bg-muted/50 flex h-full w-[60px] flex-col border-l transition-colors"
|
|
1497
|
+
class="border-rule hover:bg-muted/50 flex h-full w-[60px] cursor-pointer flex-col border-l transition-colors"
|
|
1357
1498
|
title="Click to expand {selectedDocumentType}"
|
|
1358
1499
|
>
|
|
1359
|
-
<div class="
|
|
1360
|
-
<div
|
|
1500
|
+
<div class="flex flex-1 items-start justify-center p-2 pt-8 text-left">
|
|
1501
|
+
<div
|
|
1502
|
+
class="text-foreground -mt-2 text-sm font-medium whitespace-nowrap [writing-mode:vertical-rl]"
|
|
1503
|
+
>
|
|
1361
1504
|
{selectedDocumentType
|
|
1362
1505
|
? selectedDocumentType.charAt(0).toUpperCase() +
|
|
1363
1506
|
selectedDocumentType.slice(1)
|
|
@@ -1375,7 +1518,7 @@
|
|
|
1375
1518
|
|
|
1376
1519
|
{#if isExpanded}
|
|
1377
1520
|
<div
|
|
1378
|
-
class="h-full flex-1 overflow-y-auto border-l transition-all duration-200"
|
|
1521
|
+
class="border-rule h-full flex-1 overflow-x-hidden overflow-y-auto border-l transition-all duration-200"
|
|
1379
1522
|
style="min-width: 0;"
|
|
1380
1523
|
>
|
|
1381
1524
|
<DocumentEditor
|
|
@@ -1386,9 +1529,26 @@
|
|
|
1386
1529
|
onBack={() => handleCloseStackedEditor(index)}
|
|
1387
1530
|
onOpenReference={handleOpenReference}
|
|
1388
1531
|
onOpenVersionHistory={handleOpenVersionHistory}
|
|
1532
|
+
externalVersionPreview={versionPanelDocId === stackedEditor.documentId
|
|
1533
|
+
? versionPreviewData
|
|
1534
|
+
: null}
|
|
1389
1535
|
onSaved={async () => {}}
|
|
1390
1536
|
onAutoSaved={() => {}}
|
|
1391
|
-
onPublished={async () => {
|
|
1537
|
+
onPublished={async () => {
|
|
1538
|
+
if (selectedDocumentType) {
|
|
1539
|
+
await fetchDocuments(selectedDocumentType);
|
|
1540
|
+
}
|
|
1541
|
+
}}
|
|
1542
|
+
onUnpublished={async () => {
|
|
1543
|
+
if (selectedDocumentType) {
|
|
1544
|
+
await fetchDocuments(selectedDocumentType);
|
|
1545
|
+
}
|
|
1546
|
+
}}
|
|
1547
|
+
onRestored={async () => {
|
|
1548
|
+
if (selectedDocumentType) {
|
|
1549
|
+
await fetchDocuments(selectedDocumentType);
|
|
1550
|
+
}
|
|
1551
|
+
}}
|
|
1392
1552
|
onDeleted={async () => {
|
|
1393
1553
|
handleCloseStackedEditor(index);
|
|
1394
1554
|
}}
|
|
@@ -1399,14 +1559,12 @@
|
|
|
1399
1559
|
<!-- Collapsed Stacked Editor Strip -->
|
|
1400
1560
|
<button
|
|
1401
1561
|
onclick={() => setActiveEditor(editorIndex)}
|
|
1402
|
-
class="hover:bg-muted/50 flex h-full w-[60px] flex-col border-l transition-colors"
|
|
1562
|
+
class="border-rule hover:bg-muted/50 flex h-full w-[60px] cursor-pointer flex-col border-l transition-colors"
|
|
1403
1563
|
title="Click to expand {stackedEditor.documentType}"
|
|
1404
1564
|
>
|
|
1405
|
-
<div
|
|
1406
|
-
class="-mt-2 flex h-full flex-1 items-start justify-center p-2 pt-8 text-left"
|
|
1407
|
-
>
|
|
1565
|
+
<div class="flex h-full flex-1 items-start justify-center p-2 pt-8 text-left">
|
|
1408
1566
|
<div
|
|
1409
|
-
class="text-foreground
|
|
1567
|
+
class="text-foreground text-sm font-medium whitespace-nowrap [writing-mode:vertical-rl]"
|
|
1410
1568
|
>
|
|
1411
1569
|
{stackedEditor.documentType.charAt(0).toUpperCase() +
|
|
1412
1570
|
stackedEditor.documentType.slice(1)}
|
|
@@ -1420,12 +1578,14 @@
|
|
|
1420
1578
|
<!-- Version History Panel -->
|
|
1421
1579
|
{#if showVersionPanel && versionPanelDocId}
|
|
1422
1580
|
<div
|
|
1423
|
-
class="h-full w-[280px] min-w-[250px] overflow-y-auto border-l transition-all duration-200"
|
|
1581
|
+
class="border-rule h-full w-[280px] min-w-[250px] overflow-y-auto border-l transition-all duration-200"
|
|
1424
1582
|
>
|
|
1425
1583
|
<DocumentVersionPanel
|
|
1426
1584
|
documentId={versionPanelDocId}
|
|
1427
1585
|
onClose={handleCloseVersionPanel}
|
|
1428
|
-
onPreviewVersion={(v) => {
|
|
1586
|
+
onPreviewVersion={(v) => {
|
|
1587
|
+
versionPreviewData = v;
|
|
1588
|
+
}}
|
|
1429
1589
|
onRestored={async () => {
|
|
1430
1590
|
versionPreviewData = null;
|
|
1431
1591
|
if (selectedDocumentType) {
|
|
@@ -16,6 +16,14 @@ interface Props {
|
|
|
16
16
|
enableGraphiQL: boolean;
|
|
17
17
|
} | null;
|
|
18
18
|
isReadOnly?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Capabilities resolved for the current session. Used for per-action UI
|
|
21
|
+
* gating. When absent, all actions are shown and the server remains the
|
|
22
|
+
* enforcement surface.
|
|
23
|
+
*/
|
|
24
|
+
capabilities?: string[];
|
|
25
|
+
/** Effective organization role name, for role-list style checks. */
|
|
26
|
+
rbacRole?: string | null;
|
|
19
27
|
activeTab?: {
|
|
20
28
|
value: 'structure' | 'vision' | 'media';
|
|
21
29
|
};
|