@actuate-media/cms-admin 0.9.0 → 0.11.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/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js +8 -5
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/layout/primitives.test.d.ts +2 -0
- package/dist/__tests__/layout/primitives.test.d.ts.map +1 -0
- package/dist/__tests__/layout/primitives.test.js +34 -0
- package/dist/__tests__/layout/primitives.test.js.map +1 -0
- package/dist/__tests__/lib/cv.test.d.ts +2 -0
- package/dist/__tests__/lib/cv.test.d.ts.map +1 -0
- package/dist/__tests__/lib/cv.test.js +66 -0
- package/dist/__tests__/lib/cv.test.js.map +1 -0
- package/dist/actuate-admin.css +1 -1
- package/dist/assets/actuate-logo.d.ts +36 -0
- package/dist/assets/actuate-logo.d.ts.map +1 -0
- package/dist/assets/actuate-logo.js +15 -0
- package/dist/assets/actuate-logo.js.map +1 -0
- package/dist/components/Breadcrumbs.js +2 -2
- package/dist/components/CommandPalette.js +10 -10
- package/dist/components/ContentOverviewChart.js +3 -3
- package/dist/components/ErrorBoundary.js +1 -1
- package/dist/components/FocalPointPicker.js +2 -2
- package/dist/components/FolderTree.js +20 -20
- package/dist/components/LivePreview.js +3 -3
- package/dist/components/LocaleSwitcher.js +1 -1
- package/dist/components/MediaPickerModal.js +4 -4
- package/dist/components/PresenceIndicator.js +1 -1
- package/dist/components/SEOConfigPanel.d.ts +2 -0
- package/dist/components/SEOConfigPanel.d.ts.map +1 -0
- package/dist/components/SEOConfigPanel.js +174 -0
- package/dist/components/SEOConfigPanel.js.map +1 -0
- package/dist/components/SEOPanel.js +9 -9
- package/dist/components/SEOPerformance.js +2 -2
- package/dist/components/SchedulePublishDialog.d.ts +18 -0
- package/dist/components/SchedulePublishDialog.d.ts.map +1 -0
- package/dist/components/SchedulePublishDialog.js +106 -0
- package/dist/components/SchedulePublishDialog.js.map +1 -0
- package/dist/components/SharePreviewLinkDialog.d.ts +17 -0
- package/dist/components/SharePreviewLinkDialog.d.ts.map +1 -0
- package/dist/components/SharePreviewLinkDialog.js +83 -0
- package/dist/components/SharePreviewLinkDialog.js.map +1 -0
- package/dist/components/TipTapEditor.js +5 -5
- package/dist/components/VersionHistory.js +2 -2
- package/dist/components/ui/Badge.d.ts +33 -3
- package/dist/components/ui/Badge.d.ts.map +1 -1
- package/dist/components/ui/Badge.js +42 -8
- package/dist/components/ui/Badge.js.map +1 -1
- package/dist/components/ui/Button.d.ts +19 -8
- package/dist/components/ui/Button.d.ts.map +1 -1
- package/dist/components/ui/Button.js +35 -14
- package/dist/components/ui/Button.js.map +1 -1
- package/dist/components/ui/Card.d.ts +26 -0
- package/dist/components/ui/Card.d.ts.map +1 -0
- package/dist/components/ui/Card.js +45 -0
- package/dist/components/ui/Card.js.map +1 -0
- package/dist/components/ui/DataTable.js +1 -1
- package/dist/components/ui/Input.d.ts +15 -0
- package/dist/components/ui/Input.d.ts.map +1 -0
- package/dist/components/ui/Input.js +23 -0
- package/dist/components/ui/Input.js.map +1 -0
- package/dist/components/ui/SearchInput.js +1 -1
- package/dist/components/ui/Select.d.ts +16 -0
- package/dist/components/ui/Select.d.ts.map +1 -0
- package/dist/components/ui/Select.js +25 -0
- package/dist/components/ui/Select.js.map +1 -0
- package/dist/components/ui/Toast.js +1 -1
- package/dist/components/ui/index.d.ts +10 -4
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +5 -2
- package/dist/components/ui/index.js.map +1 -1
- package/dist/fields/BlockBuilderField.js +3 -3
- package/dist/fields/DateField.js +1 -1
- package/dist/fields/RelationshipField.js +3 -3
- package/dist/fields/TextField.js +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/layout/Header.js +1 -1
- package/dist/layout/Layout.d.ts +14 -0
- package/dist/layout/Layout.d.ts.map +1 -1
- package/dist/layout/Layout.js +17 -11
- package/dist/layout/Layout.js.map +1 -1
- package/dist/layout/Sidebar.d.ts.map +1 -1
- package/dist/layout/Sidebar.js +21 -11
- package/dist/layout/Sidebar.js.map +1 -1
- package/dist/layout/primitives/AdminShell.d.ts +43 -0
- package/dist/layout/primitives/AdminShell.d.ts.map +1 -0
- package/dist/layout/primitives/AdminShell.js +51 -0
- package/dist/layout/primitives/AdminShell.js.map +1 -0
- package/dist/layout/primitives/Box.d.ts +19 -0
- package/dist/layout/primitives/Box.d.ts.map +1 -0
- package/dist/layout/primitives/Box.js +12 -0
- package/dist/layout/primitives/Box.js.map +1 -0
- package/dist/layout/primitives/Cluster.d.ts +27 -0
- package/dist/layout/primitives/Cluster.d.ts.map +1 -0
- package/dist/layout/primitives/Cluster.js +37 -0
- package/dist/layout/primitives/Cluster.js.map +1 -0
- package/dist/layout/primitives/Grid.d.ts +45 -0
- package/dist/layout/primitives/Grid.d.ts.map +1 -0
- package/dist/layout/primitives/Grid.js +59 -0
- package/dist/layout/primitives/Grid.js.map +1 -0
- package/dist/layout/primitives/PageContainer.d.ts +36 -0
- package/dist/layout/primitives/PageContainer.d.ts.map +1 -0
- package/dist/layout/primitives/PageContainer.js +41 -0
- package/dist/layout/primitives/PageContainer.js.map +1 -0
- package/dist/layout/primitives/Split.d.ts +34 -0
- package/dist/layout/primitives/Split.d.ts.map +1 -0
- package/dist/layout/primitives/Split.js +27 -0
- package/dist/layout/primitives/Split.js.map +1 -0
- package/dist/layout/primitives/Stack.d.ts +23 -0
- package/dist/layout/primitives/Stack.d.ts.map +1 -0
- package/dist/layout/primitives/Stack.js +34 -0
- package/dist/layout/primitives/Stack.js.map +1 -0
- package/dist/layout/primitives/index.d.ts +30 -0
- package/dist/layout/primitives/index.d.ts.map +1 -0
- package/dist/layout/primitives/index.js +22 -0
- package/dist/layout/primitives/index.js.map +1 -0
- package/dist/layout/primitives/tokens.d.ts +48 -0
- package/dist/layout/primitives/tokens.d.ts.map +1 -0
- package/dist/layout/primitives/tokens.js +54 -0
- package/dist/layout/primitives/tokens.js.map +1 -0
- package/dist/lib/cv.d.ts +53 -0
- package/dist/lib/cv.d.ts.map +1 -0
- package/dist/lib/cv.js +39 -0
- package/dist/lib/cv.js.map +1 -0
- package/dist/views/ApiKeys.d.ts.map +1 -1
- package/dist/views/ApiKeys.js +13 -11
- package/dist/views/ApiKeys.js.map +1 -1
- package/dist/views/CollectionList.js +8 -8
- package/dist/views/Dashboard.d.ts.map +1 -1
- package/dist/views/Dashboard.js +333 -78
- package/dist/views/Dashboard.js.map +1 -1
- package/dist/views/DocumentEdit.d.ts.map +1 -1
- package/dist/views/DocumentEdit.js +17 -5
- package/dist/views/DocumentEdit.js.map +1 -1
- package/dist/views/ForgotPassword.js +2 -2
- package/dist/views/FormEditor.js +5 -5
- package/dist/views/FormSubmissions.js +6 -6
- package/dist/views/Forms.js +2 -2
- package/dist/views/Login.d.ts +16 -1
- package/dist/views/Login.d.ts.map +1 -1
- package/dist/views/Login.js +17 -7
- package/dist/views/Login.js.map +1 -1
- package/dist/views/MediaBrowser.js +16 -16
- package/dist/views/PageEditor.js +2 -2
- package/dist/views/Pages.js +10 -10
- package/dist/views/PostEditor.js +2 -2
- package/dist/views/Posts.js +4 -4
- package/dist/views/Redirects.js +4 -4
- package/dist/views/ResetPassword.js +2 -2
- package/dist/views/SEO.js +6 -6
- package/dist/views/ScriptTagEditor.js +4 -4
- package/dist/views/ScriptTags.js +2 -2
- package/dist/views/Settings.d.ts.map +1 -1
- package/dist/views/Settings.js +9 -8
- package/dist/views/Settings.js.map +1 -1
- package/dist/views/SetupWizard.js +2 -2
- package/dist/views/Users.js +4 -4
- package/dist/views/page-builder/AIBlockAssist.js +1 -1
- package/dist/views/page-builder/AIGenerateDialog.js +10 -10
- package/dist/views/page-builder/BlockEditor.js +10 -10
- package/dist/views/page-builder/BlockPicker.js +4 -4
- package/dist/views/page-builder/BottomBar.js +1 -1
- package/dist/views/page-builder/BuilderToolbar.js +2 -2
- package/dist/views/page-builder/ContextPanel.js +2 -2
- package/dist/views/page-builder/DesignScore.js +9 -9
- package/dist/views/page-builder/NodeSettings.js +8 -8
- package/dist/views/page-builder/PageBuilder.js +3 -3
- package/dist/views/page-builder/PageSettings.js +1 -1
- package/dist/views/page-builder/PageTemplates.js +2 -2
- package/dist/views/page-builder/SEOPanel.js +13 -13
- package/dist/views/page-builder/SavedSections.js +5 -5
- package/dist/views/page-builder/TemplatePicker.js +2 -2
- package/dist/views/page-builder/block-renderers/CTAPreview.js +5 -5
- package/dist/views/page-builder/block-renderers/CardsPreview.js +1 -1
- package/dist/views/page-builder/block-renderers/CodePreview.js +1 -1
- package/dist/views/page-builder/block-renderers/FAQPreview.js +3 -3
- package/dist/views/page-builder/block-renderers/FallbackPreview.js +1 -1
- package/dist/views/page-builder/block-renderers/FormPreview.js +3 -3
- package/dist/views/page-builder/block-renderers/GalleryPreview.js +5 -5
- package/dist/views/page-builder/block-renderers/HeroPreview.js +3 -3
- package/dist/views/page-builder/block-renderers/ImagePreview.js +3 -3
- package/dist/views/page-builder/block-renderers/TextPreview.js +3 -3
- package/dist/views/page-builder/block-renderers/VideoPreview.js +4 -4
- package/dist/views/page-builder/canvas/BlockRenderer.js +1 -1
- package/dist/views/page-builder/canvas/BuilderCanvas.js +3 -3
- package/dist/views/page-builder/canvas/ColumnRenderer.js +2 -2
- package/dist/views/page-builder/canvas/ContainerRenderer.js +2 -2
- package/dist/views/page-builder/canvas/RowRenderer.js +2 -2
- package/dist/views/page-builder/canvas/SectionRenderer.js +2 -2
- package/package.json +6 -2
- package/src/AdminRoot.tsx +21 -11
- package/src/__tests__/layout/primitives.test.ts +37 -0
- package/src/__tests__/lib/cv.test.ts +74 -0
- package/src/assets/actuate-logo.tsx +72 -0
- package/src/components/Breadcrumbs.tsx +6 -6
- package/src/components/CommandPalette.tsx +34 -34
- package/src/components/ContentOverviewChart.tsx +3 -3
- package/src/components/ErrorBoundary.tsx +3 -3
- package/src/components/FocalPointPicker.tsx +4 -4
- package/src/components/FolderTree.tsx +38 -38
- package/src/components/LivePreview.tsx +16 -16
- package/src/components/LocaleSwitcher.tsx +7 -7
- package/src/components/MediaPickerModal.tsx +21 -21
- package/src/components/PresenceIndicator.tsx +2 -2
- package/src/components/SEOConfigPanel.tsx +582 -0
- package/src/components/SEOPanel.tsx +46 -46
- package/src/components/SEOPerformance.tsx +21 -21
- package/src/components/SchedulePublishDialog.tsx +241 -0
- package/src/components/SharePreviewLinkDialog.tsx +227 -0
- package/src/components/TipTapEditor.tsx +33 -33
- package/src/components/VersionHistory.tsx +16 -16
- package/src/components/ui/Badge.tsx +66 -14
- package/src/components/ui/Button.tsx +70 -33
- package/src/components/ui/Card.tsx +101 -0
- package/src/components/ui/DataTable.tsx +1 -1
- package/src/components/ui/Input.tsx +35 -0
- package/src/components/ui/SearchInput.tsx +4 -4
- package/src/components/ui/Select.tsx +56 -0
- package/src/components/ui/Toast.tsx +1 -1
- package/src/components/ui/index.ts +18 -4
- package/src/fields/BlockBuilderField.tsx +3 -3
- package/src/fields/DateField.tsx +1 -1
- package/src/fields/RelationshipField.tsx +10 -10
- package/src/fields/TextField.tsx +1 -1
- package/src/index.ts +32 -0
- package/src/layout/Header.tsx +28 -28
- package/src/layout/Layout.tsx +39 -46
- package/src/layout/Sidebar.tsx +37 -64
- package/src/layout/primitives/AdminShell.tsx +118 -0
- package/src/layout/primitives/Box.tsx +30 -0
- package/src/layout/primitives/Cluster.tsx +74 -0
- package/src/layout/primitives/Grid.tsx +120 -0
- package/src/layout/primitives/PageContainer.tsx +96 -0
- package/src/layout/primitives/Split.tsx +73 -0
- package/src/layout/primitives/Stack.tsx +67 -0
- package/src/layout/primitives/index.ts +36 -0
- package/src/layout/primitives/tokens.ts +76 -0
- package/src/lib/cv.ts +96 -0
- package/src/styles/build-input.css +1 -1
- package/src/views/ApiKeys.tsx +57 -57
- package/src/views/CollectionList.tsx +30 -30
- package/src/views/Dashboard.tsx +737 -186
- package/src/views/DocumentEdit.tsx +90 -10
- package/src/views/ForgotPassword.tsx +18 -18
- package/src/views/FormEditor.tsx +75 -75
- package/src/views/FormSubmissions.tsx +76 -76
- package/src/views/Forms.tsx +27 -27
- package/src/views/Login.tsx +65 -25
- package/src/views/MediaBrowser.tsx +127 -127
- package/src/views/PageEditor.tsx +25 -25
- package/src/views/Pages.tsx +59 -59
- package/src/views/PostEditor.tsx +37 -37
- package/src/views/Posts.tsx +48 -48
- package/src/views/Redirects.tsx +21 -21
- package/src/views/ResetPassword.tsx +28 -28
- package/src/views/SEO.tsx +144 -144
- package/src/views/ScriptTagEditor.tsx +24 -24
- package/src/views/ScriptTags.tsx +10 -10
- package/src/views/Settings.tsx +88 -80
- package/src/views/SetupWizard.tsx +28 -28
- package/src/views/Users.tsx +20 -20
- package/src/views/page-builder/AIBlockAssist.tsx +1 -1
- package/src/views/page-builder/AIGenerateDialog.tsx +63 -63
- package/src/views/page-builder/BlockEditor.tsx +26 -26
- package/src/views/page-builder/BlockPicker.tsx +22 -22
- package/src/views/page-builder/BottomBar.tsx +8 -8
- package/src/views/page-builder/BuilderToolbar.tsx +17 -17
- package/src/views/page-builder/ContextPanel.tsx +3 -3
- package/src/views/page-builder/DesignScore.tsx +21 -21
- package/src/views/page-builder/NodeSettings.tsx +27 -27
- package/src/views/page-builder/PageBuilder.tsx +11 -11
- package/src/views/page-builder/PageSettings.tsx +4 -4
- package/src/views/page-builder/PageTemplates.tsx +18 -18
- package/src/views/page-builder/SEOPanel.tsx +53 -53
- package/src/views/page-builder/SavedSections.tsx +37 -37
- package/src/views/page-builder/TemplatePicker.tsx +17 -17
- package/src/views/page-builder/block-renderers/CTAPreview.tsx +13 -13
- package/src/views/page-builder/block-renderers/CardsPreview.tsx +5 -5
- package/src/views/page-builder/block-renderers/CodePreview.tsx +6 -6
- package/src/views/page-builder/block-renderers/FAQPreview.tsx +13 -13
- package/src/views/page-builder/block-renderers/FallbackPreview.tsx +3 -3
- package/src/views/page-builder/block-renderers/FormPreview.tsx +20 -20
- package/src/views/page-builder/block-renderers/GalleryPreview.tsx +8 -8
- package/src/views/page-builder/block-renderers/HeroPreview.tsx +16 -16
- package/src/views/page-builder/block-renderers/ImagePreview.tsx +4 -4
- package/src/views/page-builder/block-renderers/TextPreview.tsx +14 -14
- package/src/views/page-builder/block-renderers/VideoPreview.tsx +12 -12
- package/src/views/page-builder/canvas/BlockRenderer.tsx +4 -4
- package/src/views/page-builder/canvas/BuilderCanvas.tsx +6 -6
- package/src/views/page-builder/canvas/ColumnRenderer.tsx +3 -3
- package/src/views/page-builder/canvas/ContainerRenderer.tsx +2 -2
- package/src/views/page-builder/canvas/RowRenderer.tsx +2 -2
- package/src/views/page-builder/canvas/SectionRenderer.tsx +2 -2
|
@@ -130,68 +130,68 @@ export function FolderTree({ scope, selected, onSelect, itemCounts, totalCount,
|
|
|
130
130
|
const isActive = isSelected({ type: 'folder', folderId: folder.id });
|
|
131
131
|
const isDragOver = dragOverId === folder.id;
|
|
132
132
|
const count = countForFolder(folder.id);
|
|
133
|
-
return (_jsxs("div", { children: [_jsxs("div", { className: `group flex items-center gap-1 px-2 py-1.5
|
|
134
|
-
? 'bg-blue-50 text-blue-700
|
|
133
|
+
return (_jsxs("div", { children: [_jsxs("div", { className: `group flex cursor-pointer items-center gap-1 rounded-md px-2 py-1.5 text-sm transition-colors ${isActive
|
|
134
|
+
? 'bg-blue-50 font-medium text-blue-700'
|
|
135
135
|
: isDragOver
|
|
136
136
|
? 'bg-blue-100 ring-2 ring-blue-300'
|
|
137
137
|
: 'text-gray-700 hover:bg-gray-100'}`, style: { paddingLeft: `${8 + depth * 16}px` }, onClick: () => onSelect({ type: 'folder', folderId: folder.id }), onContextMenu: (e) => {
|
|
138
138
|
e.preventDefault();
|
|
139
139
|
setContextMenu({ id: folder.id, x: e.clientX, y: e.clientY });
|
|
140
|
-
}, onDragOver: (e) => handleDragOver(e, folder.id), onDragLeave: handleDragLeave, onDrop: (e) => handleDrop(e, folder.id), children: [_jsx("button", { type: "button", className: `p-0.5
|
|
140
|
+
}, onDragOver: (e) => handleDragOver(e, folder.id), onDragLeave: handleDragLeave, onDrop: (e) => handleDrop(e, folder.id), children: [_jsx("button", { type: "button", className: `rounded p-0.5 transition-colors hover:bg-gray-200 ${hasChildren ? '' : 'invisible'}`, onClick: (e) => {
|
|
141
141
|
e.stopPropagation();
|
|
142
142
|
toggleExpand(folder.id);
|
|
143
|
-
}, children: isExpanded ? (_jsx(ChevronDown, { className: "
|
|
143
|
+
}, children: isExpanded ? (_jsx(ChevronDown, { className: "h-3.5 w-3.5" })) : (_jsx(ChevronRight, { className: "h-3.5 w-3.5" })) }), isExpanded ? (_jsx(FolderOpen, { className: "h-4 w-4 shrink-0 text-blue-500" })) : (_jsx(FolderIcon, { className: "h-4 w-4 shrink-0 text-gray-400" })), editingId === folder.id ? (_jsx("input", { ref: editInputRef, type: "text", value: editName, onChange: (e) => setEditName(e.target.value), onBlur: () => handleRename(folder.id), onKeyDown: (e) => {
|
|
144
144
|
if (e.key === 'Enter')
|
|
145
145
|
handleRename(folder.id);
|
|
146
146
|
if (e.key === 'Escape')
|
|
147
147
|
setEditingId(null);
|
|
148
|
-
}, className: "
|
|
148
|
+
}, className: "min-w-0 flex-1 rounded border border-blue-300 px-1 py-0 text-sm focus:ring-1 focus:ring-blue-500 focus:outline-none", onClick: (e) => e.stopPropagation() })) : (_jsx("span", { className: "min-w-0 flex-1 truncate", children: folder.name })), count !== undefined && !editingId && (_jsx("span", { className: "text-xs text-gray-400 tabular-nums", children: count })), _jsx("button", { type: "button", className: "rounded p-0.5 opacity-0 transition-all group-hover:opacity-100 hover:bg-gray-200", onClick: (e) => {
|
|
149
149
|
e.stopPropagation();
|
|
150
150
|
setContextMenu({ id: folder.id, x: e.clientX, y: e.clientY });
|
|
151
|
-
}, children: _jsx(MoreHorizontal, { className: "
|
|
151
|
+
}, children: _jsx(MoreHorizontal, { className: "h-3.5 w-3.5 text-gray-500" }) })] }), isExpanded && (_jsxs("div", { children: [folder.children.map((child) => renderFolder(child, depth + 1)), creatingIn === folder.id && (_jsxs("div", { className: "flex items-center gap-1 px-2 py-1", style: { paddingLeft: `${24 + (depth + 1) * 16}px` }, children: [_jsx(FolderIcon, { className: "h-4 w-4 shrink-0 text-gray-400" }), _jsx("input", { ref: newFolderInputRef, type: "text", value: newFolderName, onChange: (e) => setNewFolderName(e.target.value), onBlur: () => handleCreate(folder.id), onKeyDown: (e) => {
|
|
152
152
|
if (e.key === 'Enter')
|
|
153
153
|
handleCreate(folder.id);
|
|
154
154
|
if (e.key === 'Escape') {
|
|
155
155
|
setCreatingIn(false);
|
|
156
156
|
setNewFolderName('');
|
|
157
157
|
}
|
|
158
|
-
}, placeholder: "Folder name...", className: "
|
|
158
|
+
}, placeholder: "Folder name...", className: "min-w-0 flex-1 rounded border border-blue-300 px-1 py-0 text-sm focus:ring-1 focus:ring-blue-500 focus:outline-none" })] }))] }))] }, folder.id));
|
|
159
159
|
};
|
|
160
160
|
if (loading) {
|
|
161
|
-
return (_jsx("div", { className: "flex items-center justify-center py-8", children: _jsx(Loader2, { className: "
|
|
161
|
+
return (_jsx("div", { className: "flex items-center justify-center py-8", children: _jsx(Loader2, { className: "h-5 w-5 animate-spin text-gray-400" }) }));
|
|
162
162
|
}
|
|
163
|
-
return (_jsxs("div", { className: "flex flex-col
|
|
164
|
-
? 'bg-blue-50 text-blue-700
|
|
165
|
-
: 'text-gray-700 hover:bg-gray-100'}`, onClick: () => onSelect({ type: 'smart', smart: 'all' }), onDragOver: (e) => handleDragOver(e, null), onDragLeave: handleDragLeave, onDrop: (e) => handleDrop(e, null), children: [_jsx(LayoutGrid, { className: "
|
|
166
|
-
? 'bg-blue-50 text-blue-700
|
|
167
|
-
: 'text-gray-700 hover:bg-gray-100'}`, onClick: () => onSelect({ type: 'smart', smart: 'recent' }), children: [_jsx(Clock, { className: "
|
|
168
|
-
? 'bg-blue-50 text-blue-700
|
|
169
|
-
: 'text-gray-700 hover:bg-gray-100'}`, onClick: () => onSelect({ type: 'smart', smart: 'uncategorized' }), children: [_jsx(Inbox, { className: "
|
|
163
|
+
return (_jsxs("div", { className: "flex h-full flex-col", children: [_jsxs("div", { className: "space-y-0.5 px-1 py-2", children: [_jsxs("button", { type: "button", className: `flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors ${isSelected({ type: 'smart', smart: 'all' })
|
|
164
|
+
? 'bg-blue-50 font-medium text-blue-700'
|
|
165
|
+
: 'text-gray-700 hover:bg-gray-100'}`, onClick: () => onSelect({ type: 'smart', smart: 'all' }), onDragOver: (e) => handleDragOver(e, null), onDragLeave: handleDragLeave, onDrop: (e) => handleDrop(e, null), children: [_jsx(LayoutGrid, { className: "h-4 w-4 shrink-0" }), _jsx("span", { className: "flex-1 text-left", children: "All" }), totalCount !== undefined && (_jsx("span", { className: "text-xs text-gray-400 tabular-nums", children: totalCount }))] }), _jsxs("button", { type: "button", className: `flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors ${isSelected({ type: 'smart', smart: 'recent' })
|
|
166
|
+
? 'bg-blue-50 font-medium text-blue-700'
|
|
167
|
+
: 'text-gray-700 hover:bg-gray-100'}`, onClick: () => onSelect({ type: 'smart', smart: 'recent' }), children: [_jsx(Clock, { className: "h-4 w-4 shrink-0" }), _jsx("span", { className: "flex-1 text-left", children: "Recent" }), recentCount !== undefined && (_jsx("span", { className: "text-xs text-gray-400 tabular-nums", children: recentCount }))] }), _jsxs("button", { type: "button", className: `flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors ${isSelected({ type: 'smart', smart: 'uncategorized' })
|
|
168
|
+
? 'bg-blue-50 font-medium text-blue-700'
|
|
169
|
+
: 'text-gray-700 hover:bg-gray-100'}`, onClick: () => onSelect({ type: 'smart', smart: 'uncategorized' }), children: [_jsx(Inbox, { className: "h-4 w-4 shrink-0" }), _jsx("span", { className: "flex-1 text-left", children: "Uncategorized" }), uncategorizedCount !== undefined && (_jsx("span", { className: "text-xs text-gray-400 tabular-nums", children: uncategorizedCount }))] })] }), _jsx("div", { className: "my-1 border-t border-gray-200" }), _jsxs("div", { className: "flex-1 space-y-0.5 overflow-y-auto px-1 py-1", children: [folders.map((folder) => renderFolder(folder, 0)), creatingIn === null && (_jsxs("div", { className: "flex items-center gap-1 px-2 py-1", children: [_jsx(FolderIcon, { className: "ml-5 h-4 w-4 shrink-0 text-gray-400" }), _jsx("input", { ref: newFolderInputRef, type: "text", value: newFolderName, onChange: (e) => setNewFolderName(e.target.value), onBlur: () => handleCreate(null), onKeyDown: (e) => {
|
|
170
170
|
if (e.key === 'Enter')
|
|
171
171
|
handleCreate(null);
|
|
172
172
|
if (e.key === 'Escape') {
|
|
173
173
|
setCreatingIn(false);
|
|
174
174
|
setNewFolderName('');
|
|
175
175
|
}
|
|
176
|
-
}, placeholder: "Folder name...", className: "
|
|
176
|
+
}, placeholder: "Folder name...", className: "min-w-0 flex-1 rounded border border-blue-300 px-1 py-0 text-sm focus:ring-1 focus:ring-blue-500 focus:outline-none" })] }))] }), _jsx("div", { className: "border-t border-gray-200 p-2", children: _jsxs("button", { type: "button", onClick: () => {
|
|
177
177
|
setCreatingIn(null);
|
|
178
178
|
setNewFolderName('');
|
|
179
|
-
}, className: "w-full
|
|
179
|
+
}, className: "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm text-gray-600 transition-colors hover:bg-gray-100", children: [_jsx(Plus, { className: "h-4 w-4" }), "New Folder"] }) }), contextMenu && (_jsxs("div", { className: "fixed z-50 min-w-[160px] rounded-lg border border-gray-200 bg-white py-1 shadow-lg", style: { left: contextMenu.x, top: contextMenu.y }, children: [_jsxs("button", { type: "button", className: "flex w-full items-center gap-2 px-3 py-1.5 text-sm text-gray-700 transition-colors hover:bg-gray-100", onClick: () => {
|
|
180
180
|
const folder = findFolder(folders, contextMenu.id);
|
|
181
181
|
if (folder) {
|
|
182
182
|
setEditingId(folder.id);
|
|
183
183
|
setEditName(folder.name);
|
|
184
184
|
}
|
|
185
185
|
setContextMenu(null);
|
|
186
|
-
}, children: [_jsx(Pencil, { className: "
|
|
186
|
+
}, children: [_jsx(Pencil, { className: "h-3.5 w-3.5" }), "Rename"] }), _jsxs("button", { type: "button", className: "flex w-full items-center gap-2 px-3 py-1.5 text-sm text-gray-700 transition-colors hover:bg-gray-100", onClick: () => {
|
|
187
187
|
setCreatingIn(contextMenu.id);
|
|
188
188
|
setNewFolderName('');
|
|
189
189
|
setExpanded((prev) => new Set(prev).add(contextMenu.id));
|
|
190
190
|
setContextMenu(null);
|
|
191
|
-
}, children: [_jsx(FolderPlus, { className: "
|
|
191
|
+
}, children: [_jsx(FolderPlus, { className: "h-3.5 w-3.5" }), "New Subfolder"] }), _jsx("div", { className: "my-1 border-t border-gray-200" }), _jsxs("button", { type: "button", className: "flex w-full items-center gap-2 px-3 py-1.5 text-sm text-red-600 transition-colors hover:bg-red-50", onClick: () => {
|
|
192
192
|
handleDelete(contextMenu.id);
|
|
193
193
|
setContextMenu(null);
|
|
194
|
-
}, children: [_jsx(Trash2, { className: "
|
|
194
|
+
}, children: [_jsx(Trash2, { className: "h-3.5 w-3.5" }), "Delete"] })] }))] }));
|
|
195
195
|
}
|
|
196
196
|
function findFolder(nodes, id) {
|
|
197
197
|
for (const n of nodes) {
|
|
@@ -53,14 +53,14 @@ export function LivePreview({ collection, documentId, previewUrl, values, onClos
|
|
|
53
53
|
window.open(previewSrc, '_blank');
|
|
54
54
|
}, [previewSrc]);
|
|
55
55
|
if (!previewSrc) {
|
|
56
|
-
return (_jsxs("div", { className: "flex flex-col items-center justify-center
|
|
56
|
+
return (_jsxs("div", { className: "flex h-full flex-col items-center justify-center rounded-lg border border-dashed border-gray-300 bg-gray-50 p-8", children: [_jsx("p", { className: "text-center text-sm text-gray-500", children: !documentId
|
|
57
57
|
? 'Save the document first to enable preview'
|
|
58
58
|
: 'Configure a preview URL in the collection settings to enable live preview' }), _jsx("button", { onClick: onClose, className: "mt-4 text-xs text-gray-400 hover:text-gray-600", children: "Close Preview" })] }));
|
|
59
59
|
}
|
|
60
|
-
return (_jsxs("div", { className: "flex flex-col
|
|
60
|
+
return (_jsxs("div", { className: "flex h-full flex-col border-l border-gray-200 bg-white", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-200 bg-gray-50 px-3 py-2", children: [_jsx("div", { className: "flex items-center gap-1", children: [
|
|
61
61
|
['desktop', Monitor],
|
|
62
62
|
['tablet', Tablet],
|
|
63
63
|
['mobile', Smartphone],
|
|
64
|
-
].map(([vp, Icon]) => (_jsx("button", { onClick: () => setViewport(vp), className: `p-1.5
|
|
64
|
+
].map(([vp, Icon]) => (_jsx("button", { onClick: () => setViewport(vp), className: `rounded p-1.5 ${viewport === vp ? 'bg-white text-blue-600 shadow-sm' : 'text-gray-400 hover:text-gray-600'}`, title: vp, children: _jsx(Icon, { className: "h-4 w-4" }) }, vp))) }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { onClick: handleRefresh, className: "rounded p-1.5 text-gray-400 hover:text-gray-600", title: "Refresh", children: _jsx(RefreshCw, { className: "h-4 w-4" }) }), _jsx("button", { onClick: handleOpenExternal, className: "rounded p-1.5 text-gray-400 hover:text-gray-600", title: "Open in new tab", children: _jsx(ExternalLink, { className: "h-4 w-4" }) }), _jsx("button", { onClick: onClose, className: "rounded p-1.5 text-gray-400 hover:text-gray-600", title: "Close", children: _jsx(X, { className: "h-4 w-4" }) })] })] }), _jsx("div", { className: "flex flex-1 justify-center overflow-auto bg-gray-100 p-4", children: _jsxs("div", { style: { width: VIEWPORT_WIDTHS[viewport], maxWidth: '100%', transition: 'width 0.3s' }, className: "relative", children: [loading && (_jsx("div", { className: "absolute inset-0 z-10 flex items-center justify-center rounded-lg bg-white/80", children: _jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-b-2 border-blue-600" }) })), _jsx("iframe", { ref: iframeRef, src: previewSrc, className: "h-full w-full rounded-lg border border-gray-200 bg-white shadow-lg", style: { minHeight: '600px' }, onLoad: () => setLoading(false), title: "Page Preview" })] }) })] }));
|
|
65
65
|
}
|
|
66
66
|
//# sourceMappingURL=LivePreview.js.map
|
|
@@ -8,6 +8,6 @@ export function LocaleSwitcher() {
|
|
|
8
8
|
if (locales.length < 2)
|
|
9
9
|
return null;
|
|
10
10
|
const activeLabel = locales.find((l) => l.code === activeLocale)?.label ?? activeLocale;
|
|
11
|
-
return (_jsxs(DropdownMenu.Root, { children: [_jsx(DropdownMenu.Trigger, { asChild: true, children: _jsxs("button", { className: "flex items-center gap-1.5 px-2 py-1.5 text-sm hover:bg-[var(--accent)]
|
|
11
|
+
return (_jsxs(DropdownMenu.Root, { children: [_jsx(DropdownMenu.Trigger, { asChild: true, children: _jsxs("button", { className: "flex items-center gap-1.5 rounded-lg px-2 py-1.5 text-sm transition-colors hover:bg-[var(--accent)]", "aria-label": "Switch locale", children: [_jsx(Globe, { className: "h-4 w-4 text-[var(--muted-foreground)]" }), _jsx("span", { className: "hidden text-sm font-medium text-[var(--foreground)] sm:inline", children: activeLabel }), _jsx(ChevronDown, { className: "h-3 w-3 text-[var(--muted-foreground)]" })] }) }), _jsx(DropdownMenu.Portal, { children: _jsx(DropdownMenu.Content, { className: "z-50 min-w-[160px] rounded-lg border border-[var(--border)] bg-[var(--popover)] p-1 text-[var(--popover-foreground)] shadow-lg", align: "end", sideOffset: 5, children: locales.map((locale) => (_jsxs(DropdownMenu.Item, { onSelect: () => setLocale(locale.code), className: "flex cursor-pointer items-center justify-between gap-2 rounded px-3 py-2 text-sm outline-none hover:bg-[var(--accent)]", children: [_jsx("span", { children: locale.label }), locale.code === activeLocale && _jsx(Check, { className: "h-4 w-4 text-[var(--primary)]" })] }, locale.code))) }) })] }));
|
|
12
12
|
}
|
|
13
13
|
//# sourceMappingURL=LocaleSwitcher.js.map
|
|
@@ -44,10 +44,10 @@ export function MediaPickerModal({ open, onClose, onSelect, accept }) {
|
|
|
44
44
|
}
|
|
45
45
|
if (!open)
|
|
46
46
|
return null;
|
|
47
|
-
return (_jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [_jsx("div", { className: "fixed inset-0 bg-black/40", onClick: onClose }), _jsxs("div", { className: "relative
|
|
48
|
-
? '
|
|
47
|
+
return (_jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [_jsx("div", { className: "fixed inset-0 bg-black/40", onClick: onClose }), _jsxs("div", { className: "relative mx-4 flex max-h-[80vh] w-full max-w-2xl flex-col rounded-xl bg-white shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-200 px-4 py-3", children: [_jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Insert Image" }), _jsx("button", { onClick: onClose, className: "rounded-lg p-1.5 transition-colors hover:bg-gray-100", children: _jsx(X, { className: "h-5 w-5 text-gray-500" }) })] }), _jsxs("div", { className: "flex border-b border-gray-200", children: [_jsx("button", { onClick: () => setTab('library'), className: `flex-1 px-4 py-2.5 text-sm font-medium transition-colors ${tab === 'library'
|
|
48
|
+
? 'border-b-2 border-blue-600 text-blue-600'
|
|
49
49
|
: 'text-gray-500 hover:text-gray-700'}`, children: "Media Library" }), _jsx("button", { onClick: () => setTab('upload'), className: `flex-1 px-4 py-2.5 text-sm font-medium transition-colors ${tab === 'upload'
|
|
50
|
-
? '
|
|
51
|
-
: 'text-gray-500 hover:text-gray-700'}`, children: "Upload New" })] }), _jsx("div", { className: "flex-1 overflow-y-auto p-4", children: tab === 'library' ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative mb-4", children: [_jsx(Search, { className: "absolute
|
|
50
|
+
? 'border-b-2 border-blue-600 text-blue-600'
|
|
51
|
+
: 'text-gray-500 hover:text-gray-700'}`, children: "Upload New" })] }), _jsx("div", { className: "flex-1 overflow-y-auto p-4", children: tab === 'library' ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative mb-4", children: [_jsx(Search, { className: "absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-gray-400" }), _jsx("input", { type: "text", value: search, onChange: (e) => setSearch(e.target.value), placeholder: "Search images...", className: "w-full rounded-lg border border-gray-300 py-2 pr-3 pl-9 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none" })] }), loading ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-gray-400" }) })) : imageItems.length === 0 ? (_jsx("div", { className: "py-12 text-center text-sm text-gray-500", children: "No images found. Try uploading one." })) : (_jsx("div", { className: "grid grid-cols-3 gap-3 sm:grid-cols-4", children: imageItems.map((item) => (_jsxs("button", { onClick: () => handleSelectItem(item), className: "group relative aspect-square overflow-hidden rounded-lg border-2 border-gray-200 bg-gray-100 transition-colors hover:border-blue-500", children: [_jsx("div", { className: "flex h-full w-full items-center justify-center", children: _jsx(ImageIcon, { className: "h-8 w-8 text-gray-300" }) }), _jsx("div", { className: "absolute inset-x-0 bottom-0 bg-black/60 p-1.5 opacity-0 transition-opacity group-hover:opacity-100", children: _jsx("p", { className: "truncate text-xs text-white", children: item.filename }) })] }, item.id))) }))] })) : (_jsxs("div", { className: "flex flex-col items-center justify-center py-12", children: [_jsx("input", { ref: fileInputRef, type: "file", accept: accept ?? 'image/*', className: "hidden", onChange: (e) => handleUpload(e.target.files) }), _jsx("div", { className: "mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-blue-50", children: uploading ? (_jsx(Loader2, { className: "h-8 w-8 animate-spin text-blue-600" })) : (_jsx(Upload, { className: "h-8 w-8 text-blue-600" })) }), _jsx("p", { className: "mb-4 text-sm text-gray-600", children: uploading ? 'Uploading...' : 'Select an image file to upload' }), !uploading && (_jsx("button", { onClick: () => fileInputRef.current?.click(), className: "rounded-lg bg-blue-600 px-4 py-2 text-sm text-white transition-colors hover:bg-blue-700", children: "Choose File" }))] })) })] })] }));
|
|
52
52
|
}
|
|
53
53
|
//# sourceMappingURL=MediaPickerModal.js.map
|
|
@@ -35,6 +35,6 @@ export function PresenceIndicator({ documentId, currentUserId }) {
|
|
|
35
35
|
}, [documentId, currentUserId]);
|
|
36
36
|
if (users.length === 0)
|
|
37
37
|
return null;
|
|
38
|
-
return (_jsxs("div", { className: "flex items-center gap-1", title: `${users.length} other editor(s) active`, children: [users.slice(0, 5).map((user, i) => (_jsx("div", { className: "
|
|
38
|
+
return (_jsxs("div", { className: "flex items-center gap-1", title: `${users.length} other editor(s) active`, children: [users.slice(0, 5).map((user, i) => (_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-full text-xs font-medium text-white ring-2 ring-white", style: { backgroundColor: COLORS[i % COLORS.length] }, title: user.name, children: user.name.charAt(0).toUpperCase() }, user.userId))), users.length > 5 && (_jsxs("div", { className: "bg-muted text-muted-foreground flex h-7 w-7 items-center justify-center rounded-full text-xs font-medium ring-2 ring-white", children: ["+", users.length - 5] }))] }));
|
|
39
39
|
}
|
|
40
40
|
//# sourceMappingURL=PresenceIndicator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SEOConfigPanel.d.ts","sourceRoot":"","sources":["../../src/components/SEOConfigPanel.tsx"],"names":[],"mappings":"AAoFA,wBAAgB,cAAc,4CA+V7B"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Admin UI for editing the site-wide + per-collection SEO defaults that are
|
|
5
|
+
* normally defined in `actuate.config.ts`. The CMS merges code-level config
|
|
6
|
+
* with these DB-stored overrides, so any field left blank here falls back to
|
|
7
|
+
* the static config (shown as the placeholder).
|
|
8
|
+
*
|
|
9
|
+
* Wired in `Settings.tsx` as the "SEO" tab. Talks to `/seo/config` (GET + PUT).
|
|
10
|
+
*/
|
|
11
|
+
import { Loader2, RefreshCw, Save, Globe, FileText } from 'lucide-react';
|
|
12
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
13
|
+
import { toast } from 'sonner';
|
|
14
|
+
import { cmsApi } from '../lib/api.js';
|
|
15
|
+
const SCHEMA_TYPES = [
|
|
16
|
+
'',
|
|
17
|
+
'WebPage',
|
|
18
|
+
'Article',
|
|
19
|
+
'BlogPosting',
|
|
20
|
+
'NewsArticle',
|
|
21
|
+
'Product',
|
|
22
|
+
'Service',
|
|
23
|
+
'LocalBusiness',
|
|
24
|
+
'FAQPage',
|
|
25
|
+
'JobPosting',
|
|
26
|
+
'Event',
|
|
27
|
+
'Recipe',
|
|
28
|
+
'Person',
|
|
29
|
+
'Organization',
|
|
30
|
+
];
|
|
31
|
+
const CHANGE_FREQS = ['', 'always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'never'];
|
|
32
|
+
export function SEOConfigPanel() {
|
|
33
|
+
const [data, setData] = useState(null);
|
|
34
|
+
const [loading, setLoading] = useState(true);
|
|
35
|
+
const [saving, setSaving] = useState(false);
|
|
36
|
+
const [error, setError] = useState(null);
|
|
37
|
+
const [site, setSite] = useState({});
|
|
38
|
+
const [collections, setCollections] = useState({});
|
|
39
|
+
async function load() {
|
|
40
|
+
setLoading(true);
|
|
41
|
+
setError(null);
|
|
42
|
+
const res = await cmsApi('/seo/config', { method: 'GET' });
|
|
43
|
+
if (res.error || !res.data) {
|
|
44
|
+
setError(res.error ?? 'Failed to load SEO config');
|
|
45
|
+
setLoading(false);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
setData(res.data);
|
|
49
|
+
setSite(res.data.overrides?.site ?? {});
|
|
50
|
+
setCollections(res.data.overrides?.collections ?? {});
|
|
51
|
+
setLoading(false);
|
|
52
|
+
}
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
void load();
|
|
55
|
+
}, []);
|
|
56
|
+
// Field-level placeholder = the static value from actuate.config.ts. The
|
|
57
|
+
// input value is whatever the admin has typed (or the saved override). Empty
|
|
58
|
+
// input + empty override means "fall back to static", which is the desired
|
|
59
|
+
// UX for an additive overrides panel.
|
|
60
|
+
const staticSite = data?.static?.site ?? {};
|
|
61
|
+
async function handleSave() {
|
|
62
|
+
setSaving(true);
|
|
63
|
+
// Strip empty strings so they unset back to the static default rather
|
|
64
|
+
// than persisting "" — the store treats "" as "no override".
|
|
65
|
+
const cleanSite = stripEmpty(site);
|
|
66
|
+
const cleanCollections = {};
|
|
67
|
+
for (const [slug, c] of Object.entries(collections)) {
|
|
68
|
+
const cleaned = stripEmpty(c);
|
|
69
|
+
if (Object.keys(cleaned).length > 0)
|
|
70
|
+
cleanCollections[slug] = cleaned;
|
|
71
|
+
}
|
|
72
|
+
const res = await cmsApi('/seo/config', {
|
|
73
|
+
method: 'PUT',
|
|
74
|
+
body: JSON.stringify({ site: cleanSite, collections: cleanCollections }),
|
|
75
|
+
});
|
|
76
|
+
if (res.error) {
|
|
77
|
+
toast.error(res.error);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
toast.success('SEO defaults saved');
|
|
81
|
+
await load();
|
|
82
|
+
}
|
|
83
|
+
setSaving(false);
|
|
84
|
+
}
|
|
85
|
+
function patchSite(key, value) {
|
|
86
|
+
setSite((prev) => ({ ...prev, [key]: value }));
|
|
87
|
+
}
|
|
88
|
+
function patchSiteRobots(key, value) {
|
|
89
|
+
setSite((prev) => ({ ...prev, robots: { ...(prev.robots ?? {}), [key]: value } }));
|
|
90
|
+
}
|
|
91
|
+
function patchSiteSitemap(key, value) {
|
|
92
|
+
setSite((prev) => ({ ...prev, sitemap: { ...(prev.sitemap ?? {}), [key]: value } }));
|
|
93
|
+
}
|
|
94
|
+
function patchSiteOg(key, value) {
|
|
95
|
+
setSite((prev) => ({ ...prev, ogImage: { ...(prev.ogImage ?? {}), [key]: value } }));
|
|
96
|
+
}
|
|
97
|
+
function patchCollection(slug, key, value) {
|
|
98
|
+
setCollections((prev) => ({
|
|
99
|
+
...prev,
|
|
100
|
+
[slug]: { ...(prev[slug] ?? {}), [key]: value },
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
function patchCollectionRobots(slug, key, value) {
|
|
104
|
+
setCollections((prev) => ({
|
|
105
|
+
...prev,
|
|
106
|
+
[slug]: {
|
|
107
|
+
...(prev[slug] ?? {}),
|
|
108
|
+
defaultRobots: { ...(prev[slug]?.defaultRobots ?? {}), [key]: value },
|
|
109
|
+
},
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
const updatedAt = useMemo(() => {
|
|
113
|
+
// `updatedAt` is set by the server when overrides are persisted but isn't
|
|
114
|
+
// part of the declared `SeoConfigOverrides` shape. Cast through `unknown`
|
|
115
|
+
// so we can surface it in the UI without widening the type contract.
|
|
116
|
+
const ts = data?.overrides?.updatedAt;
|
|
117
|
+
if (!ts)
|
|
118
|
+
return null;
|
|
119
|
+
try {
|
|
120
|
+
return new Date(ts).toLocaleString();
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return ts;
|
|
124
|
+
}
|
|
125
|
+
}, [data]);
|
|
126
|
+
if (loading) {
|
|
127
|
+
return (_jsxs("div", { className: "flex items-center gap-2 py-8 text-sm text-gray-600", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), " Loading SEO defaults\u2026"] }));
|
|
128
|
+
}
|
|
129
|
+
if (error) {
|
|
130
|
+
return (_jsxs("div", { className: "rounded-lg border border-red-200 bg-red-50 p-4 text-sm text-red-700", children: [_jsx("div", { className: "mb-2 font-medium", children: "Failed to load SEO config" }), _jsx("div", { className: "font-mono text-xs", children: error }), _jsxs("button", { onClick: () => void load(), className: "mt-3 inline-flex items-center gap-1.5 rounded-md border border-red-300 bg-white px-3 py-1.5 text-xs font-medium text-red-700 hover:bg-red-100", children: [_jsx(RefreshCw, { className: "h-3.5 w-3.5" }), " Retry"] })] }));
|
|
131
|
+
}
|
|
132
|
+
return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { children: [_jsxs("p", { className: "text-sm text-gray-600", children: ["Edit the SEO defaults that flow into", ' ', _jsx("code", { className: "rounded bg-gray-100 px-1 py-0.5 text-[11px]", children: "/sitemap.xml" }), ",", ' ', _jsx("code", { className: "rounded bg-gray-100 px-1 py-0.5 text-[11px]", children: "/robots.txt" }), ",", ' ', _jsx("code", { className: "rounded bg-gray-100 px-1 py-0.5 text-[11px]", children: "/og.png" }), ", and the", ' ', _jsx("code", { className: "rounded bg-gray-100 px-1 py-0.5 text-[11px]", children: "/resolve" }), " meta + JSON-LD response. Empty fields fall back to", ' ', _jsx("code", { className: "rounded bg-gray-100 px-1 py-0.5 text-[11px]", children: "actuate.config.ts" }), "."] }), updatedAt && _jsxs("p", { className: "mt-1 text-xs text-gray-500", children: ["Last saved: ", updatedAt] })] }), _jsxs("button", { onClick: handleSave, disabled: saving, className: "inline-flex shrink-0 items-center gap-1.5 rounded-md bg-blue-600 px-3 py-1.5 text-sm font-medium text-white shadow-sm hover:bg-blue-700 disabled:opacity-50", children: [saving ? _jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : _jsx(Save, { className: "h-4 w-4" }), "Save SEO defaults"] })] }), _jsxs("section", { className: "rounded-lg border border-gray-200 bg-white", children: [_jsxs("header", { className: "flex items-center gap-2 border-b border-gray-200 px-4 py-3", children: [_jsx(Globe, { className: "h-4 w-4 text-blue-600" }), _jsx("h3", { className: "text-sm font-semibold text-gray-900", children: "Site-wide defaults" })] }), _jsxs("div", { className: "grid grid-cols-1 gap-4 p-4 md:grid-cols-2", children: [_jsx(Field, { label: "Site URL", placeholder: staticSite.siteUrl ?? 'https://example.com', value: site.siteUrl ?? '', onChange: (v) => patchSite('siteUrl', v), hint: "Canonical origin used in sitemap URLs and JSON-LD." }), _jsx(Field, { label: "Site name", placeholder: staticSite.siteName ?? 'My Site', value: site.siteName ?? '', onChange: (v) => patchSite('siteName', v), hint: "Brand name in OG cards and Schema.org Organization." }), _jsx(Field, { label: "Default OG image", placeholder: staticSite.defaultOgImage ?? '/og-default.png', value: site.defaultOgImage ?? '', onChange: (v) => patchSite('defaultOgImage', v), hint: "Used when a document doesn't supply its own." }), _jsx(Field, { label: "Twitter handle", placeholder: staticSite.twitterHandle ?? '@yourbrand', value: site.twitterHandle ?? '', onChange: (v) => patchSite('twitterHandle', v), hint: "Including the leading @." })] }), _jsxs("div", { className: "grid grid-cols-1 gap-4 border-t border-gray-100 p-4 md:grid-cols-3", children: [_jsx(Select, { label: "Default change frequency", value: site.sitemap?.defaultChangeFreq ?? '', options: CHANGE_FREQS, onChange: (v) => patchSiteSitemap('defaultChangeFreq', v), placeholderLabel: `default: ${staticSite.sitemap?.defaultChangeFreq ?? 'weekly'}`, hint: "Applied per-URL in sitemap.xml when a collection doesn't override." }), _jsx(NumberField, { label: "Default priority", value: site.sitemap?.defaultPriority, placeholder: `default: ${staticSite.sitemap?.defaultPriority ?? 0.6}`, onChange: (v) => patchSiteSitemap('defaultPriority', v), min: 0, max: 1, step: 0.1, hint: "Between 0.0 and 1.0." }), _jsx(Select, { label: "OG image theme", value: site.ogImage?.theme ?? '', options: ['', 'light', 'dark'], onChange: (v) => patchSiteOg('theme', (v || undefined)), placeholderLabel: `default: ${staticSite.ogImage?.theme ?? 'light'}`, hint: "Applied by the built-in /og.png renderer." })] }), _jsxs("div", { className: "flex flex-wrap gap-6 border-t border-gray-100 p-4", children: [_jsx(Toggle, { label: "Block known AI bots in robots.txt", checked: !!site.robots?.blockAIBots, onChange: (v) => patchSiteRobots('blockAIBots', v), hint: "GPTBot, ClaudeBot, anthropic-ai, Bytespider, etc." }), _jsx(Toggle, { label: "Disable robots.txt route", checked: !!site.robots?.disabled, onChange: (v) => patchSiteRobots('disabled', v) }), _jsx(Toggle, { label: "Disable sitemap.xml route", checked: !!site.sitemap?.disabled, onChange: (v) => patchSiteSitemap('disabled', v) }), _jsx(Toggle, { label: "Disable /og.png route", checked: !!site.ogImage?.disabled, onChange: (v) => patchSiteOg('disabled', v) })] })] }), _jsxs("section", { className: "rounded-lg border border-gray-200 bg-white", children: [_jsxs("header", { className: "flex items-center gap-2 border-b border-gray-200 px-4 py-3", children: [_jsx(FileText, { className: "h-4 w-4 text-blue-600" }), _jsx("h3", { className: "text-sm font-semibold text-gray-900", children: "Per-collection defaults" }), _jsx("span", { className: "text-xs text-gray-500", children: "\u2014 shown placeholders are the static defaults" })] }), _jsxs("div", { className: "divide-y divide-gray-100", children: [data?.collections.map((col) => {
|
|
133
|
+
const override = collections[col.slug] ?? {};
|
|
134
|
+
const staticC = col.staticSeo ?? {};
|
|
135
|
+
return (_jsxs("div", { className: "p-4", children: [_jsx("div", { className: "mb-3 flex items-baseline justify-between gap-4", children: _jsxs("div", { children: [_jsx("h4", { className: "text-sm font-semibold text-gray-900", children: col.label }), _jsxs("p", { className: "text-xs text-gray-500", children: [_jsx("code", { className: "rounded bg-gray-100 px-1 py-0.5", children: col.slug }), col.urlPrefix !== undefined && (_jsxs(_Fragment, { children: [' ', "prefix:", ' ', _jsxs("code", { className: "rounded bg-gray-100 px-1 py-0.5", children: ["/", col.urlPrefix] })] })), ' ', "\u2014 ", col.type] })] }) }), _jsxs("div", { className: "grid grid-cols-1 gap-3 md:grid-cols-3", children: [_jsx(Select, { label: "Default Schema.org type", value: override.defaultSchemaType ?? '', options: SCHEMA_TYPES, onChange: (v) => patchCollection(col.slug, 'defaultSchemaType', v || undefined), placeholderLabel: `default: ${staticC.defaultSchemaType ?? 'auto-detect'}` }), _jsx(Field, { label: "Archive path", placeholder: staticC.archivePath ?? '/blog', value: override.archivePath ?? '', onChange: (v) => patchCollection(col.slug, 'archivePath', v) }), _jsx(Select, { label: "Sitemap change frequency", value: override.sitemapChangeFreq ?? '', options: CHANGE_FREQS, onChange: (v) => patchCollection(col.slug, 'sitemapChangeFreq', v), placeholderLabel: `default: ${staticC.sitemapChangeFreq ?? 'weekly'}` }), _jsx(NumberField, { label: "Sitemap priority", value: override.sitemapPriority, placeholder: `default: ${staticC.sitemapPriority ?? (col.type === 'page' ? 0.8 : 0.6)}`, onChange: (v) => patchCollection(col.slug, 'sitemapPriority', v), min: 0, max: 1, step: 0.1 })] }), _jsxs("div", { className: "mt-3 flex flex-wrap gap-4", children: [_jsx(Toggle, { label: "Exclude from sitemap", checked: !!override.excludeFromSitemap, onChange: (v) => patchCollection(col.slug, 'excludeFromSitemap', v) }), _jsx(Toggle, { label: "Default: noindex", checked: !!override.defaultRobots?.noIndex, onChange: (v) => patchCollectionRobots(col.slug, 'noIndex', v) }), _jsx(Toggle, { label: "Default: nofollow", checked: !!override.defaultRobots?.noFollow, onChange: (v) => patchCollectionRobots(col.slug, 'noFollow', v) })] })] }, col.slug));
|
|
136
|
+
}), data?.collections.length === 0 && (_jsxs("div", { className: "p-4 text-sm text-gray-500", children: ["No collections configured in ", _jsx("code", { children: "actuate.config.ts" }), "."] }))] })] }), _jsx("div", { className: "flex justify-end", children: _jsxs("button", { onClick: handleSave, disabled: saving, className: "inline-flex items-center gap-1.5 rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-blue-700 disabled:opacity-50", children: [saving ? _jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : _jsx(Save, { className: "h-4 w-4" }), "Save SEO defaults"] }) })] }));
|
|
137
|
+
}
|
|
138
|
+
function stripEmpty(obj) {
|
|
139
|
+
const out = {};
|
|
140
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
141
|
+
if (v === undefined || v === null)
|
|
142
|
+
continue;
|
|
143
|
+
if (typeof v === 'string' && v.trim() === '')
|
|
144
|
+
continue;
|
|
145
|
+
if (typeof v === 'object' && !Array.isArray(v)) {
|
|
146
|
+
const nested = stripEmpty(v);
|
|
147
|
+
if (Object.keys(nested).length > 0)
|
|
148
|
+
out[k] = nested;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
out[k] = v;
|
|
152
|
+
}
|
|
153
|
+
return out;
|
|
154
|
+
}
|
|
155
|
+
function Field({ label, value, placeholder, onChange, hint, }) {
|
|
156
|
+
return (_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-xs font-medium text-gray-700", children: label }), _jsx("input", { type: "text", value: value, placeholder: placeholder, onChange: (e) => onChange(e.target.value), className: "w-full rounded-md border border-gray-300 px-2.5 py-1.5 text-sm text-gray-900 placeholder-gray-400 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none" }), hint && _jsx("p", { className: "mt-1 text-[11px] text-gray-500", children: hint })] }));
|
|
157
|
+
}
|
|
158
|
+
function NumberField({ label, value, placeholder, onChange, min, max, step, hint, }) {
|
|
159
|
+
return (_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-xs font-medium text-gray-700", children: label }), _jsx("input", { type: "number", value: value ?? '', placeholder: placeholder, min: min, max: max, step: step, onChange: (e) => {
|
|
160
|
+
const raw = e.target.value;
|
|
161
|
+
if (raw === '')
|
|
162
|
+
return onChange(undefined);
|
|
163
|
+
const n = Number(raw);
|
|
164
|
+
if (Number.isFinite(n))
|
|
165
|
+
onChange(n);
|
|
166
|
+
}, className: "w-full rounded-md border border-gray-300 px-2.5 py-1.5 text-sm text-gray-900 placeholder-gray-400 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none" }), hint && _jsx("p", { className: "mt-1 text-[11px] text-gray-500", children: hint })] }));
|
|
167
|
+
}
|
|
168
|
+
function Select({ label, value, options, onChange, placeholderLabel, hint, }) {
|
|
169
|
+
return (_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-xs font-medium text-gray-700", children: label }), _jsx("select", { value: value, onChange: (e) => onChange(e.target.value), className: "w-full rounded-md border border-gray-300 bg-white px-2.5 py-1.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none", children: options.map((opt) => (_jsx("option", { value: opt, children: opt === '' ? (placeholderLabel ?? '— use default —') : opt }, opt))) }), hint && _jsx("p", { className: "mt-1 text-[11px] text-gray-500", children: hint })] }));
|
|
170
|
+
}
|
|
171
|
+
function Toggle({ label, checked, onChange, hint, }) {
|
|
172
|
+
return (_jsxs("label", { className: "flex cursor-pointer items-center gap-2 text-sm text-gray-700", children: [_jsx("input", { type: "checkbox", checked: checked, onChange: (e) => onChange(e.target.checked), className: "h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500" }), _jsxs("span", { children: [label, hint && _jsxs("span", { className: "ml-1 text-[11px] text-gray-500", children: ["\u2014 ", hint] })] })] }));
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=SEOConfigPanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SEOConfigPanel.js","sourceRoot":"","sources":["../../src/components/SEOConfigPanel.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AACxE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAC9B,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAmDtC,MAAM,YAAY,GAAG;IACnB,EAAE;IACF,SAAS;IACT,SAAS;IACT,aAAa;IACb,aAAa;IACb,SAAS;IACT,SAAS;IACT,eAAe;IACf,SAAS;IACT,YAAY;IACZ,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,cAAc;CACf,CAAA;AAED,MAAM,YAAY,GAAG,CAAC,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;AAE9F,MAAM,UAAU,cAAc;IAC5B,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAwB,IAAI,CAAC,CAAA;IAC7D,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;IAC5C,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC3C,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAA;IAEvD,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAmB,EAAE,CAAC,CAAA;IACtD,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAyC,EAAE,CAAC,CAAA;IAE1F,KAAK,UAAU,IAAI;QACjB,UAAU,CAAC,IAAI,CAAC,CAAA;QAChB,QAAQ,CAAC,IAAI,CAAC,CAAA;QACd,MAAM,GAAG,GAAG,MAAM,MAAM,CAAiB,aAAa,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;QAC1E,IAAI,GAAG,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YAC3B,QAAQ,CAAC,GAAG,CAAC,KAAK,IAAI,2BAA2B,CAAC,CAAA;YAClD,UAAU,CAAC,KAAK,CAAC,CAAA;YACjB,OAAM;QACR,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,CAAA;QACvC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,IAAI,EAAE,CAAC,CAAA;QACrD,UAAU,CAAC,KAAK,CAAC,CAAA;IACnB,CAAC;IAED,SAAS,CAAC,GAAG,EAAE;QACb,KAAK,IAAI,EAAE,CAAA;IACb,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,yEAAyE;IACzE,6EAA6E;IAC7E,2EAA2E;IAC3E,sCAAsC;IACtC,MAAM,UAAU,GAAG,IAAI,EAAE,MAAM,EAAE,IAAI,IAAI,EAAE,CAAA;IAE3C,KAAK,UAAU,UAAU;QACvB,SAAS,CAAC,IAAI,CAAC,CAAA;QACf,sEAAsE;QACtE,6DAA6D;QAC7D,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;QAClC,MAAM,gBAAgB,GAA2C,EAAE,CAAA;QACnE,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;YACpD,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,CAAA;YAC7B,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC;gBAAE,gBAAgB,CAAC,IAAI,CAAC,GAAG,OAAO,CAAA;QACvE,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,aAAa,EAAE;YACtC,MAAM,EAAE,KAAK;YACb,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC;SACzE,CAAC,CAAA;QACF,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;YACd,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QACxB,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAA;YACnC,MAAM,IAAI,EAAE,CAAA;QACd,CAAC;QACD,SAAS,CAAC,KAAK,CAAC,CAAA;IAClB,CAAC;IAED,SAAS,SAAS,CAA0B,GAAM,EAAE,KAAiB;QACnE,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAA;IAChD,CAAC;IACD,SAAS,eAAe,CAAC,GAA+B,EAAE,KAAc;QACtE,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAA;IACpF,CAAC;IACD,SAAS,gBAAgB,CACvB,GAAM,EACN,KAAyC;QAEzC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAA;IACtF,CAAC;IACD,SAAS,WAAW,CAClB,GAAM,EACN,KAAyC;QAEzC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAA;IACtF,CAAC;IACD,SAAS,eAAe,CACtB,IAAY,EACZ,GAAM,EACN,KAAuB;QAEvB,cAAc,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACxB,GAAG,IAAI;YACP,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE;SAChD,CAAC,CAAC,CAAA;IACL,CAAC;IACD,SAAS,qBAAqB,CAAC,IAAY,EAAE,GAAyB,EAAE,KAAc;QACpF,cAAc,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACxB,GAAG,IAAI;YACP,CAAC,IAAI,CAAC,EAAE;gBACN,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACrB,aAAa,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,aAAa,IAAI,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE;aACtE;SACF,CAAC,CAAC,CAAA;IACL,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,EAAE;QAC7B,0EAA0E;QAC1E,0EAA0E;QAC1E,qEAAqE;QACrE,MAAM,EAAE,GAAI,IAAI,EAAE,SAAgD,EAAE,SAAS,CAAA;QAC7E,IAAI,CAAC,EAAE;YAAE,OAAO,IAAI,CAAA;QACpB,IAAI,CAAC;YACH,OAAO,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,cAAc,EAAE,CAAA;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAA;QACX,CAAC;IACH,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAA;IAEV,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,CACL,eAAK,SAAS,EAAC,oDAAoD,aACjE,KAAC,OAAO,IAAC,SAAS,EAAC,sBAAsB,GAAG,mCACxC,CACP,CAAA;IACH,CAAC;IAED,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CACL,eAAK,SAAS,EAAC,qEAAqE,aAClF,cAAK,SAAS,EAAC,kBAAkB,0CAAgC,EACjE,cAAK,SAAS,EAAC,mBAAmB,YAAE,KAAK,GAAO,EAChD,kBACE,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE,EAC1B,SAAS,EAAC,+IAA+I,aAEzJ,KAAC,SAAS,IAAC,SAAS,EAAC,aAAa,GAAG,cAC9B,IACL,CACP,CAAA;IACH,CAAC;IAED,OAAO,CACL,eAAK,SAAS,EAAC,WAAW,aACxB,eAAK,SAAS,EAAC,wCAAwC,aACrD,0BACE,aAAG,SAAS,EAAC,uBAAuB,qDACG,GAAG,EACxC,eAAM,SAAS,EAAC,6CAA6C,6BAAoB,OAAE,GAAG,EACtF,eAAM,SAAS,EAAC,6CAA6C,4BAAmB,OAAE,GAAG,EACrF,eAAM,SAAS,EAAC,6CAA6C,wBAAe,eAAU,GAAG,EACzF,eAAM,SAAS,EAAC,6CAA6C,yBAAgB,yDACjC,GAAG,EAC/C,eAAM,SAAS,EAAC,6CAA6C,kCAAyB,SACpF,EACH,SAAS,IAAI,aAAG,SAAS,EAAC,4BAA4B,6BAAc,SAAS,IAAK,IAC/E,EACN,kBACE,OAAO,EAAE,UAAU,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAC,6JAA6J,aAEtK,MAAM,CAAC,CAAC,CAAC,KAAC,OAAO,IAAC,SAAS,EAAC,sBAAsB,GAAG,CAAC,CAAC,CAAC,KAAC,IAAI,IAAC,SAAS,EAAC,SAAS,GAAG,yBAE9E,IACL,EAGN,mBAAS,SAAS,EAAC,4CAA4C,aAC7D,kBAAQ,SAAS,EAAC,4DAA4D,aAC5E,KAAC,KAAK,IAAC,SAAS,EAAC,uBAAuB,GAAG,EAC3C,aAAI,SAAS,EAAC,qCAAqC,mCAAwB,IACpE,EACT,eAAK,SAAS,EAAC,2CAA2C,aACxD,KAAC,KAAK,IACJ,KAAK,EAAC,UAAU,EAChB,WAAW,EAAE,UAAU,CAAC,OAAO,IAAI,qBAAqB,EACxD,KAAK,EAAE,IAAI,CAAC,OAAO,IAAI,EAAE,EACzB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC,CAAC,EACxC,IAAI,EAAC,oDAAoD,GACzD,EACF,KAAC,KAAK,IACJ,KAAK,EAAC,WAAW,EACjB,WAAW,EAAE,UAAU,CAAC,QAAQ,IAAI,SAAS,EAC7C,KAAK,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE,EAC1B,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC,EACzC,IAAI,EAAC,qDAAqD,GAC1D,EACF,KAAC,KAAK,IACJ,KAAK,EAAC,kBAAkB,EACxB,WAAW,EAAE,UAAU,CAAC,cAAc,IAAI,iBAAiB,EAC3D,KAAK,EAAE,IAAI,CAAC,cAAc,IAAI,EAAE,EAChC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,gBAAgB,EAAE,CAAC,CAAC,EAC/C,IAAI,EAAC,8CAA8C,GACnD,EACF,KAAC,KAAK,IACJ,KAAK,EAAC,gBAAgB,EACtB,WAAW,EAAE,UAAU,CAAC,aAAa,IAAI,YAAY,EACrD,KAAK,EAAE,IAAI,CAAC,aAAa,IAAI,EAAE,EAC/B,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,eAAe,EAAE,CAAC,CAAC,EAC9C,IAAI,EAAC,0BAA0B,GAC/B,IACE,EAEN,eAAK,SAAS,EAAC,oEAAoE,aACjF,KAAC,MAAM,IACL,KAAK,EAAC,0BAA0B,EAChC,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,iBAAiB,IAAI,EAAE,EAC5C,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,mBAAmB,EAAE,CAAQ,CAAC,EAChE,gBAAgB,EAAE,YAAY,UAAU,CAAC,OAAO,EAAE,iBAAiB,IAAI,QAAQ,EAAE,EACjF,IAAI,EAAC,oEAAoE,GACzE,EACF,KAAC,WAAW,IACV,KAAK,EAAC,kBAAkB,EACxB,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,eAAe,EACpC,WAAW,EAAE,YAAY,UAAU,CAAC,OAAO,EAAE,eAAe,IAAI,GAAG,EAAE,EACrE,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,iBAAiB,EAAE,CAAC,CAAC,EACvD,GAAG,EAAE,CAAC,EACN,GAAG,EAAE,CAAC,EACN,IAAI,EAAE,GAAG,EACT,IAAI,EAAC,sBAAsB,GAC3B,EACF,KAAC,MAAM,IACL,KAAK,EAAC,gBAAgB,EACtB,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE,EAChC,OAAO,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,EAC9B,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,SAAS,CAAiC,CAAC,EACvF,gBAAgB,EAAE,YAAY,UAAU,CAAC,OAAO,EAAE,KAAK,IAAI,OAAO,EAAE,EACpE,IAAI,EAAC,2CAA2C,GAChD,IACE,EAEN,eAAK,SAAS,EAAC,mDAAmD,aAChE,KAAC,MAAM,IACL,KAAK,EAAC,mCAAmC,EACzC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,EACnC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,aAAa,EAAE,CAAC,CAAC,EAClD,IAAI,EAAC,mDAAmD,GACxD,EACF,KAAC,MAAM,IACL,KAAK,EAAC,0BAA0B,EAChC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAChC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,UAAU,EAAE,CAAC,CAAC,GAC/C,EACF,KAAC,MAAM,IACL,KAAK,EAAC,2BAA2B,EACjC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,EACjC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,UAAU,EAAE,CAAC,CAAC,GAChD,EACF,KAAC,MAAM,IACL,KAAK,EAAC,uBAAuB,EAC7B,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,EACjC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC,CAAC,GAC3C,IACE,IACE,EAGV,mBAAS,SAAS,EAAC,4CAA4C,aAC7D,kBAAQ,SAAS,EAAC,4DAA4D,aAC5E,KAAC,QAAQ,IAAC,SAAS,EAAC,uBAAuB,GAAG,EAC9C,aAAI,SAAS,EAAC,qCAAqC,wCAA6B,EAChF,eAAM,SAAS,EAAC,uBAAuB,kEAEhC,IACA,EACT,eAAK,SAAS,EAAC,0BAA0B,aACtC,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;gCAC7B,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;gCAC5C,MAAM,OAAO,GAAG,GAAG,CAAC,SAAS,IAAI,EAAE,CAAA;gCACnC,OAAO,CACL,eAAoB,SAAS,EAAC,KAAK,aACjC,cAAK,SAAS,EAAC,gDAAgD,YAC7D,0BACE,aAAI,SAAS,EAAC,qCAAqC,YAAE,GAAG,CAAC,KAAK,GAAM,EACpE,aAAG,SAAS,EAAC,uBAAuB,aAClC,eAAM,SAAS,EAAC,iCAAiC,YAAE,GAAG,CAAC,IAAI,GAAQ,EAClE,GAAG,CAAC,SAAS,KAAK,SAAS,IAAI,CAC9B,8BACG,GAAG,aACI,GAAG,EACX,gBAAM,SAAS,EAAC,iCAAiC,kBAAG,GAAG,CAAC,SAAS,IAAQ,IACxE,CACJ,EAAE,GAAG,aACH,GAAG,CAAC,IAAI,IACT,IACA,GACF,EAEN,eAAK,SAAS,EAAC,uCAAuC,aACpD,KAAC,MAAM,IACL,KAAK,EAAC,yBAAyB,EAC/B,KAAK,EAAE,QAAQ,CAAC,iBAAiB,IAAI,EAAE,EACvC,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,mBAAmB,EAAE,CAAC,IAAI,SAAS,CAAC,EAC/E,gBAAgB,EAAE,YAAY,OAAO,CAAC,iBAAiB,IAAI,aAAa,EAAE,GAC1E,EACF,KAAC,KAAK,IACJ,KAAK,EAAC,cAAc,EACpB,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,OAAO,EAC3C,KAAK,EAAE,QAAQ,CAAC,WAAW,IAAI,EAAE,EACjC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC,GAC5D,EACF,KAAC,MAAM,IACL,KAAK,EAAC,0BAA0B,EAChC,KAAK,EAAE,QAAQ,CAAC,iBAAiB,IAAI,EAAE,EACvC,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,mBAAmB,EAAE,CAAQ,CAAC,EACzE,gBAAgB,EAAE,YAAY,OAAO,CAAC,iBAAiB,IAAI,QAAQ,EAAE,GACrE,EACF,KAAC,WAAW,IACV,KAAK,EAAC,kBAAkB,EACxB,KAAK,EAAE,QAAQ,CAAC,eAAe,EAC/B,WAAW,EAAE,YAAY,OAAO,CAAC,eAAe,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EACvF,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAAC,EAChE,GAAG,EAAE,CAAC,EACN,GAAG,EAAE,CAAC,EACN,IAAI,EAAE,GAAG,GACT,IACE,EAEN,eAAK,SAAS,EAAC,2BAA2B,aACxC,KAAC,MAAM,IACL,KAAK,EAAC,sBAAsB,EAC5B,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,kBAAkB,EACtC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,oBAAoB,EAAE,CAAC,CAAC,GACnE,EACF,KAAC,MAAM,IACL,KAAK,EAAC,kBAAkB,EACxB,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,OAAO,EAC1C,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,qBAAqB,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,GAC9D,EACF,KAAC,MAAM,IACL,KAAK,EAAC,mBAAmB,EACzB,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,QAAQ,EAC3C,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,qBAAqB,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,GAC/D,IACE,KAlEE,GAAG,CAAC,IAAI,CAmEZ,CACP,CAAA;4BACH,CAAC,CAAC,EACD,IAAI,EAAE,WAAW,CAAC,MAAM,KAAK,CAAC,IAAI,CACjC,eAAK,SAAS,EAAC,2BAA2B,8CACX,+CAA8B,SACvD,CACP,IACG,IACE,EAEV,cAAK,SAAS,EAAC,kBAAkB,YAC/B,kBACE,OAAO,EAAE,UAAU,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAC,kJAAkJ,aAE3J,MAAM,CAAC,CAAC,CAAC,KAAC,OAAO,IAAC,SAAS,EAAC,sBAAsB,GAAG,CAAC,CAAC,CAAC,KAAC,IAAI,IAAC,SAAS,EAAC,SAAS,GAAG,yBAE9E,GACL,IACF,CACP,CAAA;AACH,CAAC;AAED,SAAS,UAAU,CAAgC,GAAM;IACvD,MAAM,GAAG,GAAwB,EAAE,CAAA;IACnC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACzC,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI;YAAE,SAAQ;QAC3C,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,SAAQ;QACtD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/C,MAAM,MAAM,GAAG,UAAU,CAAC,CAAwB,CAAC,CAAA;YACnD,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC;gBAAE,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAA;YACnD,SAAQ;QACV,CAAC;QACD,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAA;IACZ,CAAC;IACD,OAAO,GAAiB,CAAA;AAC1B,CAAC;AAED,SAAS,KAAK,CAAC,EACb,KAAK,EACL,KAAK,EACL,WAAW,EACX,QAAQ,EACR,IAAI,GAOL;IACC,OAAO,CACL,0BACE,gBAAO,SAAS,EAAC,8CAA8C,YAAE,KAAK,GAAS,EAC/E,gBACE,IAAI,EAAC,MAAM,EACX,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EACzC,SAAS,EAAC,6KAA6K,GACvL,EACD,IAAI,IAAI,YAAG,SAAS,EAAC,gCAAgC,YAAE,IAAI,GAAK,IAC7D,CACP,CAAA;AACH,CAAC;AAED,SAAS,WAAW,CAAC,EACnB,KAAK,EACL,KAAK,EACL,WAAW,EACX,QAAQ,EACR,GAAG,EACH,GAAG,EACH,IAAI,EACJ,IAAI,GAUL;IACC,OAAO,CACL,0BACE,gBAAO,SAAS,EAAC,8CAA8C,YAAE,KAAK,GAAS,EAC/E,gBACE,IAAI,EAAC,QAAQ,EACb,KAAK,EAAE,KAAK,IAAI,EAAE,EAClB,WAAW,EAAE,WAAW,EACxB,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE;oBACd,MAAM,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAA;oBAC1B,IAAI,GAAG,KAAK,EAAE;wBAAE,OAAO,QAAQ,CAAC,SAAS,CAAC,CAAA;oBAC1C,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;oBACrB,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;wBAAE,QAAQ,CAAC,CAAC,CAAC,CAAA;gBACrC,CAAC,EACD,SAAS,EAAC,6KAA6K,GACvL,EACD,IAAI,IAAI,YAAG,SAAS,EAAC,gCAAgC,YAAE,IAAI,GAAK,IAC7D,CACP,CAAA;AACH,CAAC;AAED,SAAS,MAAM,CAAC,EACd,KAAK,EACL,KAAK,EACL,OAAO,EACP,QAAQ,EACR,gBAAgB,EAChB,IAAI,GAQL;IACC,OAAO,CACL,0BACE,gBAAO,SAAS,EAAC,8CAA8C,YAAE,KAAK,GAAS,EAC/E,iBACE,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EACzC,SAAS,EAAC,iKAAiK,YAE1K,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACpB,iBAAkB,KAAK,EAAE,GAAG,YACzB,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,gBAAgB,IAAI,iBAAiB,CAAC,CAAC,CAAC,CAAC,GAAG,IADhD,GAAG,CAEP,CACV,CAAC,GACK,EACR,IAAI,IAAI,YAAG,SAAS,EAAC,gCAAgC,YAAE,IAAI,GAAK,IAC7D,CACP,CAAA;AACH,CAAC;AAED,SAAS,MAAM,CAAC,EACd,KAAK,EACL,OAAO,EACP,QAAQ,EACR,IAAI,GAML;IACC,OAAO,CACL,iBAAO,SAAS,EAAC,8DAA8D,aAC7E,gBACE,IAAI,EAAC,UAAU,EACf,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAC3C,SAAS,EAAC,mEAAmE,GAC7E,EACF,2BACG,KAAK,EACL,IAAI,IAAI,gBAAM,SAAS,EAAC,gCAAgC,wBAAI,IAAI,IAAQ,IACpE,IACD,CACT,CAAA;AACH,CAAC"}
|
|
@@ -324,16 +324,16 @@ function ScoreRing({ score }) {
|
|
|
324
324
|
return (_jsxs("div", { className: "relative inline-flex items-center justify-center", children: [_jsxs("svg", { width: "72", height: "72", className: "-rotate-90", children: [_jsx("circle", { cx: "36", cy: "36", r: radius, fill: "none", stroke: "var(--border)", strokeWidth: "5" }), _jsx("circle", { cx: "36", cy: "36", r: radius, fill: "none", stroke: color, strokeWidth: "5", strokeDasharray: circumference, strokeDashoffset: offset, strokeLinecap: "round", className: "transition-all duration-500" })] }), _jsx("span", { className: "absolute text-sm font-bold", style: { color }, children: score })] }));
|
|
325
325
|
}
|
|
326
326
|
function Section({ id, title, icon, expanded, onToggle, children, badge, }) {
|
|
327
|
-
return (_jsxs("div", { className: "border border-[var(--border)]
|
|
327
|
+
return (_jsxs("div", { className: "overflow-hidden rounded-lg border border-[var(--border)]", children: [_jsxs("button", { onClick: () => onToggle(id), className: "flex w-full items-center justify-between px-4 py-2.5 text-left text-sm font-medium text-[var(--foreground)] transition-colors hover:bg-[var(--muted)]", children: [_jsxs("span", { className: "flex items-center gap-2", children: [icon, title, badge] }), expanded ? (_jsx(ChevronUp, { className: "h-4 w-4 text-[var(--muted-foreground)]" })) : (_jsx(ChevronDown, { className: "h-4 w-4 text-[var(--muted-foreground)]" }))] }), expanded && _jsx("div", { className: "px-4 pt-1 pb-4", children: children })] }));
|
|
328
328
|
}
|
|
329
329
|
function ToggleSwitch({ label, description, checked, onChange, }) {
|
|
330
|
-
return (_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { className: "
|
|
330
|
+
return (_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("label", { className: "text-sm font-medium text-[var(--foreground)]", children: label }), _jsx("p", { className: "mt-0.5 text-xs text-[var(--muted-foreground)]", children: description })] }), _jsx("button", { type: "button", role: "switch", "aria-checked": checked, onClick: () => onChange(!checked), className: `relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full transition-colors ${checked ? 'bg-[var(--primary)]' : 'bg-[var(--muted)]'}`, children: _jsx("span", { className: `pointer-events-none mt-0.5 block h-5 w-5 rounded-full bg-white shadow-sm transition-transform ${checked ? 'translate-x-[22px]' : 'translate-x-0.5'}` }) })] }));
|
|
331
331
|
}
|
|
332
332
|
function InputField({ label, value, onChange, placeholder, type = 'text', charCount, charTarget, }) {
|
|
333
|
-
return (_jsxs("div", { children: [_jsx("label", { className: "block text-xs font-medium text-[var(--muted-foreground)]
|
|
333
|
+
return (_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-xs font-medium text-[var(--muted-foreground)]", children: label }), _jsx("input", { type: type, value: value, onChange: (e) => onChange(e.target.value), className: "w-full rounded-lg border border-[var(--border)] bg-[var(--background)] px-3 py-1.5 text-sm text-[var(--foreground)] placeholder:text-[var(--muted-foreground)] focus:ring-2 focus:ring-[var(--primary)] focus:outline-none", placeholder: placeholder }), charCount !== undefined && charTarget && (_jsxs("p", { className: "mt-1 text-xs text-[var(--muted-foreground)]", children: [charCount, " chars ", charTarget] }))] }));
|
|
334
334
|
}
|
|
335
335
|
function TextareaField({ label, value, onChange, placeholder, rows = 3, charCount, charTarget, }) {
|
|
336
|
-
return (_jsxs("div", { children: [_jsx("label", { className: "block text-xs font-medium text-[var(--muted-foreground)]
|
|
336
|
+
return (_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-xs font-medium text-[var(--muted-foreground)]", children: label }), _jsx("textarea", { value: value, onChange: (e) => onChange(e.target.value), rows: rows, className: "w-full resize-none rounded-lg border border-[var(--border)] bg-[var(--background)] px-3 py-1.5 text-sm text-[var(--foreground)] placeholder:text-[var(--muted-foreground)] focus:ring-2 focus:ring-[var(--primary)] focus:outline-none", placeholder: placeholder }), charCount !== undefined && charTarget && (_jsxs("p", { className: "mt-1 text-xs text-[var(--muted-foreground)]", children: [charCount, " chars ", charTarget] }))] }));
|
|
337
337
|
}
|
|
338
338
|
export function SEOPanel({ title, slug, content = '', seoData, onChange, siteUrl = 'https://example.com', }) {
|
|
339
339
|
const [expandedSections, setExpandedSections] = useState(['analysis']);
|
|
@@ -366,12 +366,12 @@ export function SEOPanel({ title, slug, content = '', seoData, onChange, siteUrl
|
|
|
366
366
|
: readability.fleschScore >= 30
|
|
367
367
|
? 'text-amber-500'
|
|
368
368
|
: 'text-red-500';
|
|
369
|
-
return (_jsxs("div", { className: "rounded-lg border border-[var(--border)] bg-[var(--card)]", children: [_jsxs("div", { className: "flex items-center justify-between
|
|
370
|
-
? 'text-amber-500
|
|
371
|
-
: 'text-green-500
|
|
369
|
+
return (_jsxs("div", { className: "rounded-lg border border-[var(--border)] bg-[var(--card)]", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-[var(--border)] px-4 py-3", children: [_jsxs("h3", { className: "flex items-center gap-2 text-sm font-semibold text-[var(--foreground)]", children: [_jsx(Search, { className: "h-4 w-4" }), "SEO"] }), _jsxs("button", { onClick: () => update({ metaTitle: title, ogTitle: '', ogDescription: '' }), className: "flex items-center gap-1 text-xs text-[var(--primary)] hover:opacity-80", children: [_jsx(RefreshCw, { className: "h-3 w-3" }), "Reset"] })] }), _jsxs("div", { className: "flex items-center gap-4 border-b border-[var(--border)] px-4 py-3", children: [_jsx(ScoreRing, { score: score }), _jsxs("div", { className: "space-y-0.5 text-xs text-[var(--muted-foreground)]", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(CheckCircle2, { className: "h-3 w-3 text-green-500" }), " ", goodCount, " passed"] }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(AlertCircle, { className: "h-3 w-3 text-amber-500" }), " ", okCount, " improvements"] }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(XCircle, { className: "h-3 w-3 text-red-500" }), " ", badCount, " issues"] })] })] }), _jsxs("div", { className: "space-y-2 p-3", children: [_jsx(Section, { id: "analysis", title: "SEO Analysis", icon: _jsx(BarChart3, { className: "h-4 w-4" }), expanded: expandedSections.includes('analysis'), onToggle: toggleSection, badge: _jsxs("span", { className: "ml-1 inline-flex items-center rounded-full bg-[var(--muted)] px-2 py-0.5 text-[10px] font-medium text-[var(--muted-foreground)]", children: [goodCount, "/", checks.length] }), children: _jsx("div", { className: "space-y-1.5", children: checks.map((c) => (_jsxs("div", { className: "flex items-start gap-2 py-1", children: [_jsx(StatusDot, { status: c.status }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-sm leading-snug text-[var(--foreground)]", children: c.label }), _jsx("p", { className: "text-xs text-[var(--muted-foreground)]", children: c.detail })] })] }, c.id))) }) }), _jsxs(Section, { id: "readability", title: "Readability", icon: _jsx(BookOpen, { className: "h-4 w-4" }), expanded: expandedSections.includes('readability'), onToggle: toggleSection, children: [_jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: "rounded-lg bg-[var(--muted)] p-2.5", children: [_jsx("p", { className: "text-xs text-[var(--muted-foreground)]", children: "Flesch Score" }), _jsx("p", { className: `text-lg font-bold ${fleschColor}`, children: readability.fleschScore }), _jsx("p", { className: `text-[10px] ${fleschColor}`, children: fleschLabel })] }), _jsxs("div", { className: "rounded-lg bg-[var(--muted)] p-2.5", children: [_jsx("p", { className: "text-xs text-[var(--muted-foreground)]", children: "Word Count" }), _jsx("p", { className: "text-lg font-bold text-[var(--foreground)]", children: readability.wordCount }), _jsx("p", { className: "text-[10px] text-[var(--muted-foreground)]", children: "words" })] }), _jsxs("div", { className: "rounded-lg bg-[var(--muted)] p-2.5", children: [_jsx("p", { className: "text-xs text-[var(--muted-foreground)]", children: "Avg. Sentence" }), _jsx("p", { className: "text-lg font-bold text-[var(--foreground)]", children: readability.avgSentenceLength.toFixed(1) }), _jsx("p", { className: "text-[10px] text-[var(--muted-foreground)]", children: "words/sentence" })] }), _jsxs("div", { className: "rounded-lg bg-[var(--muted)] p-2.5", children: [_jsx("p", { className: "text-xs text-[var(--muted-foreground)]", children: "Reading Time" }), _jsx("p", { className: "text-lg font-bold text-[var(--foreground)]", children: readability.readingTime }), _jsx("p", { className: "text-[10px] text-[var(--muted-foreground)]", children: "min" })] })] }), _jsxs("div", { className: "mt-3 flex items-center gap-2 text-xs text-[var(--muted-foreground)]", children: [_jsx("span", { children: "Passive voice est.:" }), _jsxs("span", { className: readability.passiveEstimate > 15
|
|
370
|
+
? 'font-medium text-amber-500'
|
|
371
|
+
: 'font-medium text-green-500', children: [readability.passiveEstimate, "%"] })] })] }), _jsx(Section, { id: "basic", title: "Basic SEO", icon: _jsx(Target, { className: "h-4 w-4" }), expanded: expandedSections.includes('basic'), onToggle: toggleSection, children: _jsxs("div", { className: "space-y-3", children: [_jsx(InputField, { label: "Meta Title", value: metaTitle, onChange: (v) => update({ metaTitle: v }), placeholder: title || 'Enter meta title', charCount: metaTitle.length, charTarget: "(ideal: 30-60)" }), _jsx(TextareaField, { label: "Meta Description", value: metaDesc, onChange: (v) => update({ metaDescription: v }), placeholder: "Brief description for search engines", charCount: metaDesc.length, charTarget: "(ideal: 120-160)" }), _jsx(InputField, { label: "Focus Keyphrase", value: seoData.focusKeyphrase ?? '', onChange: (v) => update({ focusKeyphrase: v }), placeholder: "Primary keyword or phrase" }), _jsx(InputField, { label: "Canonical URL", value: seoData.canonical ?? '', onChange: (v) => update({ canonical: v }), placeholder: "https://example.com/canonical-url", type: "url" })] }) }), _jsx(Section, { id: "robots", title: "Robots Meta", icon: _jsx(EyeOff, { className: "h-4 w-4" }), expanded: expandedSections.includes('robots'), onToggle: toggleSection, children: _jsx("div", { className: "space-y-4", children: _jsxs("div", { children: [_jsx("label", { id: "robots-policy-label", className: "text-muted-foreground mb-1 block text-xs font-medium", children: "Robots Policy" }), _jsxs(Select.Root, { value: robotsPolicy, onValueChange: (value) => {
|
|
372
372
|
const policy = value;
|
|
373
373
|
update({ robotsPolicy: policy, ...robotsPolicyToBooleans(policy) });
|
|
374
|
-
}, children: [_jsxs(Select.Trigger, { "aria-labelledby": "robots-policy-label", className: "flex w-full items-center justify-between rounded-lg border
|
|
375
|
-
'Add a meta description to see how this page will appear in search results.' })] }) }), _jsx(Section, { id: "social", title: "Social Media", icon: _jsx(Share2, { className: "h-4 w-4" }), expanded: expandedSections.includes('social'), onToggle: toggleSection, children: _jsxs("div", { className: "space-y-3", children: [_jsx(InputField, { label: "OG Title", value: seoData.ogTitle ?? '', onChange: (v) => update({ ogTitle: v }), placeholder: metaTitle || title || 'Leave blank to use meta title' }), _jsx(TextareaField, { label: "OG Description", value: seoData.ogDescription ?? '', onChange: (v) => update({ ogDescription: v }), rows: 2, placeholder: metaDesc || 'Leave blank to use meta description' }), _jsx(InputField, { label: "OG Image URL", value: seoData.ogImage ?? '', onChange: (v) => update({ ogImage: v }), placeholder: "https://example.com/og-image.jpg", type: "url" }), _jsxs("div", { className: "border-t border-[var(--border)] pt-3
|
|
374
|
+
}, children: [_jsxs(Select.Trigger, { "aria-labelledby": "robots-policy-label", className: "border-border bg-background text-foreground focus:ring-primary flex w-full items-center justify-between rounded-lg border px-3 py-1.5 text-sm focus:ring-2 focus:outline-none", children: [_jsx(Select.Value, {}), _jsx(Select.Icon, { children: _jsx(ChevronDown, { className: "text-muted-foreground h-4 w-4" }) })] }), _jsx(Select.Portal, { children: _jsx(Select.Content, { className: "border-border bg-card z-50 overflow-hidden rounded-lg border shadow-md", children: _jsx(Select.Viewport, { className: "p-1", children: ROBOTS_POLICY_OPTIONS.map((option) => (_jsxs(Select.Item, { value: option.value, className: "text-foreground hover:bg-muted focus:bg-muted relative flex cursor-pointer items-center rounded-md py-1.5 pr-3 pl-8 text-sm outline-none select-none", children: [_jsx(Select.ItemIndicator, { className: "absolute left-2 inline-flex items-center", children: _jsx(Check, { className: "h-4 w-4" }) }), _jsx(Select.ItemText, { children: option.label })] }, option.value))) }) }) })] }), _jsx("p", { className: "text-muted-foreground mt-1 text-xs", children: "Use inheritance for most pages. Override only when a page needs different index/follow behavior." })] }) }) }), _jsx(Section, { id: "preview", title: "Search Preview", icon: _jsx(Globe, { className: "h-4 w-4" }), expanded: expandedSections.includes('preview'), onToggle: toggleSection, children: _jsxs("div", { className: "rounded-lg border border-[var(--border)] bg-[var(--background)] p-3", children: [_jsx("div", { className: "line-clamp-1 cursor-pointer text-sm text-blue-600 hover:underline", children: metaTitle || title || 'Page Title' }), _jsxs("div", { className: "mt-1 truncate text-xs text-green-700", children: [siteUrl, "/", slug] }), _jsx("div", { className: "mt-1 line-clamp-2 text-sm text-[var(--muted-foreground)]", children: metaDesc ||
|
|
375
|
+
'Add a meta description to see how this page will appear in search results.' })] }) }), _jsx(Section, { id: "social", title: "Social Media", icon: _jsx(Share2, { className: "h-4 w-4" }), expanded: expandedSections.includes('social'), onToggle: toggleSection, children: _jsxs("div", { className: "space-y-3", children: [_jsx(InputField, { label: "OG Title", value: seoData.ogTitle ?? '', onChange: (v) => update({ ogTitle: v }), placeholder: metaTitle || title || 'Leave blank to use meta title' }), _jsx(TextareaField, { label: "OG Description", value: seoData.ogDescription ?? '', onChange: (v) => update({ ogDescription: v }), rows: 2, placeholder: metaDesc || 'Leave blank to use meta description' }), _jsx(InputField, { label: "OG Image URL", value: seoData.ogImage ?? '', onChange: (v) => update({ ogImage: v }), placeholder: "https://example.com/og-image.jpg", type: "url" }), _jsxs("div", { className: "mt-3 border-t border-[var(--border)] pt-3", children: [_jsx("p", { className: "mb-2 text-xs font-medium text-[var(--muted-foreground)]", children: "Twitter / X Overrides" }), _jsxs("div", { className: "space-y-3", children: [_jsx(InputField, { label: "Twitter Title", value: seoData.twitterTitle ?? '', onChange: (v) => update({ twitterTitle: v }), placeholder: "Leave blank to use OG title" }), _jsx(TextareaField, { label: "Twitter Description", value: seoData.twitterDescription ?? '', onChange: (v) => update({ twitterDescription: v }), rows: 2, placeholder: "Leave blank to use OG description" }), _jsx(InputField, { label: "Twitter Image URL", value: seoData.twitterImage ?? '', onChange: (v) => update({ twitterImage: v }), placeholder: "Leave blank to use OG image", type: "url" })] })] }), _jsxs("div", { className: "mt-3", children: [_jsx("p", { className: "mb-2 text-xs font-medium text-[var(--muted-foreground)]", children: "Social Preview" }), _jsxs("div", { className: "overflow-hidden rounded-lg border border-[var(--border)] bg-[var(--muted)]", children: [seoData.ogImage ? (_jsx("div", { className: "flex aspect-video items-center justify-center overflow-hidden bg-[var(--muted)]", children: _jsx("img", { src: seoData.ogImage, alt: "OG preview", className: "h-full w-full object-cover" }) })) : (_jsx("div", { className: "flex aspect-video items-center justify-center bg-[var(--muted)] text-sm text-[var(--muted-foreground)]", children: "No OG image set" })), _jsxs("div", { className: "p-3", children: [_jsx("div", { className: "line-clamp-1 text-sm font-medium text-[var(--foreground)]", children: displayTitle }), _jsx("div", { className: "mt-1 line-clamp-2 text-xs text-[var(--muted-foreground)]", children: displayDesc.slice(0, 100) }), _jsx("div", { className: "mt-1 truncate text-xs text-[var(--muted-foreground)]", children: siteUrl })] })] })] })] }) }), _jsx(Section, { id: "advanced", title: "Advanced", icon: _jsx(Settings2, { className: "h-4 w-4" }), expanded: expandedSections.includes('advanced'), onToggle: toggleSection, children: _jsxs("div", { className: "space-y-4", children: [_jsx(ToggleSwitch, { label: "Cornerstone Content", description: "Mark as cornerstone \u2014 your most important, comprehensive articles", checked: seoData.isCornerstone ?? false, onChange: (v) => update({ isCornerstone: v }) }), seoData.isCornerstone && (_jsxs("div", { className: "flex items-center gap-1.5 rounded-md bg-amber-50 px-2.5 py-1.5 text-xs text-amber-600", children: [_jsx(Star, { className: "h-3.5 w-3.5" }), "Cornerstone content is held to stricter SEO standards"] })), _jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-xs font-medium text-[var(--muted-foreground)]", children: "Schema Type" }), _jsx("select", { value: seoData.schemaType ?? 'Article', onChange: (e) => update({ schemaType: e.target.value }), className: "w-full rounded-lg border border-[var(--border)] bg-[var(--background)] px-3 py-1.5 text-sm text-[var(--foreground)] focus:ring-2 focus:ring-[var(--primary)] focus:outline-none", children: SCHEMA_TYPES.map((t) => (_jsx("option", { value: t, children: t }, t))) })] })] }) })] })] }));
|
|
376
376
|
}
|
|
377
377
|
//# sourceMappingURL=SEOPanel.js.map
|