@actuate-media/cms-admin 0.10.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.js +1 -1
- package/dist/components/SharePreviewLinkDialog.js +1 -1
- 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 +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -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.js +7 -7
- 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.js +3 -3
- 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 +4 -4
- package/src/components/SharePreviewLinkDialog.tsx +1 -1
- 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 +28 -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 +9 -9
- 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
|
@@ -11,7 +11,7 @@ export function ResetPassword({ onNavigate, token }) {
|
|
|
11
11
|
const [success, setSuccess] = useState(false);
|
|
12
12
|
const [error, setError] = useState('');
|
|
13
13
|
if (!token) {
|
|
14
|
-
return (_jsx("div", { className: "min-h-screen
|
|
14
|
+
return (_jsx("div", { className: "flex min-h-screen items-center justify-center bg-gray-50 px-4", children: _jsxs("div", { className: "w-full max-w-md", children: [_jsxs("div", { className: "mb-8 text-center", children: [_jsx("div", { className: "mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-xl bg-red-100", children: _jsx(XCircle, { className: "h-7 w-7 text-red-600" }) }), _jsx("h1", { className: "text-2xl font-bold text-gray-900", children: "Invalid Reset Link" }), _jsx("p", { className: "mt-2 text-gray-600", children: "This password reset link is invalid or has expired." })] }), _jsxs("div", { className: "space-y-4 rounded-xl border border-gray-200 bg-white p-6 text-center shadow-sm", children: [_jsx("button", { type: "button", onClick: () => onNavigate('/forgot-password'), className: "w-full rounded-lg bg-blue-600 py-2.5 font-medium text-white transition-colors hover:bg-blue-700", children: "Request New Reset Link" }), _jsx("button", { type: "button", onClick: () => onNavigate('/login'), className: "w-full text-sm text-gray-600 transition-colors hover:text-gray-800", children: "Back to Sign In" })] })] }) }));
|
|
15
15
|
}
|
|
16
16
|
const passwordsMatch = password === confirmPassword;
|
|
17
17
|
const meetsLength = password.length >= 8;
|
|
@@ -41,6 +41,6 @@ export function ResetPassword({ onNavigate, token }) {
|
|
|
41
41
|
setSubmitting(false);
|
|
42
42
|
}
|
|
43
43
|
};
|
|
44
|
-
return (_jsx("div", { className: "min-h-screen
|
|
44
|
+
return (_jsx("div", { className: "flex min-h-screen items-center justify-center bg-gray-50 px-4", children: _jsxs("div", { className: "w-full max-w-md", children: [_jsxs("div", { className: "mb-8 text-center", children: [_jsx("div", { className: "mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-xl bg-blue-600", children: _jsx(Shield, { className: "h-7 w-7 text-white" }) }), _jsx("h1", { className: "text-2xl font-bold text-gray-900", children: success ? 'Password Reset' : 'Choose New Password' }), _jsx("p", { className: "mt-2 text-gray-600", children: success ? 'Your password has been updated' : 'Enter your new password below' })] }), _jsx("div", { className: "space-y-5 rounded-xl border border-gray-200 bg-white p-6 shadow-sm", children: success ? (_jsxs("div", { className: "space-y-4 text-center", children: [_jsx("div", { className: "mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100", children: _jsx(CheckCircle2, { className: "h-6 w-6 text-green-600" }) }), _jsx("p", { className: "text-sm text-gray-600", children: "Your password has been successfully reset. All existing sessions have been revoked for security." }), _jsx("button", { type: "button", onClick: () => onNavigate('/login'), className: "w-full rounded-lg bg-blue-600 py-2.5 font-medium text-white transition-colors hover:bg-blue-700", children: "Sign In with New Password" })] })) : (_jsxs("form", { onSubmit: handleSubmit, className: "space-y-5", children: [error && (_jsxs("div", { className: "flex items-start gap-3 rounded-lg border border-red-200 bg-red-50 p-3", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-5 w-5 shrink-0 text-red-600" }), _jsx("p", { className: "text-sm text-red-800", children: error })] })), _jsxs("div", { children: [_jsx("label", { htmlFor: "reset-password", className: "mb-1.5 block text-sm font-medium text-gray-700", children: "New Password" }), _jsxs("div", { className: "relative", children: [_jsx("input", { id: "reset-password", type: showPassword ? 'text' : 'password', value: password, onChange: (e) => setPassword(e.target.value), placeholder: "Enter new password", className: "w-full rounded-lg border border-gray-300 px-3 py-2.5 pr-10 text-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-500 focus:outline-none", required: true, autoFocus: true, autoComplete: "new-password", minLength: 8 }), _jsx("button", { type: "button", onClick: () => setShowPassword(!showPassword), className: "absolute top-1/2 right-3 -translate-y-1/2 text-gray-400 hover:text-gray-600", tabIndex: -1, children: showPassword ? _jsx(EyeOff, { className: "h-4 w-4" }) : _jsx(Eye, { className: "h-4 w-4" }) })] }), password && !meetsLength && (_jsx("p", { className: "mt-1 text-xs text-red-600", children: "Password must be at least 8 characters" }))] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "reset-confirm", className: "mb-1.5 block text-sm font-medium text-gray-700", children: "Confirm Password" }), _jsx("input", { id: "reset-confirm", type: showPassword ? 'text' : 'password', value: confirmPassword, onChange: (e) => setConfirmPassword(e.target.value), placeholder: "Confirm new password", className: "w-full rounded-lg border border-gray-300 px-3 py-2.5 text-sm focus:border-blue-500 focus:ring-2 focus:ring-blue-500 focus:outline-none", required: true, autoComplete: "new-password" }), confirmPassword && !passwordsMatch && (_jsx("p", { className: "mt-1 text-xs text-red-600", children: "Passwords do not match" }))] }), _jsx("button", { type: "submit", disabled: !canSubmit, className: "flex w-full items-center justify-center gap-2 rounded-lg bg-blue-600 py-2.5 font-medium text-white transition-colors hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-50", children: submitting ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), "Resetting..."] })) : ('Reset Password') })] })) })] }) }));
|
|
45
45
|
}
|
|
46
46
|
//# sourceMappingURL=ResetPassword.js.map
|
package/dist/views/SEO.js
CHANGED
|
@@ -125,19 +125,19 @@ export function SEO({ onNavigate, initialTab = 'pages' }) {
|
|
|
125
125
|
}
|
|
126
126
|
function scoreIcon(score) {
|
|
127
127
|
if (score >= 80)
|
|
128
|
-
return _jsx(CheckCircle2, { className: "
|
|
128
|
+
return _jsx(CheckCircle2, { className: "h-4 w-4 text-green-600" });
|
|
129
129
|
if (score >= 50)
|
|
130
|
-
return _jsx(AlertTriangle, { className: "
|
|
131
|
-
return _jsx(XCircle, { className: "
|
|
130
|
+
return _jsx(AlertTriangle, { className: "h-4 w-4 text-yellow-600" });
|
|
131
|
+
return _jsx(XCircle, { className: "h-4 w-4 text-red-600" });
|
|
132
132
|
}
|
|
133
133
|
const tabClass = 'px-4 py-2 text-sm font-medium text-gray-600 transition-colors hover:text-gray-900 data-[state=active]:border-b-2 data-[state=active]:border-blue-600 data-[state=active]:text-blue-600 shrink-0';
|
|
134
134
|
const isLoading = seoLoading || redirectsLoading || linkHealthLoading;
|
|
135
135
|
if (isLoading) {
|
|
136
|
-
return (_jsx("div", { className: "p-3 pr-6 sm:p-4 sm:pr-8
|
|
136
|
+
return (_jsx("div", { className: "flex h-64 items-center justify-center p-3 pr-6 sm:p-4 sm:pr-8", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-blue-600" }) }));
|
|
137
137
|
}
|
|
138
|
-
return (_jsxs("div", { className: "p-3 pr-6 sm:p-4 sm:pr-8
|
|
138
|
+
return (_jsxs("div", { className: "flex h-full flex-col p-3 pr-6 sm:p-4 sm:pr-8", children: [(seoError || redirectsError) && (_jsxs("div", { className: "mb-4 flex items-center gap-3 rounded-lg border border-red-200 bg-red-50 p-3", children: [_jsx(AlertTriangle, { className: "h-5 w-5 shrink-0 text-red-600" }), _jsx("span", { className: "flex-1 text-sm text-red-800", children: seoError || redirectsError }), _jsx("button", { onClick: () => {
|
|
139
139
|
seoRefetch();
|
|
140
140
|
redirectsRefetch();
|
|
141
|
-
}, className: "px-3 py-1 text-sm text-red-700 border border-red-300 rounded-lg hover:bg-red-100 transition-colors", children: "Retry" })] })), _jsxs("div", { className: "flex flex-col sm:flex-row sm:items-center justify-between mb-4 gap-3", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-xl sm:text-2xl font-semibold text-gray-900 mb-1", children: "SEO & Redirects" }), _jsx("p", { className: "text-sm text-gray-600", children: "Search optimization, redirects, canonicalization, and link health" })] }), _jsxs("button", { onClick: handleScan, disabled: scanning, className: "flex items-center justify-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors text-sm disabled:opacity-60", children: [_jsx(RefreshCw, { className: `w-4 h-4 ${scanning ? 'animate-spin' : ''}` }), scanning ? 'Scanning...' : 'Run SEO Scan'] })] }), _jsxs(Tabs.Root, { value: activeTab, onValueChange: handleTabChange, children: [_jsxs(Tabs.List, { className: "mb-4 flex gap-1 border-b border-gray-200 overflow-x-auto", children: [_jsx(Tabs.Trigger, { value: "pages", className: tabClass, children: _jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx(BarChart3, { className: "w-4 h-4" }), "Pages"] }) }), _jsx(Tabs.Trigger, { value: "redirects", className: tabClass, children: _jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx(ArrowRightLeft, { className: "w-4 h-4" }), "Redirects"] }) }), _jsx(Tabs.Trigger, { value: "canonicals", className: tabClass, children: _jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx(Copy, { className: "w-4 h-4" }), "Canonicalization"] }) }), _jsx(Tabs.Trigger, { value: "links", className: tabClass, children: _jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx(Link2, { className: "w-4 h-4" }), "Link Health"] }) })] }), _jsxs(Tabs.Content, { value: "pages", className: "flex flex-col flex-1 min-h-0", children: [_jsxs("div", { className: "grid grid-cols-2 lg:grid-cols-4 gap-3 mb-4", children: [_jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx(BarChart3, { className: "w-4 h-4 text-blue-600" }), _jsx("span", { className: "text-xs font-medium text-gray-600", children: "Avg SEO Score" })] }), _jsx("div", { className: "text-2xl font-semibold text-gray-900", children: avgScore }), _jsx("div", { className: `text-xs mt-1 ${avgScore >= 80 ? 'text-green-600' : avgScore >= 50 ? 'text-yellow-600' : 'text-red-600'}`, children: avgScore >= 80 ? 'Good' : avgScore >= 50 ? 'Needs improvement' : 'Critical' })] }), _jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx(AlertTriangle, { className: "w-4 h-4 text-yellow-600" }), _jsx("span", { className: "text-xs font-medium text-gray-600", children: "Total Issues" })] }), _jsx("div", { className: "text-2xl font-semibold text-gray-900", children: totalIssues }), _jsxs("div", { className: "text-xs text-gray-500 mt-1", children: ["Across ", seoPages.filter((p) => p.issues > 0).length, " pages"] })] }), _jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx(FileCode2, { className: "w-4 h-4 text-purple-600" }), _jsx("span", { className: "text-xs font-medium text-gray-600", children: "Missing Meta" })] }), _jsx("div", { className: "text-2xl font-semibold text-gray-900", children: missingMeta }), _jsx("div", { className: "text-xs text-gray-500 mt-1", children: "Title or description" })] }), _jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx(Globe, { className: "w-4 h-4 text-green-600" }), _jsx("span", { className: "text-xs font-medium text-gray-600", children: "Missing Schema" })] }), _jsx("div", { className: "text-2xl font-semibold text-gray-900", children: missingSchema }), _jsx("div", { className: "text-xs text-gray-500 mt-1", children: "No Schema.org markup" })] })] }), _jsx("div", { className: "bg-linear-to-r from-indigo-50 to-purple-50 border border-indigo-200 rounded-lg p-4 mb-4", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx(Bot, { className: "w-5 h-5 text-indigo-600 mt-0.5 shrink-0" }), _jsxs("div", { className: "flex-1", children: [_jsx("h3", { className: "text-sm font-semibold text-indigo-900 mb-1", children: "AI SEO Recommendations" }), _jsxs("p", { className: "text-sm text-indigo-700 mb-2", children: [missingMeta, " pages missing meta descriptions. ", missingCanonical.length, " pages without canonical URLs. AI can auto-generate these from page content."] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { onClick: () => onNavigate?.('/settings'), className: "px-3 py-1.5 text-xs bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors", children: "Configure AI" }), _jsx("button", { className: "px-3 py-1.5 text-xs border border-indigo-300 text-indigo-700 rounded-lg hover:bg-indigo-50 transition-colors", children: "Auto-fix All" })] })] })] }) }), _jsx("div", { className: "bg-white rounded-lg border border-gray-200 mb-4", children: _jsxs("div", { className: "p-3 flex flex-col sm:flex-row gap-3", children: [_jsxs("div", { className: "relative flex-1", children: [_jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" }), _jsx("input", { type: "text", placeholder: "Search pages by URL or title...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), className: "w-full pl-9 pr-3 py-2 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" })] }), _jsxs("select", { value: filterScore, onChange: (e) => setFilterScore(e.target.value), className: "px-3 py-2 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500", children: [_jsx("option", { value: "all", children: "All Scores" }), _jsx("option", { value: "good", children: "Good (80+)" }), _jsx("option", { value: "warning", children: "Needs Work (50-79)" }), _jsx("option", { value: "critical", children: "Critical (<50)" })] })] }) }), _jsx("div", { className: "bg-white rounded-lg border border-gray-200 flex-1 overflow-hidden", children: _jsx("div", { className: "overflow-x-auto h-full", children: _jsxs("table", { className: "w-full", children: [_jsx("thead", { className: "bg-gray-50 border-b border-gray-200 sticky top-0", children: _jsxs("tr", { children: [_jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Page" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Score" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Readability" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Schema" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Meta" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Issues" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Actions" })] }) }), _jsx("tbody", { className: "divide-y divide-gray-200", children: filtered.map((page) => (_jsxs("tr", { className: "hover:bg-gray-50 transition-colors", children: [_jsxs("td", { className: "px-4 py-3", children: [_jsx("div", { className: "text-sm font-medium text-gray-900", children: page.title }), _jsx("div", { className: "text-xs text-gray-500", children: page.url })] }), _jsx("td", { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [scoreIcon(page.score), _jsx("span", { className: `px-2 py-0.5 rounded-full text-xs font-medium ${scoreBadge(page.score)}`, children: page.score })] }) }), _jsx("td", { className: "px-4 py-3", children: page.readability > 0 ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(BookOpen, { className: "w-3.5 h-3.5 text-gray-400" }), _jsx("span", { className: `text-sm ${page.readability >= 80 ? 'text-green-700' : page.readability >= 60 ? 'text-yellow-700' : 'text-red-700'}`, children: page.readability })] })) : (_jsx("span", { className: "text-xs text-gray-400", children: "\u2014" })) }), _jsx("td", { className: "px-4 py-3", children: page.schemaType ? (_jsx("span", { className: "px-2 py-0.5 rounded-full text-xs font-medium bg-purple-100 text-purple-800", children: page.schemaType })) : (_jsx("span", { className: "px-2 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800", children: "Missing" })) }), _jsx("td", { className: "px-4 py-3", children: _jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: `text-xs ${page.metaTitle ? 'text-green-700' : 'text-red-600'}`, children: page.metaTitle ? '✓ Title' : '✗ Title' }), _jsx("span", { className: `text-xs ${page.metaDescription ? 'text-green-700' : 'text-red-600'}`, children: page.metaDescription ? '✓ Desc' : '✗ Desc' })] }) }), _jsx("td", { className: "px-4 py-3", children: page.issues > 0 ? (_jsx("span", { className: "px-2 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800", children: page.issues })) : (_jsx("span", { className: "px-2 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800", children: "\u2713" })) }), _jsx("td", { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { onClick: () => onNavigate?.(`/pages/${page.id}`), className: "p-1.5 hover:bg-gray-100 rounded transition-colors", title: "Edit", children: _jsx(ArrowUpRight, { className: "w-4 h-4 text-gray-600" }) }), _jsx("button", { className: "p-1.5 hover:bg-gray-100 rounded transition-colors", title: "AI analyze", children: _jsx(Bot, { className: "w-4 h-4 text-indigo-600" }) })] }) })] }, page.id))) })] }) }) })] }), _jsxs(Tabs.Content, { value: "redirects", className: "flex flex-col flex-1 min-h-0", children: [_jsxs("div", { className: "grid grid-cols-2 lg:grid-cols-4 gap-3 mb-4", children: [_jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [_jsx("div", { className: "text-xs text-gray-600 mb-1", children: "Total Redirects" }), _jsx("div", { className: "text-2xl font-semibold text-gray-900", children: redirects.length })] }), _jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [_jsx("div", { className: "text-xs text-gray-600 mb-1", children: "Active" }), _jsx("div", { className: "text-2xl font-semibold text-green-600", children: redirects.filter((r) => r.active).length })] }), _jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [_jsx("div", { className: "text-xs text-gray-600 mb-1", children: "Total Hits" }), _jsx("div", { className: "text-2xl font-semibold text-blue-600", children: redirects.reduce((s, r) => s + r.hits, 0).toLocaleString() })] }), _jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [_jsx("div", { className: "text-xs text-gray-600 mb-1", children: "301 Permanent" }), _jsx("div", { className: "text-2xl font-semibold text-gray-900", children: redirects.filter((r) => r.type === '301').length })] })] }), _jsxs("div", { className: "flex items-center gap-3 mb-4", children: [_jsxs("div", { className: "relative flex-1 max-w-md", children: [_jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" }), _jsx("input", { type: "text", placeholder: "Search redirects...", value: redirectSearch, onChange: (e) => setRedirectSearch(e.target.value), className: "w-full pl-9 pr-3 py-2 text-sm border border-gray-300 rounded-lg bg-white focus:outline-none focus:ring-2 focus:ring-blue-500" })] }), _jsxs("button", { onClick: () => setShowAddRedirect(true), className: "flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors text-sm shrink-0", children: [_jsx(Plus, { className: "w-4 h-4" }), "Add Redirect"] })] }), _jsx("div", { className: "bg-white rounded-lg border border-gray-200 flex-1 overflow-hidden", children: _jsx("div", { className: "overflow-x-auto h-full", children: _jsxs("table", { className: "w-full", children: [_jsx("thead", { className: "bg-gray-50 border-b border-gray-200 sticky top-0", children: _jsxs("tr", { children: [_jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Source" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Destination" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Type" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Hits" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Status" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Actions" })] }) }), _jsx("tbody", { className: "divide-y divide-gray-200", children: filteredRedirects.map((r) => (_jsxs("tr", { className: "hover:bg-gray-50 transition-colors", children: [_jsx("td", { className: "px-4 py-3", children: _jsx("code", { className: "rounded bg-gray-100 px-2 py-1 text-xs text-gray-900", children: r.from }) }), _jsx("td", { className: "px-4 py-3", children: _jsx("code", { className: "rounded bg-gray-100 px-2 py-1 text-xs text-gray-900", children: r.to }) }), _jsx("td", { className: "px-4 py-3", children: _jsx("span", { className: `px-2 py-0.5 rounded-full text-xs font-medium ${r.type === '301' ? 'bg-blue-100 text-blue-800' : 'bg-purple-100 text-purple-800'}`, children: r.type }) }), _jsx("td", { className: "px-4 py-3 text-sm text-gray-600", children: r.hits.toLocaleString() }), _jsx("td", { className: "px-4 py-3", children: _jsx("span", { className: `px-2 py-0.5 rounded-full text-xs font-medium ${r.active ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'}`, children: r.active ? 'Active' : 'Inactive' }) }), _jsx("td", { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { className: "p-1.5 hover:bg-gray-100 rounded transition-colors", children: _jsx(Pencil, { className: "w-4 h-4 text-gray-600" }) }), _jsx("button", { onClick: () => handleDeleteRedirect(r.id), className: "p-1.5 hover:bg-gray-100 rounded transition-colors", children: _jsx(Trash2, { className: "w-4 h-4 text-red-600" }) })] }) })] }, r.id))) })] }) }) }), _jsx(Dialog.Root, { open: showAddRedirect, onOpenChange: setShowAddRedirect, children: _jsxs(Dialog.Portal, { children: [_jsx(Dialog.Overlay, { className: "fixed inset-0 z-50 bg-black/50" }), _jsxs(Dialog.Content, { className: "fixed left-1/2 top-1/2 z-50 w-full max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg bg-white p-6 shadow-lg", children: [_jsx(Dialog.Title, { className: "mb-4 text-lg font-semibold text-gray-900", children: "Add Redirect" }), _jsxs("form", { onSubmit: handleAddRedirect, className: "space-y-4", children: [_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Source URL" }), _jsx("input", { type: "text", value: newRedirect.source, onChange: (e) => setNewRedirect({ ...newRedirect, source: e.target.value }), placeholder: "/old-page", className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500", required: true })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Destination URL" }), _jsx("input", { type: "text", value: newRedirect.destination, onChange: (e) => setNewRedirect({ ...newRedirect, destination: e.target.value }), placeholder: "/new-page", className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500", required: true })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Type" }), _jsxs("select", { value: newRedirect.type, onChange: (e) => setNewRedirect({ ...newRedirect, type: e.target.value }), className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500", children: [_jsx("option", { value: "301", children: "301 (Permanent)" }), _jsx("option", { value: "302", children: "302 (Temporary)" })] })] }), _jsxs("div", { className: "flex justify-end gap-3 pt-4", children: [_jsx(Dialog.Close, { asChild: true, children: _jsx("button", { type: "button", className: "rounded-lg border border-gray-300 px-4 py-2 text-sm hover:bg-gray-50", children: "Cancel" }) }), _jsx("button", { type: "submit", className: "rounded-lg bg-blue-600 px-4 py-2 text-sm text-white hover:bg-blue-700", children: "Add Redirect" })] })] })] })] }) })] }), _jsxs(Tabs.Content, { value: "canonicals", className: "flex flex-col flex-1 min-h-0 space-y-4", children: [_jsxs("div", { className: "grid grid-cols-2 lg:grid-cols-4 gap-3", children: [_jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx(ShieldCheck, { className: "w-4 h-4 text-green-600" }), _jsx("span", { className: "text-xs font-medium text-gray-600", children: "With Canonical" })] }), _jsx("div", { className: "text-2xl font-semibold text-green-700", children: allCanonicals.length })] }), _jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx(AlertTriangle, { className: "w-4 h-4 text-red-600" }), _jsx("span", { className: "text-xs font-medium text-gray-600", children: "Missing Canonical" })] }), _jsx("div", { className: "text-2xl font-semibold text-red-700", children: missingCanonical.length })] }), _jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx(Globe, { className: "w-4 h-4 text-blue-600" }), _jsx("span", { className: "text-xs font-medium text-gray-600", children: "Canonical Domains" })] }), _jsx("div", { className: "text-2xl font-semibold text-gray-900", children: canonicalDomains.length })] }), _jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx(CheckCircle2, { className: "w-4 h-4 text-green-600" }), _jsx("span", { className: "text-xs font-medium text-gray-600", children: "Self-Referencing" })] }), _jsx("div", { className: "text-2xl font-semibold text-green-700", children: allCanonicals.length }), _jsx("div", { className: "text-xs text-gray-500 mt-1", children: "Correct pattern" })] })] }), missingCanonical.length > 0 && (_jsx("div", { className: "bg-yellow-50 border border-yellow-200 rounded-lg p-4", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx(AlertTriangle, { className: "w-5 h-5 text-yellow-600 mt-0.5 shrink-0" }), _jsxs("div", { className: "flex-1", children: [_jsxs("h3", { className: "text-sm font-semibold text-yellow-900 mb-1", children: [missingCanonical.length, " page", missingCanonical.length !== 1 ? 's' : '', " missing canonical URLs"] }), _jsx("p", { className: "text-sm text-yellow-700 mb-2", children: "Without canonical tags, search engines may index duplicate versions of these pages, diluting your ranking signals." }), _jsx("div", { className: "flex flex-wrap gap-2", children: missingCanonical.map((p) => (_jsxs("button", { onClick: () => onNavigate?.(`/pages/${p.id}`), className: "px-2.5 py-1 text-xs bg-yellow-100 text-yellow-900 rounded-lg hover:bg-yellow-200 transition-colors", children: [p.title, " (", p.url, ")"] }, p.id))) })] })] }) })), _jsx("div", { className: "bg-white rounded-lg border border-gray-200 flex-1 overflow-hidden", children: _jsx("div", { className: "overflow-x-auto h-full", children: _jsxs("table", { className: "w-full", children: [_jsx("thead", { className: "bg-gray-50 border-b border-gray-200 sticky top-0", children: _jsxs("tr", { children: [_jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Page" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "URL" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Canonical URL" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Status" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Actions" })] }) }), _jsx("tbody", { className: "divide-y divide-gray-200", children: seoPages.map((page) => (_jsxs("tr", { className: "hover:bg-gray-50 transition-colors", children: [_jsx("td", { className: "px-4 py-3 text-sm font-medium text-gray-900", children: page.title }), _jsx("td", { className: "px-4 py-3", children: _jsx("code", { className: "text-xs bg-gray-100 px-2 py-1 rounded text-gray-700", children: page.url }) }), _jsx("td", { className: "px-4 py-3", children: page.canonical ? (_jsx("code", { className: "text-xs bg-gray-100 px-2 py-1 rounded text-gray-700 truncate max-w-[200px] block", children: page.canonical })) : (_jsx("span", { className: "text-xs text-red-600 font-medium", children: "Not set" })) }), _jsx("td", { className: "px-4 py-3", children: page.canonical ? (_jsx("span", { className: "px-2 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800", children: "\u2713 Set" })) : (_jsx("span", { className: "px-2 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800", children: "Missing" })) }), _jsx("td", { className: "px-4 py-3", children: _jsx("button", { onClick: () => onNavigate?.(`/pages/${page.id}`), className: "p-1.5 hover:bg-gray-100 rounded transition-colors", title: "Edit", children: _jsx(Pencil, { className: "w-4 h-4 text-gray-600" }) }) })] }, page.id))) })] }) }) })] }), _jsxs(Tabs.Content, { value: "links", className: "flex flex-col flex-1 min-h-0 space-y-4", children: [_jsxs("div", { className: "grid grid-cols-2 lg:grid-cols-4 gap-3", children: [_jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [_jsx("div", { className: "text-xs text-gray-600 mb-1", children: "Broken Links" }), _jsx("div", { className: "text-2xl font-semibold text-red-600", children: linkHealth.filter((l) => l.status === 404).length })] }), _jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [_jsx("div", { className: "text-xs text-gray-600 mb-1", children: "Redirect Chains" }), _jsx("div", { className: "text-2xl font-semibold text-yellow-600", children: linkHealth.filter((l) => l.status === 301).length })] }), _jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [_jsx("div", { className: "text-xs text-gray-600 mb-1", children: "Internal Issues" }), _jsx("div", { className: "text-2xl font-semibold text-gray-900", children: linkHealth.filter((l) => l.type === 'internal').length })] }), _jsxs("div", { className: "bg-white rounded-lg border border-gray-200 p-4", children: [_jsx("div", { className: "text-xs text-gray-600 mb-1", children: "External Issues" }), _jsx("div", { className: "text-2xl font-semibold text-gray-900", children: linkHealth.filter((l) => l.type === 'external').length })] })] }), _jsx("div", { className: "bg-white rounded-lg border border-gray-200 flex-1 overflow-hidden", children: _jsx("div", { className: "overflow-x-auto h-full", children: _jsxs("table", { className: "w-full", children: [_jsx("thead", { className: "bg-gray-50 border-b border-gray-200 sticky top-0", children: _jsxs("tr", { children: [_jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Found On" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Broken URL" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Status" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Type" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Last Checked" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Actions" })] }) }), _jsx("tbody", { className: "divide-y divide-gray-200", children: linkHealth.map((link) => (_jsxs("tr", { className: "hover:bg-gray-50 transition-colors", children: [_jsx("td", { className: "px-4 py-3", children: _jsx("code", { className: "text-xs bg-gray-100 px-2 py-1 rounded text-gray-700", children: link.page }) }), _jsx("td", { className: "px-4 py-3", children: _jsx("code", { className: "text-xs bg-red-50 px-2 py-1 rounded text-red-800 truncate max-w-[200px] block", children: link.url }) }), _jsx("td", { className: "px-4 py-3", children: _jsx("span", { className: `px-2 py-0.5 rounded-full text-xs font-medium ${link.status === 404 ? 'bg-red-100 text-red-800' : 'bg-yellow-100 text-yellow-800'}`, children: link.status }) }), _jsx("td", { className: "px-4 py-3", children: _jsx("span", { className: `px-2 py-0.5 rounded-full text-xs font-medium ${link.type === 'internal' ? 'bg-blue-100 text-blue-800' : 'bg-gray-100 text-gray-800'}`, children: link.type }) }), _jsx("td", { className: "px-4 py-3 text-sm text-gray-600", children: link.lastChecked }), _jsx("td", { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { className: "p-1.5 hover:bg-gray-100 rounded transition-colors", title: "Create redirect", children: _jsx(ArrowRightLeft, { className: "w-4 h-4 text-gray-600" }) }), _jsx("button", { className: "p-1.5 hover:bg-gray-100 rounded transition-colors", title: "Open URL", children: _jsx(ExternalLink, { className: "w-4 h-4 text-gray-600" }) })] }) })] }, link.id))) })] }) }) })] })] })] }));
|
|
141
|
+
}, className: "rounded-lg border border-red-300 px-3 py-1 text-sm text-red-700 transition-colors hover:bg-red-100", children: "Retry" })] })), _jsxs("div", { className: "mb-4 flex flex-col justify-between gap-3 sm:flex-row sm:items-center", children: [_jsxs("div", { children: [_jsx("h1", { className: "mb-1 text-xl font-semibold text-gray-900 sm:text-2xl", children: "SEO & Redirects" }), _jsx("p", { className: "text-sm text-gray-600", children: "Search optimization, redirects, canonicalization, and link health" })] }), _jsxs("button", { onClick: handleScan, disabled: scanning, className: "flex items-center justify-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm text-white transition-colors hover:bg-blue-700 disabled:opacity-60", children: [_jsx(RefreshCw, { className: `h-4 w-4 ${scanning ? 'animate-spin' : ''}` }), scanning ? 'Scanning...' : 'Run SEO Scan'] })] }), _jsxs(Tabs.Root, { value: activeTab, onValueChange: handleTabChange, children: [_jsxs(Tabs.List, { className: "mb-4 flex gap-1 overflow-x-auto border-b border-gray-200", children: [_jsx(Tabs.Trigger, { value: "pages", className: tabClass, children: _jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx(BarChart3, { className: "h-4 w-4" }), "Pages"] }) }), _jsx(Tabs.Trigger, { value: "redirects", className: tabClass, children: _jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx(ArrowRightLeft, { className: "h-4 w-4" }), "Redirects"] }) }), _jsx(Tabs.Trigger, { value: "canonicals", className: tabClass, children: _jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx(Copy, { className: "h-4 w-4" }), "Canonicalization"] }) }), _jsx(Tabs.Trigger, { value: "links", className: tabClass, children: _jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx(Link2, { className: "h-4 w-4" }), "Link Health"] }) })] }), _jsxs(Tabs.Content, { value: "pages", className: "flex min-h-0 flex-1 flex-col", children: [_jsxs("div", { className: "mb-4 grid grid-cols-2 gap-3 lg:grid-cols-4", children: [_jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx(BarChart3, { className: "h-4 w-4 text-blue-600" }), _jsx("span", { className: "text-xs font-medium text-gray-600", children: "Avg SEO Score" })] }), _jsx("div", { className: "text-2xl font-semibold text-gray-900", children: avgScore }), _jsx("div", { className: `mt-1 text-xs ${avgScore >= 80 ? 'text-green-600' : avgScore >= 50 ? 'text-yellow-600' : 'text-red-600'}`, children: avgScore >= 80 ? 'Good' : avgScore >= 50 ? 'Needs improvement' : 'Critical' })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx(AlertTriangle, { className: "h-4 w-4 text-yellow-600" }), _jsx("span", { className: "text-xs font-medium text-gray-600", children: "Total Issues" })] }), _jsx("div", { className: "text-2xl font-semibold text-gray-900", children: totalIssues }), _jsxs("div", { className: "mt-1 text-xs text-gray-500", children: ["Across ", seoPages.filter((p) => p.issues > 0).length, " pages"] })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx(FileCode2, { className: "h-4 w-4 text-purple-600" }), _jsx("span", { className: "text-xs font-medium text-gray-600", children: "Missing Meta" })] }), _jsx("div", { className: "text-2xl font-semibold text-gray-900", children: missingMeta }), _jsx("div", { className: "mt-1 text-xs text-gray-500", children: "Title or description" })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx(Globe, { className: "h-4 w-4 text-green-600" }), _jsx("span", { className: "text-xs font-medium text-gray-600", children: "Missing Schema" })] }), _jsx("div", { className: "text-2xl font-semibold text-gray-900", children: missingSchema }), _jsx("div", { className: "mt-1 text-xs text-gray-500", children: "No Schema.org markup" })] })] }), _jsx("div", { className: "mb-4 rounded-lg border border-indigo-200 bg-linear-to-r from-indigo-50 to-purple-50 p-4", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx(Bot, { className: "mt-0.5 h-5 w-5 shrink-0 text-indigo-600" }), _jsxs("div", { className: "flex-1", children: [_jsx("h3", { className: "mb-1 text-sm font-semibold text-indigo-900", children: "AI SEO Recommendations" }), _jsxs("p", { className: "mb-2 text-sm text-indigo-700", children: [missingMeta, " pages missing meta descriptions. ", missingCanonical.length, " pages without canonical URLs. AI can auto-generate these from page content."] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { onClick: () => onNavigate?.('/settings'), className: "rounded-lg bg-indigo-600 px-3 py-1.5 text-xs text-white transition-colors hover:bg-indigo-700", children: "Configure AI" }), _jsx("button", { className: "rounded-lg border border-indigo-300 px-3 py-1.5 text-xs text-indigo-700 transition-colors hover:bg-indigo-50", children: "Auto-fix All" })] })] })] }) }), _jsx("div", { className: "mb-4 rounded-lg border border-gray-200 bg-white", children: _jsxs("div", { className: "flex flex-col gap-3 p-3 sm:flex-row", children: [_jsxs("div", { className: "relative flex-1", 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", placeholder: "Search pages by URL or title...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), 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" })] }), _jsxs("select", { value: filterScore, onChange: (e) => setFilterScore(e.target.value), className: "rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none", children: [_jsx("option", { value: "all", children: "All Scores" }), _jsx("option", { value: "good", children: "Good (80+)" }), _jsx("option", { value: "warning", children: "Needs Work (50-79)" }), _jsx("option", { value: "critical", children: "Critical (<50)" })] })] }) }), _jsx("div", { className: "flex-1 overflow-hidden rounded-lg border border-gray-200 bg-white", children: _jsx("div", { className: "h-full overflow-x-auto", children: _jsxs("table", { className: "w-full", children: [_jsx("thead", { className: "sticky top-0 border-b border-gray-200 bg-gray-50", children: _jsxs("tr", { children: [_jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Page" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Score" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Readability" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Schema" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Meta" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Issues" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Actions" })] }) }), _jsx("tbody", { className: "divide-y divide-gray-200", children: filtered.map((page) => (_jsxs("tr", { className: "transition-colors hover:bg-gray-50", children: [_jsxs("td", { className: "px-4 py-3", children: [_jsx("div", { className: "text-sm font-medium text-gray-900", children: page.title }), _jsx("div", { className: "text-xs text-gray-500", children: page.url })] }), _jsx("td", { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [scoreIcon(page.score), _jsx("span", { className: `rounded-full px-2 py-0.5 text-xs font-medium ${scoreBadge(page.score)}`, children: page.score })] }) }), _jsx("td", { className: "px-4 py-3", children: page.readability > 0 ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(BookOpen, { className: "h-3.5 w-3.5 text-gray-400" }), _jsx("span", { className: `text-sm ${page.readability >= 80 ? 'text-green-700' : page.readability >= 60 ? 'text-yellow-700' : 'text-red-700'}`, children: page.readability })] })) : (_jsx("span", { className: "text-xs text-gray-400", children: "\u2014" })) }), _jsx("td", { className: "px-4 py-3", children: page.schemaType ? (_jsx("span", { className: "rounded-full bg-purple-100 px-2 py-0.5 text-xs font-medium text-purple-800", children: page.schemaType })) : (_jsx("span", { className: "rounded-full bg-red-100 px-2 py-0.5 text-xs font-medium text-red-800", children: "Missing" })) }), _jsx("td", { className: "px-4 py-3", children: _jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: `text-xs ${page.metaTitle ? 'text-green-700' : 'text-red-600'}`, children: page.metaTitle ? '✓ Title' : '✗ Title' }), _jsx("span", { className: `text-xs ${page.metaDescription ? 'text-green-700' : 'text-red-600'}`, children: page.metaDescription ? '✓ Desc' : '✗ Desc' })] }) }), _jsx("td", { className: "px-4 py-3", children: page.issues > 0 ? (_jsx("span", { className: "rounded-full bg-yellow-100 px-2 py-0.5 text-xs font-medium text-yellow-800", children: page.issues })) : (_jsx("span", { className: "rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800", children: "\u2713" })) }), _jsx("td", { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { onClick: () => onNavigate?.(`/pages/${page.id}`), className: "rounded p-1.5 transition-colors hover:bg-gray-100", title: "Edit", children: _jsx(ArrowUpRight, { className: "h-4 w-4 text-gray-600" }) }), _jsx("button", { className: "rounded p-1.5 transition-colors hover:bg-gray-100", title: "AI analyze", children: _jsx(Bot, { className: "h-4 w-4 text-indigo-600" }) })] }) })] }, page.id))) })] }) }) })] }), _jsxs(Tabs.Content, { value: "redirects", className: "flex min-h-0 flex-1 flex-col", children: [_jsxs("div", { className: "mb-4 grid grid-cols-2 gap-3 lg:grid-cols-4", children: [_jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsx("div", { className: "mb-1 text-xs text-gray-600", children: "Total Redirects" }), _jsx("div", { className: "text-2xl font-semibold text-gray-900", children: redirects.length })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsx("div", { className: "mb-1 text-xs text-gray-600", children: "Active" }), _jsx("div", { className: "text-2xl font-semibold text-green-600", children: redirects.filter((r) => r.active).length })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsx("div", { className: "mb-1 text-xs text-gray-600", children: "Total Hits" }), _jsx("div", { className: "text-2xl font-semibold text-blue-600", children: redirects.reduce((s, r) => s + r.hits, 0).toLocaleString() })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsx("div", { className: "mb-1 text-xs text-gray-600", children: "301 Permanent" }), _jsx("div", { className: "text-2xl font-semibold text-gray-900", children: redirects.filter((r) => r.type === '301').length })] })] }), _jsxs("div", { className: "mb-4 flex items-center gap-3", children: [_jsxs("div", { className: "relative max-w-md flex-1", 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", placeholder: "Search redirects...", value: redirectSearch, onChange: (e) => setRedirectSearch(e.target.value), className: "w-full rounded-lg border border-gray-300 bg-white py-2 pr-3 pl-9 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none" })] }), _jsxs("button", { onClick: () => setShowAddRedirect(true), className: "flex shrink-0 items-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm text-white transition-colors hover:bg-blue-700", children: [_jsx(Plus, { className: "h-4 w-4" }), "Add Redirect"] })] }), _jsx("div", { className: "flex-1 overflow-hidden rounded-lg border border-gray-200 bg-white", children: _jsx("div", { className: "h-full overflow-x-auto", children: _jsxs("table", { className: "w-full", children: [_jsx("thead", { className: "sticky top-0 border-b border-gray-200 bg-gray-50", children: _jsxs("tr", { children: [_jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Source" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Destination" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Type" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Hits" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Status" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Actions" })] }) }), _jsx("tbody", { className: "divide-y divide-gray-200", children: filteredRedirects.map((r) => (_jsxs("tr", { className: "transition-colors hover:bg-gray-50", children: [_jsx("td", { className: "px-4 py-3", children: _jsx("code", { className: "rounded bg-gray-100 px-2 py-1 text-xs text-gray-900", children: r.from }) }), _jsx("td", { className: "px-4 py-3", children: _jsx("code", { className: "rounded bg-gray-100 px-2 py-1 text-xs text-gray-900", children: r.to }) }), _jsx("td", { className: "px-4 py-3", children: _jsx("span", { className: `rounded-full px-2 py-0.5 text-xs font-medium ${r.type === '301' ? 'bg-blue-100 text-blue-800' : 'bg-purple-100 text-purple-800'}`, children: r.type }) }), _jsx("td", { className: "px-4 py-3 text-sm text-gray-600", children: r.hits.toLocaleString() }), _jsx("td", { className: "px-4 py-3", children: _jsx("span", { className: `rounded-full px-2 py-0.5 text-xs font-medium ${r.active ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'}`, children: r.active ? 'Active' : 'Inactive' }) }), _jsx("td", { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { className: "rounded p-1.5 transition-colors hover:bg-gray-100", children: _jsx(Pencil, { className: "h-4 w-4 text-gray-600" }) }), _jsx("button", { onClick: () => handleDeleteRedirect(r.id), className: "rounded p-1.5 transition-colors hover:bg-gray-100", children: _jsx(Trash2, { className: "h-4 w-4 text-red-600" }) })] }) })] }, r.id))) })] }) }) }), _jsx(Dialog.Root, { open: showAddRedirect, onOpenChange: setShowAddRedirect, children: _jsxs(Dialog.Portal, { children: [_jsx(Dialog.Overlay, { className: "fixed inset-0 z-50 bg-black/50" }), _jsxs(Dialog.Content, { className: "fixed top-1/2 left-1/2 z-50 w-full max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg bg-white p-6 shadow-lg", children: [_jsx(Dialog.Title, { className: "mb-4 text-lg font-semibold text-gray-900", children: "Add Redirect" }), _jsxs("form", { onSubmit: handleAddRedirect, className: "space-y-4", children: [_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Source URL" }), _jsx("input", { type: "text", value: newRedirect.source, onChange: (e) => setNewRedirect({ ...newRedirect, source: e.target.value }), placeholder: "/old-page", className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none", required: true })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Destination URL" }), _jsx("input", { type: "text", value: newRedirect.destination, onChange: (e) => setNewRedirect({ ...newRedirect, destination: e.target.value }), placeholder: "/new-page", className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none", required: true })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Type" }), _jsxs("select", { value: newRedirect.type, onChange: (e) => setNewRedirect({ ...newRedirect, type: e.target.value }), className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none", children: [_jsx("option", { value: "301", children: "301 (Permanent)" }), _jsx("option", { value: "302", children: "302 (Temporary)" })] })] }), _jsxs("div", { className: "flex justify-end gap-3 pt-4", children: [_jsx(Dialog.Close, { asChild: true, children: _jsx("button", { type: "button", className: "rounded-lg border border-gray-300 px-4 py-2 text-sm hover:bg-gray-50", children: "Cancel" }) }), _jsx("button", { type: "submit", className: "rounded-lg bg-blue-600 px-4 py-2 text-sm text-white hover:bg-blue-700", children: "Add Redirect" })] })] })] })] }) })] }), _jsxs(Tabs.Content, { value: "canonicals", className: "flex min-h-0 flex-1 flex-col space-y-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-3 lg:grid-cols-4", children: [_jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx(ShieldCheck, { className: "h-4 w-4 text-green-600" }), _jsx("span", { className: "text-xs font-medium text-gray-600", children: "With Canonical" })] }), _jsx("div", { className: "text-2xl font-semibold text-green-700", children: allCanonicals.length })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx(AlertTriangle, { className: "h-4 w-4 text-red-600" }), _jsx("span", { className: "text-xs font-medium text-gray-600", children: "Missing Canonical" })] }), _jsx("div", { className: "text-2xl font-semibold text-red-700", children: missingCanonical.length })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx(Globe, { className: "h-4 w-4 text-blue-600" }), _jsx("span", { className: "text-xs font-medium text-gray-600", children: "Canonical Domains" })] }), _jsx("div", { className: "text-2xl font-semibold text-gray-900", children: canonicalDomains.length })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-green-600" }), _jsx("span", { className: "text-xs font-medium text-gray-600", children: "Self-Referencing" })] }), _jsx("div", { className: "text-2xl font-semibold text-green-700", children: allCanonicals.length }), _jsx("div", { className: "mt-1 text-xs text-gray-500", children: "Correct pattern" })] })] }), missingCanonical.length > 0 && (_jsx("div", { className: "rounded-lg border border-yellow-200 bg-yellow-50 p-4", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-5 w-5 shrink-0 text-yellow-600" }), _jsxs("div", { className: "flex-1", children: [_jsxs("h3", { className: "mb-1 text-sm font-semibold text-yellow-900", children: [missingCanonical.length, " page", missingCanonical.length !== 1 ? 's' : '', " missing canonical URLs"] }), _jsx("p", { className: "mb-2 text-sm text-yellow-700", children: "Without canonical tags, search engines may index duplicate versions of these pages, diluting your ranking signals." }), _jsx("div", { className: "flex flex-wrap gap-2", children: missingCanonical.map((p) => (_jsxs("button", { onClick: () => onNavigate?.(`/pages/${p.id}`), className: "rounded-lg bg-yellow-100 px-2.5 py-1 text-xs text-yellow-900 transition-colors hover:bg-yellow-200", children: [p.title, " (", p.url, ")"] }, p.id))) })] })] }) })), _jsx("div", { className: "flex-1 overflow-hidden rounded-lg border border-gray-200 bg-white", children: _jsx("div", { className: "h-full overflow-x-auto", children: _jsxs("table", { className: "w-full", children: [_jsx("thead", { className: "sticky top-0 border-b border-gray-200 bg-gray-50", children: _jsxs("tr", { children: [_jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Page" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "URL" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Canonical URL" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Status" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Actions" })] }) }), _jsx("tbody", { className: "divide-y divide-gray-200", children: seoPages.map((page) => (_jsxs("tr", { className: "transition-colors hover:bg-gray-50", children: [_jsx("td", { className: "px-4 py-3 text-sm font-medium text-gray-900", children: page.title }), _jsx("td", { className: "px-4 py-3", children: _jsx("code", { className: "rounded bg-gray-100 px-2 py-1 text-xs text-gray-700", children: page.url }) }), _jsx("td", { className: "px-4 py-3", children: page.canonical ? (_jsx("code", { className: "block max-w-[200px] truncate rounded bg-gray-100 px-2 py-1 text-xs text-gray-700", children: page.canonical })) : (_jsx("span", { className: "text-xs font-medium text-red-600", children: "Not set" })) }), _jsx("td", { className: "px-4 py-3", children: page.canonical ? (_jsx("span", { className: "rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800", children: "\u2713 Set" })) : (_jsx("span", { className: "rounded-full bg-red-100 px-2 py-0.5 text-xs font-medium text-red-800", children: "Missing" })) }), _jsx("td", { className: "px-4 py-3", children: _jsx("button", { onClick: () => onNavigate?.(`/pages/${page.id}`), className: "rounded p-1.5 transition-colors hover:bg-gray-100", title: "Edit", children: _jsx(Pencil, { className: "h-4 w-4 text-gray-600" }) }) })] }, page.id))) })] }) }) })] }), _jsxs(Tabs.Content, { value: "links", className: "flex min-h-0 flex-1 flex-col space-y-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-3 lg:grid-cols-4", children: [_jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsx("div", { className: "mb-1 text-xs text-gray-600", children: "Broken Links" }), _jsx("div", { className: "text-2xl font-semibold text-red-600", children: linkHealth.filter((l) => l.status === 404).length })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsx("div", { className: "mb-1 text-xs text-gray-600", children: "Redirect Chains" }), _jsx("div", { className: "text-2xl font-semibold text-yellow-600", children: linkHealth.filter((l) => l.status === 301).length })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsx("div", { className: "mb-1 text-xs text-gray-600", children: "Internal Issues" }), _jsx("div", { className: "text-2xl font-semibold text-gray-900", children: linkHealth.filter((l) => l.type === 'internal').length })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsx("div", { className: "mb-1 text-xs text-gray-600", children: "External Issues" }), _jsx("div", { className: "text-2xl font-semibold text-gray-900", children: linkHealth.filter((l) => l.type === 'external').length })] })] }), _jsx("div", { className: "flex-1 overflow-hidden rounded-lg border border-gray-200 bg-white", children: _jsx("div", { className: "h-full overflow-x-auto", children: _jsxs("table", { className: "w-full", children: [_jsx("thead", { className: "sticky top-0 border-b border-gray-200 bg-gray-50", children: _jsxs("tr", { children: [_jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Found On" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Broken URL" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Status" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Type" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Last Checked" }), _jsx("th", { className: "px-4 py-2 text-left text-xs font-medium text-gray-700", children: "Actions" })] }) }), _jsx("tbody", { className: "divide-y divide-gray-200", children: linkHealth.map((link) => (_jsxs("tr", { className: "transition-colors hover:bg-gray-50", children: [_jsx("td", { className: "px-4 py-3", children: _jsx("code", { className: "rounded bg-gray-100 px-2 py-1 text-xs text-gray-700", children: link.page }) }), _jsx("td", { className: "px-4 py-3", children: _jsx("code", { className: "block max-w-[200px] truncate rounded bg-red-50 px-2 py-1 text-xs text-red-800", children: link.url }) }), _jsx("td", { className: "px-4 py-3", children: _jsx("span", { className: `rounded-full px-2 py-0.5 text-xs font-medium ${link.status === 404 ? 'bg-red-100 text-red-800' : 'bg-yellow-100 text-yellow-800'}`, children: link.status }) }), _jsx("td", { className: "px-4 py-3", children: _jsx("span", { className: `rounded-full px-2 py-0.5 text-xs font-medium ${link.type === 'internal' ? 'bg-blue-100 text-blue-800' : 'bg-gray-100 text-gray-800'}`, children: link.type }) }), _jsx("td", { className: "px-4 py-3 text-sm text-gray-600", children: link.lastChecked }), _jsx("td", { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { className: "rounded p-1.5 transition-colors hover:bg-gray-100", title: "Create redirect", children: _jsx(ArrowRightLeft, { className: "h-4 w-4 text-gray-600" }) }), _jsx("button", { className: "rounded p-1.5 transition-colors hover:bg-gray-100", title: "Open URL", children: _jsx(ExternalLink, { className: "h-4 w-4 text-gray-600" }) })] }) })] }, link.id))) })] }) }) })] })] })] }));
|
|
142
142
|
}
|
|
143
143
|
//# sourceMappingURL=SEO.js.map
|
|
@@ -97,14 +97,14 @@ export function ScriptTagEditor({ tagId, onNavigate }) {
|
|
|
97
97
|
}
|
|
98
98
|
};
|
|
99
99
|
if (loading && !isNew) {
|
|
100
|
-
return (_jsx("div", { className: "p-3 pr-6 sm:p-4 sm:pr-8
|
|
100
|
+
return (_jsx("div", { className: "flex h-64 items-center justify-center p-3 pr-6 sm:p-4 sm:pr-8", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-blue-600" }) }));
|
|
101
101
|
}
|
|
102
|
-
return (_jsxs("div", { className: "p-3 pr-6 sm:p-4 sm:pr-8
|
|
103
|
-
'Best for chat widgets, deferred scripts, and tracking pixels.'] })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Scope" }), _jsxs("select", { value: scope, onChange: (e) => setScope(e.target.value), className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:
|
|
102
|
+
return (_jsxs("div", { className: "max-w-3xl p-3 pr-6 sm:p-4 sm:pr-8", children: [error && (_jsxs("div", { className: "mb-4 flex items-center gap-3 rounded-lg border border-red-200 bg-red-50 p-3", children: [_jsx(AlertTriangle, { className: "h-5 w-5 shrink-0 text-red-600" }), _jsx("span", { className: "flex-1 text-sm text-red-800", children: error })] })), _jsxs("div", { className: "mb-6", children: [_jsxs("button", { type: "button", onClick: () => onNavigate?.('/script-tags'), className: "mb-3 flex items-center gap-1.5 text-sm text-gray-500 transition-colors hover:text-gray-700", children: [_jsx(ArrowLeft, { className: "h-4 w-4" }), "Back to Script Tags"] }), _jsx("h1", { className: "text-2xl font-semibold text-gray-900", children: isNew ? 'New Script Tag' : 'Edit Script Tag' })] }), _jsx("div", { className: "mb-6 rounded-lg border border-amber-200 bg-amber-50 p-3", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(AlertTriangle, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-600" }), _jsx("p", { className: "text-sm text-amber-800", children: "This code will run on public pages matching the scope below. Only add trusted code from verified sources (e.g. Google Analytics, Meta Pixel)." })] }) }), _jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "space-y-4 rounded-lg border border-gray-200 bg-white p-4", children: [_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Name" }), _jsx("input", { type: "text", value: name, onChange: (e) => setName(e.target.value), placeholder: "e.g. Google Tag Manager, Facebook Pixel", className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none" })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Code" }), _jsx("textarea", { value: code, onChange: (e) => setCode(e.target.value), placeholder: "Paste your HTML/JavaScript snippet here...", rows: 10, className: "w-full rounded-lg border border-gray-300 px-3 py-2 font-mono text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none", spellCheck: false }), _jsxs("p", { className: "mt-1 text-xs text-gray-500", children: ["Paste the full code snippet including ", '<script>', " tags."] })] })] }), _jsxs("div", { className: "space-y-4 rounded-lg border border-gray-200 bg-white p-4", children: [_jsx("h3", { className: "text-sm font-semibold text-gray-900", children: "Placement & Scope" }), _jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Placement" }), _jsxs("select", { value: placement, onChange: (e) => setPlacement(e.target.value), className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none", children: [_jsxs("option", { value: "head", children: ["Head \u2014 inside ", '<head>'] }), _jsxs("option", { value: "body_open", children: ["Body Open \u2014 right after ", '<body>'] }), _jsxs("option", { value: "body_close", children: ["Body Close \u2014 before ", '</body>'] })] }), _jsxs("p", { className: "mt-1 text-xs text-gray-500", children: [placement === 'head' && 'Best for analytics scripts, meta tags, and custom CSS.', placement === 'body_open' && 'Best for GTM noscript tags and early-loading scripts.', placement === 'body_close' &&
|
|
103
|
+
'Best for chat widgets, deferred scripts, and tracking pixels.'] })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Scope" }), _jsxs("select", { value: scope, onChange: (e) => setScope(e.target.value), className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none", children: [_jsx("option", { value: "site", children: "Entire Website" }), _jsx("option", { value: "parents", children: "Specific Parent Pages (includes child pages)" }), _jsx("option", { value: "urls", children: "Specific URLs (exact match)" })] })] }), scope !== 'site' && (_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: scope === 'parents' ? 'Parent Paths' : 'URL Paths' }), _jsxs("div", { className: "flex gap-2", children: [_jsx("input", { type: "text", value: pathInput, onChange: (e) => setPathInput(e.target.value), onKeyDown: (e) => {
|
|
104
104
|
if (e.key === 'Enter') {
|
|
105
105
|
e.preventDefault();
|
|
106
106
|
addPath();
|
|
107
107
|
}
|
|
108
|
-
}, placeholder: "/services", className: "flex-1 rounded-lg border border-gray-300 px-3 py-2
|
|
108
|
+
}, placeholder: "/services", className: "flex-1 rounded-lg border border-gray-300 px-3 py-2 font-mono text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none" }), _jsx("button", { type: "button", onClick: addPath, className: "rounded-lg border border-gray-300 px-3 py-2 text-sm transition-colors hover:bg-gray-50", children: _jsx(Plus, { className: "h-4 w-4" }) })] }), scope === 'parents' && (_jsx("p", { className: "mt-1 text-xs text-gray-500", children: "This tag will also apply to all child pages under each path." })), targetPaths.length > 0 && (_jsx("div", { className: "mt-2 flex flex-wrap gap-2", children: targetPaths.map((p) => (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-gray-100 px-3 py-1 font-mono text-xs text-gray-700", children: [p, _jsx("button", { type: "button", onClick: () => removePath(p), className: "ml-0.5 text-gray-400 hover:text-gray-600", children: _jsx(X, { className: "h-3 w-3" }) })] }, p))) }))] })), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Priority" }), _jsx("input", { type: "number", value: priority, onChange: (e) => setPriority(parseInt(e.target.value, 10) || 0), min: 0, className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none" }), _jsx("p", { className: "mt-1 text-xs text-gray-500", children: "Lower numbers load first (e.g. 1 = first, 100 = default)" })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Enabled" }), _jsx("div", { className: "pt-2", children: _jsx("button", { type: "button", onClick: () => setEnabled(!enabled), className: `relative h-6 w-11 shrink-0 rounded-full transition-colors ${enabled ? 'bg-blue-600' : 'bg-gray-300'}`, "aria-pressed": enabled, children: _jsx("span", { className: `absolute top-0.5 block h-5 w-5 rounded-full bg-white transition-transform ${enabled ? 'translate-x-[22px]' : 'translate-x-0.5'}` }) }) })] })] })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { children: !isNew && (_jsx(_Fragment, { children: showDeleteConfirm ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-sm text-red-600", children: "Delete this tag?" }), _jsx("button", { type: "button", onClick: handleDelete, disabled: deleting, className: "rounded-lg bg-red-600 px-3 py-1.5 text-sm text-white hover:bg-red-700 disabled:opacity-50", children: deleting ? 'Deleting...' : 'Confirm' }), _jsx("button", { type: "button", onClick: () => setShowDeleteConfirm(false), className: "rounded-lg border border-gray-300 px-3 py-1.5 text-sm hover:bg-gray-50", children: "Cancel" })] })) : (_jsxs("button", { type: "button", onClick: () => setShowDeleteConfirm(true), className: "flex items-center gap-1.5 text-sm text-red-600 transition-colors hover:text-red-700", children: [_jsx(Trash2, { className: "h-4 w-4" }), "Delete"] })) })) }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("button", { type: "button", onClick: () => onNavigate?.('/script-tags'), className: "rounded-lg border border-gray-300 px-4 py-2 text-sm text-gray-700 transition-colors hover:bg-gray-50", children: "Cancel" }), _jsx("button", { type: "button", onClick: handleSave, disabled: saving, className: "rounded-lg bg-blue-600 px-6 py-2 text-sm text-white transition-colors hover:bg-blue-700 disabled:opacity-50", children: saving ? 'Saving...' : isNew ? 'Create Tag' : 'Save Changes' })] })] })] })] }));
|
|
109
109
|
}
|
|
110
110
|
//# sourceMappingURL=ScriptTagEditor.js.map
|
package/dist/views/ScriptTags.js
CHANGED
|
@@ -49,8 +49,8 @@ export function ScriptTags({ onNavigate }) {
|
|
|
49
49
|
}
|
|
50
50
|
};
|
|
51
51
|
if (loading) {
|
|
52
|
-
return (_jsx("div", { className: "p-3 pr-6 sm:p-4 sm:pr-8
|
|
52
|
+
return (_jsx("div", { className: "flex h-64 items-center justify-center p-3 pr-6 sm:p-4 sm:pr-8", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-blue-600" }) }));
|
|
53
53
|
}
|
|
54
|
-
return (_jsxs("div", { className: "p-3 pr-6 sm:p-4 sm:pr-8", children: [error && (_jsxs("div", { className: "mb-4 flex items-center gap-3 rounded-lg border border-red-200 bg-red-50 p-3", children: [_jsx(AlertTriangle, { className: "
|
|
54
|
+
return (_jsxs("div", { className: "p-3 pr-6 sm:p-4 sm:pr-8", children: [error && (_jsxs("div", { className: "mb-4 flex items-center gap-3 rounded-lg border border-red-200 bg-red-50 p-3", children: [_jsx(AlertTriangle, { className: "h-5 w-5 shrink-0 text-red-600" }), _jsx("span", { className: "flex-1 text-sm text-red-800", children: error }), _jsx("button", { onClick: refetch, className: "rounded-lg border border-red-300 px-3 py-1 text-sm text-red-700 transition-colors hover:bg-red-100", children: "Retry" })] })), _jsxs("div", { className: "mb-4 flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("h1", { className: "mb-1 text-2xl font-semibold text-gray-900", children: "Script Tags" }), _jsx("p", { className: "text-sm text-gray-600", children: "Manage tracking codes, analytics, and custom scripts injected into your site" })] }), _jsxs("button", { type: "button", onClick: () => onNavigate?.('/script-tags/new'), className: "flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700", children: [_jsx(Plus, { className: "h-4 w-4" }), "New Tag"] })] }), tags.length === 0 && !error ? (_jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-12 text-center", children: [_jsx(Code2, { className: "mx-auto mb-3 h-10 w-10 text-gray-300" }), _jsx("h3", { className: "text-sm font-semibold text-gray-900", children: "No script tags yet" }), _jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Add tracking codes like Google Analytics, Tag Manager, or Facebook Pixel." }), _jsxs("button", { type: "button", onClick: () => onNavigate?.('/script-tags/new'), className: "mt-4 inline-flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700", children: [_jsx(Plus, { className: "h-4 w-4" }), "Add Your First Tag"] })] })) : (_jsx("div", { className: "overflow-hidden rounded-lg border border-gray-200 bg-white", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b border-gray-200 bg-gray-50", children: [_jsx("th", { className: "px-4 py-3 text-left font-medium text-gray-600", children: "Name" }), _jsx("th", { className: "px-4 py-3 text-left font-medium text-gray-600", children: "Placement" }), _jsx("th", { className: "px-4 py-3 text-left font-medium text-gray-600", children: "Scope" }), _jsx("th", { className: "px-4 py-3 text-center font-medium text-gray-600", children: "Priority" }), _jsx("th", { className: "px-4 py-3 text-center font-medium text-gray-600", children: "Enabled" })] }) }), _jsx("tbody", { children: tags.map((tag) => (_jsxs("tr", { className: "border-b border-gray-100 transition-colors last:border-0 hover:bg-gray-50", children: [_jsx("td", { className: "px-4 py-3", children: _jsx("button", { type: "button", onClick: () => onNavigate?.(`/script-tags/${tag.id}`), className: "font-medium text-blue-600 hover:text-blue-800 hover:underline", children: tag.name }) }), _jsx("td", { className: "px-4 py-3", children: _jsx("span", { className: `inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${PLACEMENT_COLORS[tag.placement] ?? 'bg-gray-100 text-gray-700'}`, children: PLACEMENT_LABELS[tag.placement] ?? tag.placement }) }), _jsx("td", { className: "px-4 py-3 text-gray-600", children: scopeLabel(tag) }), _jsx("td", { className: "px-4 py-3 text-center font-mono text-gray-600", children: tag.priority }), _jsx("td", { className: "px-4 py-3 text-center", children: _jsx("button", { type: "button", onClick: () => toggleEnabled(tag), disabled: togglingId === tag.id, className: `relative h-6 w-11 shrink-0 rounded-full transition-colors ${tag.enabled ? 'bg-blue-600' : 'bg-gray-300'}`, "aria-pressed": tag.enabled, children: _jsx("span", { className: `absolute top-0.5 block h-5 w-5 rounded-full bg-white transition-transform ${tag.enabled ? 'translate-x-[22px]' : 'translate-x-0.5'}` }) }) })] }, tag.id))) })] }) }))] }));
|
|
55
55
|
}
|
|
56
56
|
//# sourceMappingURL=ScriptTags.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Settings.d.ts","sourceRoot":"","sources":["../../src/views/Settings.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Settings.d.ts","sourceRoot":"","sources":["../../src/views/Settings.tsx"],"names":[],"mappings":"AA+BA,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,MAAM,CAAC,EAAE,GAAG,CAAA;CACb;AAED,wBAAgB,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,GAAE,aAAkB,2CA4nBjE"}
|
package/dist/views/Settings.js
CHANGED
|
@@ -8,6 +8,7 @@ import { useApiData } from '../lib/useApiData.js';
|
|
|
8
8
|
import { cmsApi } from '../lib/api.js';
|
|
9
9
|
import { useTheme } from '../components/ThemeProvider.js';
|
|
10
10
|
import { RelationshipField } from '../fields/RelationshipField.js';
|
|
11
|
+
import { SEOConfigPanel } from '../components/SEOConfigPanel.js';
|
|
11
12
|
export function Settings({ config, ..._props } = {}) {
|
|
12
13
|
const { data, loading, error, refetch } = useApiData('/globals/settings');
|
|
13
14
|
const [siteTitle, setSiteTitle] = useState('My CMS');
|
|
@@ -115,9 +116,9 @@ export function Settings({ config, ..._props } = {}) {
|
|
|
115
116
|
};
|
|
116
117
|
const tabTriggerClass = 'px-4 py-2 text-sm font-medium text-gray-600 transition-colors hover:text-gray-900 data-[state=active]:border-b-2 data-[state=active]:border-blue-600 data-[state=active]:text-blue-600';
|
|
117
118
|
if (loading) {
|
|
118
|
-
return (_jsx("div", { className: "p-3 pr-6 sm:p-4 sm:pr-8
|
|
119
|
+
return (_jsx("div", { className: "flex h-64 items-center justify-center p-3 pr-6 sm:p-4 sm:pr-8", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-blue-600" }) }));
|
|
119
120
|
}
|
|
120
|
-
return (_jsxs("div", { className: "p-3 pr-6 sm:p-4 sm:pr-8", children: [error && (_jsxs("div", { className: "mb-4 flex items-center gap-3 rounded-lg border border-red-200 bg-red-50 p-3", children: [_jsx(AlertTriangle, { className: "
|
|
121
|
+
return (_jsxs("div", { className: "p-3 pr-6 sm:p-4 sm:pr-8", children: [error && (_jsxs("div", { className: "mb-4 flex items-center gap-3 rounded-lg border border-red-200 bg-red-50 p-3", children: [_jsx(AlertTriangle, { className: "h-5 w-5 shrink-0 text-red-600" }), _jsx("span", { className: "flex-1 text-sm text-red-800", children: error }), _jsx("button", { onClick: refetch, className: "rounded-lg border border-red-300 px-3 py-1 text-sm text-red-700 transition-colors hover:bg-red-100", children: "Retry" })] })), _jsxs("div", { className: "mb-4", children: [_jsx("h1", { className: "mb-1 text-2xl font-semibold text-gray-900", children: "Settings" }), _jsx("p", { className: "text-sm text-gray-600", children: "Manage your CMS configuration" })] }), _jsxs(Tabs.Root, { value: activeTab, onValueChange: setActiveTab, children: [_jsxs(Tabs.List, { className: "mb-4 flex gap-1 overflow-x-auto border-b border-gray-200", children: [_jsx(Tabs.Trigger, { value: "general", className: tabTriggerClass, children: "General" }), _jsx(Tabs.Trigger, { value: "appearance", className: tabTriggerClass, children: "Appearance" }), hasLayoutRegions && (_jsx(Tabs.Trigger, { value: "layout", className: tabTriggerClass, children: _jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx(Layers, { className: "h-4 w-4" }), "Layout"] }) })), _jsx(Tabs.Trigger, { value: "security", className: tabTriggerClass, children: "Security" }), _jsx(Tabs.Trigger, { value: "seo", className: tabTriggerClass, children: "SEO" }), _jsx(Tabs.Trigger, { value: "ai", className: tabTriggerClass, children: _jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx(Bot, { className: "h-4 w-4" }), "AI"] }) }), _jsx(Tabs.Trigger, { value: "integrations", className: tabTriggerClass, children: "Integrations" }), _jsx(Tabs.Trigger, { value: "updates", className: tabTriggerClass, children: _jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx(Download, { className: "h-4 w-4" }), "Updates"] }) })] }), _jsxs(Tabs.Content, { value: "general", className: "space-y-4", children: [_jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsx("h3", { className: "mb-4 text-sm font-semibold text-gray-900", children: "Site Information" }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Site Title" }), _jsx("input", { type: "text", value: siteTitle, onChange: (e) => setSiteTitle(e.target.value), className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none" })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Tagline" }), _jsx("input", { type: "text", value: tagline, onChange: (e) => setTagline(e.target.value), className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none" })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Site URL" }), _jsx("input", { type: "url", value: siteUrl, onChange: (e) => setSiteUrl(e.target.value), className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none" })] })] })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsx("h3", { className: "mb-4 text-sm font-semibold text-gray-900", children: "Language & Region" }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Language" }), _jsxs("select", { value: language, onChange: (e) => setLanguage(e.target.value), className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none", children: [_jsx("option", { value: "en", children: "English" }), _jsx("option", { value: "es", children: "Spanish" }), _jsx("option", { value: "fr", children: "French" }), _jsx("option", { value: "de", children: "German" })] })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Timezone" }), _jsxs("select", { value: timezone, onChange: (e) => setTimezone(e.target.value), className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none", children: [_jsx("option", { value: "UTC", children: "UTC" }), _jsx("option", { value: "America/New_York", children: "Eastern Time" }), _jsx("option", { value: "America/Chicago", children: "Central Time" }), _jsx("option", { value: "America/Denver", children: "Mountain Time" }), _jsx("option", { value: "America/Los_Angeles", children: "Pacific Time" })] })] })] })] }), _jsxs("div", { className: "border-border bg-card rounded-lg border p-4", children: [_jsx("h3", { className: "text-foreground mb-1 text-sm font-medium", children: "SEO & Robots Defaults" }), _jsx("p", { className: "text-muted-foreground mb-4 text-xs", children: "Set the site-wide default for search engine indexing. Individual pages can inherit or override these rules in their SEO panel." }), _jsxs("div", { className: "space-y-4", children: [_jsx(ToggleSetting, { label: "Default No Index", description: "Ask search engines not to index pages unless a page explicitly allows indexing", checked: defaultNoIndex, onChange: setDefaultNoIndex }), _jsx(ToggleSetting, { label: "Default No Follow", description: "Ask search engines not to follow page links unless a page explicitly allows following", checked: defaultNoFollow, onChange: setDefaultNoFollow }), _jsx(ToggleSetting, { label: "Noindex Non-Production Environments", description: "Force noindex when the deployed environment is not production", checked: noIndexNonProduction, onChange: setNoIndexNonProduction })] })] })] }), _jsx(Tabs.Content, { value: "appearance", className: "space-y-4", children: _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsx("h3", { className: "mb-4 text-sm font-semibold text-gray-900", children: "Theme" }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Color Scheme" }), _jsx(ThemeSelect, {})] }), _jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Primary Color" }), _jsx("input", { type: "color", defaultValue: "#3b82f6", className: "h-10 w-full rounded-lg border border-gray-300 px-3 py-2" })] })] })] }) }), hasLayoutRegions && (_jsxs(Tabs.Content, { value: "layout", className: "space-y-4", children: [_jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsx("h3", { className: "mb-1 text-sm font-semibold text-gray-900", children: "Default Layout Variants" }), _jsx("p", { className: "mb-4 text-xs text-gray-500", children: "Select the default header, footer, and other layout variants used site-wide. Pages can override these individually or inherit from parent pages." }), _jsx("div", { className: "space-y-4", children: layoutRegions.map((region) => (_jsx(RelationshipField, { label: `Default ${region.label}`, value: defaultLayout[region.name] ?? '', onChange: (val) => {
|
|
121
122
|
setDefaultLayout((prev) => {
|
|
122
123
|
const next = { ...prev };
|
|
123
124
|
if (val && typeof val === 'string') {
|
|
@@ -128,26 +129,26 @@ export function Settings({ config, ..._props } = {}) {
|
|
|
128
129
|
}
|
|
129
130
|
return next;
|
|
130
131
|
});
|
|
131
|
-
}, relationTo: region.collection, helpText: `The ${region.label.toLowerCase()} variant used when no page in the ancestor chain specifies one` }, region.name))) })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-gray-50 p-4", children: [_jsx("h3", { className: "text-sm font-semibold text-gray-700
|
|
132
|
+
}, relationTo: region.collection, helpText: `The ${region.label.toLowerCase()} variant used when no page in the ancestor chain specifies one` }, region.name))) })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-gray-50 p-4", children: [_jsx("h3", { className: "mb-2 text-sm font-semibold text-gray-700", children: "How Layout Inheritance Works" }), _jsxs("ul", { className: "space-y-1.5 text-xs text-gray-600", children: [_jsxs("li", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-blue-100 text-[10px] font-bold text-blue-600", children: "1" }), _jsx("span", { children: "Each page can assign specific layout variants (header, footer, etc.) from the document editor." })] }), _jsxs("li", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-blue-100 text-[10px] font-bold text-blue-600", children: "2" }), _jsxs("span", { children: ["Child pages automatically inherit their parent's layout. For example,", ' ', _jsx("code", { className: "rounded bg-gray-200 px-1 font-mono", children: "/hampton-roads/thank-you" }), ' ', "inherits from", ' ', _jsx("code", { className: "rounded bg-gray-200 px-1 font-mono", children: "/hampton-roads" }), "."] })] }), _jsxs("li", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-blue-100 text-[10px] font-bold text-blue-600", children: "3" }), _jsx("span", { children: "If no page in the ancestor chain sets a variant, the defaults configured above are used." })] })] })] })] })), _jsx(Tabs.Content, { value: "security", className: "space-y-4", children: _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsx("h3", { className: "mb-4 text-sm font-semibold text-gray-900", children: "Security Settings" }), _jsxs("div", { className: "space-y-4", children: [_jsx(ToggleSetting, { label: "Two-Factor Authentication", description: "Require 2FA for all admin users", checked: twoFactorEnabled, onChange: setTwoFactorEnabled }), _jsx(ToggleSetting, { label: "Session Timeout", description: "Automatically log out inactive users after 30 minutes", checked: sessionTimeout, onChange: setSessionTimeout }), _jsx(ToggleSetting, { label: "IP Whitelist", description: "Only allow access from approved IP addresses", checked: ipWhitelist, onChange: setIpWhitelist })] })] }) }), _jsx(Tabs.Content, { value: "seo", className: "space-y-4", children: _jsx(SEOConfigPanel, {}) }), _jsxs(Tabs.Content, { value: "ai", className: "space-y-4", children: [_jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsx("h3", { className: "mb-1 text-sm font-semibold text-gray-900", children: "AI Provider & API Key" }), _jsx("p", { className: "mb-4 text-xs text-gray-500", children: "Connect an AI provider to enable intelligent content features" }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Provider" }), _jsxs("select", { value: aiProvider, onChange: (e) => setAiProvider(e.target.value), className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none", children: [_jsx("option", { value: "anthropic", children: "Anthropic (Claude)" }), _jsx("option", { value: "openai", children: "OpenAI (GPT)" }), _jsx("option", { value: "google", children: "Google (Gemini)" })] }), _jsxs("p", { className: "mt-1 text-xs text-gray-500", children: [aiProvider === 'anthropic' &&
|
|
132
133
|
'Recommended. Best for content analysis, brand voice learning, and nuanced writing.', aiProvider === 'openai' &&
|
|
133
134
|
'Strong alternative. Good for general content generation and image understanding.', aiProvider === 'google' &&
|
|
134
135
|
'Multimodal. Excellent for image analysis and multilingual content.'] })] }), _jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "API Key" }), _jsxs("div", { className: "flex gap-2", children: [_jsxs("div", { className: "relative flex-1", children: [_jsx("input", { type: showApiKey ? 'text' : 'password', value: aiApiKey, onChange: (e) => setAiApiKey(e.target.value), placeholder: aiProvider === 'anthropic'
|
|
135
136
|
? 'sk-ant-...'
|
|
136
137
|
: aiProvider === 'openai'
|
|
137
138
|
? 'sk-...'
|
|
138
|
-
: 'AIza...', className: "w-full rounded-lg border border-gray-300 px-3 py-2 pr-10 text-sm focus:
|
|
139
|
+
: 'AIza...', className: "w-full rounded-lg border border-gray-300 px-3 py-2 pr-10 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none" }), _jsx("button", { type: "button", onClick: () => setShowApiKey(!showApiKey), className: "absolute top-1/2 right-2 -translate-y-1/2 p-1 text-gray-400 hover:text-gray-600", children: showApiKey ? _jsx(EyeOff, { className: "h-4 w-4" }) : _jsx(Eye, { className: "h-4 w-4" }) })] }), _jsx("button", { type: "button", onClick: () => {
|
|
139
140
|
if (aiApiKey)
|
|
140
141
|
toast.success('API key verified successfully');
|
|
141
142
|
else
|
|
142
143
|
toast.error('Please enter an API key first');
|
|
143
|
-
}, className: "rounded-lg border border-gray-300 px-4 py-2 text-sm transition-colors hover:bg-gray-50", children: "Verify" })] }), _jsx("p", { className: "mt-1 text-xs text-gray-500", children: "Your key is encrypted at rest (AES-256-GCM) and never exposed to the client" })] })] })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsxs("div", { className: "flex items-center gap-2
|
|
144
|
+
}, className: "rounded-lg border border-gray-300 px-4 py-2 text-sm transition-colors hover:bg-gray-50", children: "Verify" })] }), _jsx("p", { className: "mt-1 text-xs text-gray-500", children: "Your key is encrypted at rest (AES-256-GCM) and never exposed to the client" })] })] })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsxs("div", { className: "mb-1 flex items-center gap-2", children: [_jsx(Image, { className: "h-4 w-4 text-blue-600" }), _jsx("h3", { className: "text-sm font-semibold text-gray-900", children: "Media Intelligence" })] }), _jsx("p", { className: "mb-4 text-xs text-gray-500", children: "AI-powered image and media analysis" }), _jsxs("div", { className: "space-y-4", children: [_jsx(ToggleSetting, { label: "Auto Alt-Tag Generation", description: "Scan uploaded images and automatically generate descriptive alt text for accessibility and SEO", checked: aiAltTags, onChange: setAiAltTags }), _jsx(ToggleSetting, { label: "Media Auto-Categorization", description: "Automatically tag and categorize uploaded media based on visual content analysis", checked: aiMediaCategorize, onChange: setAiMediaCategorize })] })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsxs("div", { className: "mb-1 flex items-center gap-2", children: [_jsx(FileCode2, { className: "h-4 w-4 text-purple-600" }), _jsx("h3", { className: "text-sm font-semibold text-gray-900", children: "Content SEO" })] }), _jsx("p", { className: "mb-4 text-xs text-gray-500", children: "AI-driven SEO optimization for your content" }), _jsxs("div", { className: "space-y-4", children: [_jsx(ToggleSetting, { label: "Meta Description Generation", description: "Auto-generate optimized meta descriptions by digesting page content", checked: aiMetaDescriptions, onChange: setAiMetaDescriptions }), _jsx(ToggleSetting, { label: "Readability Analysis", description: "Score content readability (Flesch-Kincaid, Gunning Fog) with improvement suggestions", checked: aiReadability, onChange: setAiReadability }), _jsx(ToggleSetting, { label: "Schema.org Enrichment", description: "Automatically detect content types and generate appropriate Schema.org JSON-LD markup", checked: aiSchema, onChange: setAiSchema })] })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsxs("div", { className: "mb-1 flex items-center gap-2", children: [_jsx(MessageSquare, { className: "h-4 w-4 text-green-600" }), _jsx("h3", { className: "text-sm font-semibold text-gray-900", children: "Brand Voice & Writing" })] }), _jsx("p", { className: "mb-4 text-xs text-gray-500", children: "AI that understands and writes in your brand's voice" }), _jsxs("div", { className: "space-y-4", children: [_jsx(ToggleSetting, { label: "Brand Voice Training", description: "Analyze existing content to learn your brand's tone, style, and vocabulary", checked: aiBrandVoice, onChange: setAiBrandVoice }), _jsx(ToggleSetting, { label: "AI Writing Assistant", description: "In-editor AI helper for drafting, rewriting, and expanding content in your brand voice", checked: aiWritingAssistant, onChange: setAiWritingAssistant })] })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsxs("div", { className: "mb-1 flex items-center gap-2", children: [_jsx(Sparkles, { className: "h-4 w-4 text-yellow-600" }), _jsx("h3", { className: "text-sm font-semibold text-gray-900", children: "Content Quality" })] }), _jsx("p", { className: "mb-4 text-xs text-gray-500", children: "Automated quality scoring and content intelligence" }), _jsxs("div", { className: "space-y-4", children: [_jsx(ToggleSetting, { label: "Content Scoring", description: "Automatically score content quality based on structure, readability, SEO, and completeness", checked: aiContentScoring, onChange: setAiContentScoring }), _jsx(ToggleSetting, { label: "AI-Powered Translation", description: "Translate content into target languages while preserving tone and meaning", checked: aiTranslation, onChange: setAiTranslation })] })] }), _jsxs("div", { className: "rounded-lg border border-indigo-200 bg-indigo-50 p-4", children: [_jsxs("div", { className: "mb-1 flex items-center gap-2", children: [_jsx(BookOpen, { className: "h-4 w-4 text-indigo-600" }), _jsx("h3", { className: "text-sm font-semibold text-indigo-900", children: "AI Usage This Month" })] }), _jsxs("div", { className: "mt-3 grid grid-cols-3 gap-4", children: [_jsxs("div", { children: [_jsx("div", { className: "text-lg font-semibold text-indigo-900", children: "0" }), _jsx("div", { className: "text-xs text-indigo-700", children: "API Calls" })] }), _jsxs("div", { children: [_jsx("div", { className: "text-lg font-semibold text-indigo-900", children: "0" }), _jsx("div", { className: "text-xs text-indigo-700", children: "Tokens Used" })] }), _jsxs("div", { children: [_jsx("div", { className: "text-lg font-semibold text-indigo-900", children: "$0.00" }), _jsx("div", { className: "text-xs text-indigo-700", children: "Estimated Cost" })] })] }), _jsx("p", { className: "mt-2 text-xs text-indigo-600", children: "Add an API key above to start using AI features" })] })] }), _jsx(Tabs.Content, { value: "integrations", className: "space-y-4", children: _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsx("h3", { className: "mb-4 text-sm font-semibold text-gray-900", children: "API Keys" }), _jsx("div", { className: "space-y-4", children: _jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "API Key" }), _jsxs("div", { className: "flex gap-2", children: [_jsx("input", { type: "text", value: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", readOnly: true, className: "flex-1 rounded-lg border border-gray-300 bg-gray-50 px-3 py-2 text-sm" }), _jsx("button", { type: "button", className: "rounded-lg border border-gray-300 px-4 py-2 text-sm transition-colors hover:bg-gray-50", children: "Regenerate" })] })] }) })] }) }), _jsx(Tabs.Content, { value: "updates", className: "space-y-4", children: _jsx(UpdatesPanel, {}) })] }), _jsx("div", { className: "mt-6 flex justify-end", children: _jsx("button", { type: "button", onClick: handleSave, disabled: saving, className: "rounded-lg bg-blue-600 px-6 py-2 text-sm text-white transition-colors hover:bg-blue-700 disabled:opacity-50", children: saving ? 'Saving...' : 'Save Changes' }) })] }));
|
|
144
145
|
}
|
|
145
146
|
function ThemeSelect() {
|
|
146
147
|
const { theme, setTheme } = useTheme();
|
|
147
|
-
return (_jsxs("select", { value: theme, onChange: (e) => setTheme(e.target.value), className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:
|
|
148
|
+
return (_jsxs("select", { value: theme, onChange: (e) => setTheme(e.target.value), className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none", children: [_jsx("option", { value: "light", children: "Light" }), _jsx("option", { value: "dark", children: "Dark" }), _jsx("option", { value: "system", children: "Auto (System)" })] }));
|
|
148
149
|
}
|
|
149
150
|
function ToggleSetting({ label, description, checked, onChange, }) {
|
|
150
|
-
return (_jsxs("div", { className: "flex items-center justify-between gap-4", children: [_jsxs("div", { className: "flex-1", children: [_jsx("label", { className: "text-sm font-medium
|
|
151
|
+
return (_jsxs("div", { className: "flex items-center justify-between gap-4", children: [_jsxs("div", { className: "flex-1", children: [_jsx("label", { className: "text-foreground text-sm font-medium", children: label }), _jsx("p", { className: "text-muted-foreground mt-0.5 text-xs", children: description })] }), _jsx("button", { type: "button", role: "switch", "aria-checked": checked, "aria-label": label, onClick: () => onChange(!checked), className: `focus:ring-primary relative h-6 w-11 shrink-0 rounded-full transition-colors focus:ring-2 focus:outline-none ${checked ? 'bg-primary' : 'bg-muted'}`, children: _jsx("span", { className: `absolute top-0.5 block h-5 w-5 rounded-full bg-white transition-transform ${checked ? 'translate-x-[22px]' : 'translate-x-0.5'}` }) })] }));
|
|
151
152
|
}
|
|
152
153
|
function UpdatesPanel() {
|
|
153
154
|
const [updateInfo, setUpdateInfo] = useState(null);
|
|
@@ -252,6 +253,6 @@ function UpdatesPanel() {
|
|
|
252
253
|
},
|
|
253
254
|
major: { bg: 'bg-red-50', text: 'text-red-700', border: 'border-red-200', label: 'Major' },
|
|
254
255
|
};
|
|
255
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-sm font-semibold text-gray-900", children: "Actuate CMS" }), _jsxs("p", { className: "mt-1 text-sm text-gray-600", children: ["Current version:", ' ', _jsx("span", { className: "font-mono font-medium", children: updateInfo?.current ?? '...' })] })] }), _jsxs("button", { type: "button", onClick: checkForUpdates, disabled: checking, className: "flex items-center gap-2 rounded-lg border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-50 disabled:opacity-50", children: [_jsx(RefreshCw, { className: `w-4 h-4 ${checking ? 'animate-spin' : ''}` }), checking ? 'Checking...' : 'Check for Updates'] })] }) }), checkError && (_jsxs("div", { className: "flex items-center gap-3 rounded-lg border border-red-200 bg-red-50 p-3", children: [_jsx(AlertTriangle, { className: "w-5 h-5 text-red-600 shrink-0" }), _jsx("span", { className: "text-sm text-red-800 flex-1", children: checkError }), _jsx("button", { onClick: checkForUpdates, className: "px-3 py-1 text-sm text-red-700 border border-red-300 rounded-lg hover:bg-red-100 transition-colors", children: "Retry" })] })), hasChecked && updateInfo && !updateInfo.updateAvailable && !checkError && (_jsxs("div", { className: "flex items-center gap-3 rounded-lg border border-green-200 bg-green-50 p-4", children: [_jsx(CheckCircle2, { className: "w-6 h-6 text-green-600 shrink-0" }), _jsxs("div", { children: [_jsx("h3", { className: "text-sm font-semibold text-green-900", children: "You're up to date!" }), _jsxs("p", { className: "text-sm text-green-700 mt-0.5", children: ["Actuate CMS ", _jsx("span", { className: "font-mono", children: updateInfo.current }), " is the latest version."] })] })] })), updateInfo?.updateAvailable && (_jsxs(_Fragment, { children: [_jsxs("div", { className: `rounded-lg border p-4 ${severityColors[updateInfo.severity ?? 'patch']?.border ?? 'border-blue-200'} ${severityColors[updateInfo.severity ?? 'patch']?.bg ?? 'bg-blue-50'}`, children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsx(ArrowUpCircle, { className: `w-6 h-6 mt-0.5 shrink-0 ${severityColors[updateInfo.severity ?? 'patch']?.text ?? 'text-blue-700'}` }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("h3", { className: `text-sm font-semibold ${severityColors[updateInfo.severity ?? 'patch']?.text ?? 'text-blue-700'}`, children: "Update Available" }), _jsx("span", { className: `inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${severityColors[updateInfo.severity ?? 'patch']?.bg ?? 'bg-blue-50'} ${severityColors[updateInfo.severity ?? 'patch']?.text ?? 'text-blue-700'} border ${severityColors[updateInfo.severity ?? 'patch']?.border ?? 'border-blue-200'}`, children: severityColors[updateInfo.severity ?? 'patch']?.label ?? 'Update' })] }), _jsxs("p", { className: "text-sm mt-1", style: { color: 'inherit' }, children: [_jsx("span", { className: "font-mono", children: updateInfo.current }), " \u2192", ' ', _jsx("span", { className: "font-mono font-semibold", children: updateInfo.latest }), updateInfo.releaseDate && (_jsxs("span", { className: "text-xs ml-2 opacity-70", children: ["Released ", updateInfo.releaseDate] }))] })] })] }), _jsxs("div", { className: "mt-4 flex items-center gap-3", children: [_jsx("button", { type: "button", onClick: applyUpdate, disabled: applying, className: "flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700 disabled:opacity-50", children: applying ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "w-4 h-4 animate-spin" }), "Creating PR..."] })) : (_jsxs(_Fragment, { children: [_jsx(GitPullRequest, { className: "w-4 h-4" }), "Create Update PR"] })) }), _jsx("span", { className: "text-xs text-gray-500", children: "Opens a pull request on your repository" })] }), updateInfo.updateCommand && (_jsxs("div", { className: "mt-3 rounded border border-gray-200 bg-white p-3", children: [_jsx("p", { className: "text-xs text-gray-500 mb-1", children: "Or update manually:" }), _jsx("code", { className: "block text-xs font-mono text-gray-800 bg-gray-50 rounded px-2 py-1.5 select-all", children: updateInfo.updateCommand })] }))] }), updateInfo.changelog && updateInfo.changelog.length > 0 && (_jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsx("h3", { className: "text-sm font-semibold text-gray-900 mb-3", children: "Changelog" }), _jsx("div", { className: "space-y-2 max-h-64 overflow-y-auto", children: updateInfo.changelog.map((entry) => (_jsxs("div", { className: "flex items-baseline gap-3 text-sm", children: [_jsx("span", { className: "font-mono text-xs text-gray-500 shrink-0 w-14", children: entry.version }), _jsx("span", { className: "text-xs text-gray-400 shrink-0 w-20", children: entry.date }), _jsx("span", { className: "text-gray-700", children: entry.summary })] }, entry.version))) })] }))] })), prResult && (_jsx("div", { className: "rounded-lg border border-green-200 bg-green-50 p-4", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx(GitPullRequest, { className: "w-5 h-5 text-green-600 mt-0.5 shrink-0" }), _jsxs("div", { children: [_jsx("h3", { className: "text-sm font-semibold text-green-900", children: "Pull Request Created" }), _jsxs("p", { className: "text-sm text-green-700 mt-1", children: ["PR #", prResult.prNumber, " has been created on your repository. Review and merge it to apply the update, then run", ' ', _jsx("code", { className: "text-xs font-mono bg-green-100 px-1 rounded", children: "npx prisma migrate deploy" }), "."] }), _jsxs("a", { href: prResult.prUrl, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1.5 mt-2 text-sm font-medium text-green-700 hover:text-green-800", children: [_jsx(ExternalLink, { className: "w-4 h-4" }), "View Pull Request"] })] })] }) })), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsx("h3", { className: "text-sm font-semibold text-gray-900 mb-1", children: "GitHub Integration" }), _jsx("p", { className: "text-xs text-gray-500 mb-4", children: "Connect your repository to enable one-click update PRs. Credentials are encrypted at rest (AES-256-GCM)." }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Repository" }), _jsx("input", { type: "text", value: ghRepo, onChange: (e) => setGhRepo(e.target.value), placeholder: "owner/repo", className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" }), _jsxs("p", { className: "mt-1 text-xs text-gray-500", children: ["e.g.", ' ', _jsx("code", { className: "font-mono bg-gray-100 px-1 rounded", children: "actuate-media/my-client-site" })] })] }), _jsxs("div", { children: [_jsxs("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: ["GitHub Token", updateInfo?.hasGithubToken && !ghToken && (_jsx("span", { className: "ml-2 text-xs font-normal text-green-600", children: "Saved" }))] }), _jsx("div", { className: "flex gap-2", children: _jsxs("div", { className: "relative flex-1", children: [_jsx("input", { type: showGhToken ? 'text' : 'password', value: ghToken, onChange: (e) => setGhToken(e.target.value), placeholder: updateInfo?.hasGithubToken ? '••••••••••••••••' : 'ghp_... or github_pat_...', className: "w-full rounded-lg border border-gray-300 px-3 py-2 pr-10 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" }), _jsx("button", { type: "button", onClick: () => setShowGhToken(!showGhToken), className: "absolute right-2 top-1/2 -translate-y-1/2 p-1 text-gray-400 hover:text-gray-600", children: showGhToken ? _jsx(EyeOff, { className: "w-4 h-4" }) : _jsx(Eye, { className: "w-4 h-4" }) })] }) }), _jsxs("p", { className: "mt-1 text-xs text-gray-500", children: ["Needs ", _jsx("code", { className: "font-mono bg-gray-100 px-1 rounded", children: "repo" }), " scope. Create at", ' ', _jsx("a", { href: "https://github.com/settings/tokens", target: "_blank", rel: "noopener noreferrer", className: "text-blue-600 hover:underline", children: "github.com/settings/tokens" })] })] }), _jsx("button", { type: "button", onClick: saveGitHubConfig, disabled: savingConfig || (!ghToken && !ghRepo), className: "flex items-center gap-2 rounded-lg bg-gray-900 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-gray-800 disabled:opacity-50", children: savingConfig ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "w-4 h-4 animate-spin" }), "Encrypting & Saving..."] })) : ('Save Configuration') })] })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-gray-50 p-4", children: [_jsx("h3", { className: "text-sm font-semibold text-gray-700 mb-2", children: "How Updates Work" }), _jsxs("ul", { className: "space-y-1.5 text-xs text-gray-600", children: [_jsxs("li", { className: "flex items-start gap-2", children: [_jsx("span", { className: "w-4 h-4 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5", children: "1" }), _jsx("span", { children: "Click \"Check for Updates\" to see if a new version is available on npm." })] }), _jsxs("li", { className: "flex items-start gap-2", children: [_jsx("span", { className: "w-4 h-4 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5", children: "2" }), _jsx("span", { children: "Add your GitHub token and repository above. They're encrypted at rest \u2014 never stored in plaintext." })] }), _jsxs("li", { className: "flex items-start gap-2", children: [_jsx("span", { className: "w-4 h-4 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5", children: "3" }), _jsx("span", { children: "Click \"Create Update PR\" to open a pull request that bumps your CMS packages automatically." })] }), _jsxs("li", { className: "flex items-start gap-2", children: [_jsx("span", { className: "w-4 h-4 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5", children: "4" }), _jsxs("span", { children: ["Review and merge the PR, then deploy. Database migrations run automatically via", ' ', _jsx("code", { className: "font-mono bg-gray-200 px-1 rounded", children: "prisma migrate deploy" }), "."] })] })] })] })] }));
|
|
256
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-sm font-semibold text-gray-900", children: "Actuate CMS" }), _jsxs("p", { className: "mt-1 text-sm text-gray-600", children: ["Current version:", ' ', _jsx("span", { className: "font-mono font-medium", children: updateInfo?.current ?? '...' })] })] }), _jsxs("button", { type: "button", onClick: checkForUpdates, disabled: checking, className: "flex items-center gap-2 rounded-lg border border-gray-300 px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-50 disabled:opacity-50", children: [_jsx(RefreshCw, { className: `h-4 w-4 ${checking ? 'animate-spin' : ''}` }), checking ? 'Checking...' : 'Check for Updates'] })] }) }), checkError && (_jsxs("div", { className: "flex items-center gap-3 rounded-lg border border-red-200 bg-red-50 p-3", children: [_jsx(AlertTriangle, { className: "h-5 w-5 shrink-0 text-red-600" }), _jsx("span", { className: "flex-1 text-sm text-red-800", children: checkError }), _jsx("button", { onClick: checkForUpdates, className: "rounded-lg border border-red-300 px-3 py-1 text-sm text-red-700 transition-colors hover:bg-red-100", children: "Retry" })] })), hasChecked && updateInfo && !updateInfo.updateAvailable && !checkError && (_jsxs("div", { className: "flex items-center gap-3 rounded-lg border border-green-200 bg-green-50 p-4", children: [_jsx(CheckCircle2, { className: "h-6 w-6 shrink-0 text-green-600" }), _jsxs("div", { children: [_jsx("h3", { className: "text-sm font-semibold text-green-900", children: "You're up to date!" }), _jsxs("p", { className: "mt-0.5 text-sm text-green-700", children: ["Actuate CMS ", _jsx("span", { className: "font-mono", children: updateInfo.current }), " is the latest version."] })] })] })), updateInfo?.updateAvailable && (_jsxs(_Fragment, { children: [_jsxs("div", { className: `rounded-lg border p-4 ${severityColors[updateInfo.severity ?? 'patch']?.border ?? 'border-blue-200'} ${severityColors[updateInfo.severity ?? 'patch']?.bg ?? 'bg-blue-50'}`, children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsx(ArrowUpCircle, { className: `mt-0.5 h-6 w-6 shrink-0 ${severityColors[updateInfo.severity ?? 'patch']?.text ?? 'text-blue-700'}` }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("h3", { className: `text-sm font-semibold ${severityColors[updateInfo.severity ?? 'patch']?.text ?? 'text-blue-700'}`, children: "Update Available" }), _jsx("span", { className: `inline-flex items-center rounded px-2 py-0.5 text-xs font-medium ${severityColors[updateInfo.severity ?? 'patch']?.bg ?? 'bg-blue-50'} ${severityColors[updateInfo.severity ?? 'patch']?.text ?? 'text-blue-700'} border ${severityColors[updateInfo.severity ?? 'patch']?.border ?? 'border-blue-200'}`, children: severityColors[updateInfo.severity ?? 'patch']?.label ?? 'Update' })] }), _jsxs("p", { className: "mt-1 text-sm", style: { color: 'inherit' }, children: [_jsx("span", { className: "font-mono", children: updateInfo.current }), " \u2192", ' ', _jsx("span", { className: "font-mono font-semibold", children: updateInfo.latest }), updateInfo.releaseDate && (_jsxs("span", { className: "ml-2 text-xs opacity-70", children: ["Released ", updateInfo.releaseDate] }))] })] })] }), _jsxs("div", { className: "mt-4 flex items-center gap-3", children: [_jsx("button", { type: "button", onClick: applyUpdate, disabled: applying, className: "flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700 disabled:opacity-50", children: applying ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), "Creating PR..."] })) : (_jsxs(_Fragment, { children: [_jsx(GitPullRequest, { className: "h-4 w-4" }), "Create Update PR"] })) }), _jsx("span", { className: "text-xs text-gray-500", children: "Opens a pull request on your repository" })] }), updateInfo.updateCommand && (_jsxs("div", { className: "mt-3 rounded border border-gray-200 bg-white p-3", children: [_jsx("p", { className: "mb-1 text-xs text-gray-500", children: "Or update manually:" }), _jsx("code", { className: "block rounded bg-gray-50 px-2 py-1.5 font-mono text-xs text-gray-800 select-all", children: updateInfo.updateCommand })] }))] }), updateInfo.changelog && updateInfo.changelog.length > 0 && (_jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsx("h3", { className: "mb-3 text-sm font-semibold text-gray-900", children: "Changelog" }), _jsx("div", { className: "max-h-64 space-y-2 overflow-y-auto", children: updateInfo.changelog.map((entry) => (_jsxs("div", { className: "flex items-baseline gap-3 text-sm", children: [_jsx("span", { className: "w-14 shrink-0 font-mono text-xs text-gray-500", children: entry.version }), _jsx("span", { className: "w-20 shrink-0 text-xs text-gray-400", children: entry.date }), _jsx("span", { className: "text-gray-700", children: entry.summary })] }, entry.version))) })] }))] })), prResult && (_jsx("div", { className: "rounded-lg border border-green-200 bg-green-50 p-4", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx(GitPullRequest, { className: "mt-0.5 h-5 w-5 shrink-0 text-green-600" }), _jsxs("div", { children: [_jsx("h3", { className: "text-sm font-semibold text-green-900", children: "Pull Request Created" }), _jsxs("p", { className: "mt-1 text-sm text-green-700", children: ["PR #", prResult.prNumber, " has been created on your repository. Review and merge it to apply the update, then run", ' ', _jsx("code", { className: "rounded bg-green-100 px-1 font-mono text-xs", children: "npx prisma migrate deploy" }), "."] }), _jsxs("a", { href: prResult.prUrl, target: "_blank", rel: "noopener noreferrer", className: "mt-2 inline-flex items-center gap-1.5 text-sm font-medium text-green-700 hover:text-green-800", children: [_jsx(ExternalLink, { className: "h-4 w-4" }), "View Pull Request"] })] })] }) })), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4", children: [_jsx("h3", { className: "mb-1 text-sm font-semibold text-gray-900", children: "GitHub Integration" }), _jsx("p", { className: "mb-4 text-xs text-gray-500", children: "Connect your repository to enable one-click update PRs. Credentials are encrypted at rest (AES-256-GCM)." }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { children: [_jsx("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: "Repository" }), _jsx("input", { type: "text", value: ghRepo, onChange: (e) => setGhRepo(e.target.value), placeholder: "owner/repo", className: "w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none" }), _jsxs("p", { className: "mt-1 text-xs text-gray-500", children: ["e.g.", ' ', _jsx("code", { className: "rounded bg-gray-100 px-1 font-mono", children: "actuate-media/my-client-site" })] })] }), _jsxs("div", { children: [_jsxs("label", { className: "mb-1 block text-sm font-medium text-gray-700", children: ["GitHub Token", updateInfo?.hasGithubToken && !ghToken && (_jsx("span", { className: "ml-2 text-xs font-normal text-green-600", children: "Saved" }))] }), _jsx("div", { className: "flex gap-2", children: _jsxs("div", { className: "relative flex-1", children: [_jsx("input", { type: showGhToken ? 'text' : 'password', value: ghToken, onChange: (e) => setGhToken(e.target.value), placeholder: updateInfo?.hasGithubToken ? '••••••••••••••••' : 'ghp_... or github_pat_...', className: "w-full rounded-lg border border-gray-300 px-3 py-2 pr-10 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none" }), _jsx("button", { type: "button", onClick: () => setShowGhToken(!showGhToken), className: "absolute top-1/2 right-2 -translate-y-1/2 p-1 text-gray-400 hover:text-gray-600", children: showGhToken ? _jsx(EyeOff, { className: "h-4 w-4" }) : _jsx(Eye, { className: "h-4 w-4" }) })] }) }), _jsxs("p", { className: "mt-1 text-xs text-gray-500", children: ["Needs ", _jsx("code", { className: "rounded bg-gray-100 px-1 font-mono", children: "repo" }), " scope. Create at", ' ', _jsx("a", { href: "https://github.com/settings/tokens", target: "_blank", rel: "noopener noreferrer", className: "text-blue-600 hover:underline", children: "github.com/settings/tokens" })] })] }), _jsx("button", { type: "button", onClick: saveGitHubConfig, disabled: savingConfig || (!ghToken && !ghRepo), className: "flex items-center gap-2 rounded-lg bg-gray-900 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-gray-800 disabled:opacity-50", children: savingConfig ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), "Encrypting & Saving..."] })) : ('Save Configuration') })] })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-gray-50 p-4", children: [_jsx("h3", { className: "mb-2 text-sm font-semibold text-gray-700", children: "How Updates Work" }), _jsxs("ul", { className: "space-y-1.5 text-xs text-gray-600", children: [_jsxs("li", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-blue-100 text-[10px] font-bold text-blue-600", children: "1" }), _jsx("span", { children: "Click \"Check for Updates\" to see if a new version is available on npm." })] }), _jsxs("li", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-blue-100 text-[10px] font-bold text-blue-600", children: "2" }), _jsx("span", { children: "Add your GitHub token and repository above. They're encrypted at rest \u2014 never stored in plaintext." })] }), _jsxs("li", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-blue-100 text-[10px] font-bold text-blue-600", children: "3" }), _jsx("span", { children: "Click \"Create Update PR\" to open a pull request that bumps your CMS packages automatically." })] }), _jsxs("li", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-blue-100 text-[10px] font-bold text-blue-600", children: "4" }), _jsxs("span", { children: ["Review and merge the PR, then deploy. Database migrations run automatically via", ' ', _jsx("code", { className: "rounded bg-gray-200 px-1 font-mono", children: "prisma migrate deploy" }), "."] })] })] })] })] }));
|
|
256
257
|
}
|
|
257
258
|
//# sourceMappingURL=Settings.js.map
|