@actuate-media/cms-admin 0.67.0 → 0.69.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/CHANGELOG.md +28 -0
- package/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js +59 -15
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/hooks/useIdleLogout.test.d.ts +2 -0
- package/dist/__tests__/hooks/useIdleLogout.test.d.ts.map +1 -0
- package/dist/__tests__/hooks/useIdleLogout.test.js +119 -0
- package/dist/__tests__/hooks/useIdleLogout.test.js.map +1 -0
- package/dist/__tests__/lib/post-types-service.test.d.ts +2 -0
- package/dist/__tests__/lib/post-types-service.test.d.ts.map +1 -0
- package/dist/__tests__/lib/post-types-service.test.js +88 -0
- package/dist/__tests__/lib/post-types-service.test.js.map +1 -0
- package/dist/__tests__/router/query-string.test.d.ts +2 -0
- package/dist/__tests__/router/query-string.test.d.ts.map +1 -0
- package/dist/__tests__/router/query-string.test.js +44 -0
- package/dist/__tests__/router/query-string.test.js.map +1 -0
- package/dist/__tests__/views/collection-list-nav.render.test.js +43 -5
- package/dist/__tests__/views/collection-list-nav.render.test.js.map +1 -1
- package/dist/__tests__/views/forms-list.render.test.js +25 -6
- package/dist/__tests__/views/forms-list.render.test.js.map +1 -1
- package/dist/__tests__/views/page-builder-sections-panel.render.test.d.ts +2 -0
- package/dist/__tests__/views/page-builder-sections-panel.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/page-builder-sections-panel.render.test.js +121 -0
- package/dist/__tests__/views/page-builder-sections-panel.render.test.js.map +1 -0
- package/dist/__tests__/views/page-templates.render.test.d.ts +2 -0
- package/dist/__tests__/views/page-templates.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/page-templates.render.test.js +105 -0
- package/dist/__tests__/views/page-templates.render.test.js.map +1 -0
- package/dist/__tests__/views/post-type-editor.render.test.d.ts +2 -0
- package/dist/__tests__/views/post-type-editor.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/post-type-editor.render.test.js +102 -0
- package/dist/__tests__/views/post-type-editor.render.test.js.map +1 -0
- package/dist/__tests__/views/security-settings.render.test.js +85 -4
- package/dist/__tests__/views/security-settings.render.test.js.map +1 -1
- package/dist/__tests__/views/seo-content-pane.render.test.d.ts +2 -0
- package/dist/__tests__/views/seo-content-pane.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/seo-content-pane.render.test.js +117 -0
- package/dist/__tests__/views/seo-content-pane.render.test.js.map +1 -0
- package/dist/__tests__/views/users-selected-deeplink.render.test.d.ts +2 -0
- package/dist/__tests__/views/users-selected-deeplink.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/users-selected-deeplink.render.test.js +91 -0
- package/dist/__tests__/views/users-selected-deeplink.render.test.js.map +1 -0
- package/dist/actuate-admin.css +1 -1
- package/dist/components/collections/EntryDetailPane.d.ts +10 -0
- package/dist/components/collections/EntryDetailPane.d.ts.map +1 -0
- package/dist/components/collections/EntryDetailPane.js +92 -0
- package/dist/components/collections/EntryDetailPane.js.map +1 -0
- package/dist/components/forms/FieldsPanel.js +1 -1
- package/dist/components/forms/FieldsPanel.js.map +1 -1
- package/dist/components/forms/FormSchemaPane.d.ts +8 -0
- package/dist/components/forms/FormSchemaPane.d.ts.map +1 -0
- package/dist/components/forms/FormSchemaPane.js +95 -0
- package/dist/components/forms/FormSchemaPane.js.map +1 -0
- package/dist/components/seo/SeoEditorPane.d.ts +9 -0
- package/dist/components/seo/SeoEditorPane.d.ts.map +1 -0
- package/dist/components/seo/SeoEditorPane.js +175 -0
- package/dist/components/seo/SeoEditorPane.js.map +1 -0
- package/dist/components/ui/Badge.d.ts +1 -1
- package/dist/components/ui/Button.d.ts +1 -1
- package/dist/components/ui/StatusBadge.d.ts +1 -1
- package/dist/hooks/useIdleLogout.d.ts +33 -0
- package/dist/hooks/useIdleLogout.d.ts.map +1 -0
- package/dist/hooks/useIdleLogout.js +99 -0
- package/dist/hooks/useIdleLogout.js.map +1 -0
- package/dist/lib/api.d.ts +1 -0
- package/dist/lib/api.d.ts.map +1 -1
- package/dist/lib/api.js +7 -1
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/post-types-service.d.ts +44 -0
- package/dist/lib/post-types-service.d.ts.map +1 -0
- package/dist/lib/post-types-service.js +83 -0
- package/dist/lib/post-types-service.js.map +1 -0
- package/dist/router/index.d.ts +7 -0
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +23 -4
- package/dist/router/index.js.map +1 -1
- package/dist/views/CollectionList.d.ts.map +1 -1
- package/dist/views/CollectionList.js +23 -4
- package/dist/views/CollectionList.js.map +1 -1
- package/dist/views/Forms.d.ts.map +1 -1
- package/dist/views/Forms.js +25 -21
- package/dist/views/Forms.js.map +1 -1
- package/dist/views/MediaBrowser.d.ts +4 -1
- package/dist/views/MediaBrowser.d.ts.map +1 -1
- package/dist/views/MediaBrowser.js +27 -5
- package/dist/views/MediaBrowser.js.map +1 -1
- package/dist/views/Posts/PostTypeEditor.d.ts +16 -0
- package/dist/views/Posts/PostTypeEditor.d.ts.map +1 -0
- package/dist/views/Posts/PostTypeEditor.js +159 -0
- package/dist/views/Posts/PostTypeEditor.js.map +1 -0
- package/dist/views/Posts/PostTypesView.d.ts +3 -2
- package/dist/views/Posts/PostTypesView.d.ts.map +1 -1
- package/dist/views/Posts/PostTypesView.js +1 -1
- package/dist/views/Posts/PostTypesView.js.map +1 -1
- package/dist/views/SEO.d.ts.map +1 -1
- package/dist/views/SEO.js +2 -1
- package/dist/views/SEO.js.map +1 -1
- package/dist/views/Settings.d.ts +4 -1
- package/dist/views/Settings.d.ts.map +1 -1
- package/dist/views/Settings.js +4 -3
- package/dist/views/Settings.js.map +1 -1
- package/dist/views/Users.d.ts +4 -1
- package/dist/views/Users.d.ts.map +1 -1
- package/dist/views/Users.js +22 -2
- package/dist/views/Users.js.map +1 -1
- package/dist/views/page-builder/BuilderSectionsPanel.d.ts +20 -0
- package/dist/views/page-builder/BuilderSectionsPanel.d.ts.map +1 -0
- package/dist/views/page-builder/BuilderSectionsPanel.js +105 -0
- package/dist/views/page-builder/BuilderSectionsPanel.js.map +1 -0
- package/dist/views/page-builder/PageBuilder.d.ts.map +1 -1
- package/dist/views/page-builder/PageBuilder.js +51 -2
- package/dist/views/page-builder/PageBuilder.js.map +1 -1
- package/dist/views/page-builder/PageTemplates.d.ts.map +1 -1
- package/dist/views/page-builder/PageTemplates.js +113 -6
- package/dist/views/page-builder/PageTemplates.js.map +1 -1
- package/dist/views/seo/ContentTab.d.ts.map +1 -1
- package/dist/views/seo/ContentTab.js +22 -6
- package/dist/views/seo/ContentTab.js.map +1 -1
- package/dist/views/settings/AccessProtectionCard.d.ts +5 -1
- package/dist/views/settings/AccessProtectionCard.d.ts.map +1 -1
- package/dist/views/settings/AccessProtectionCard.js +13 -10
- package/dist/views/settings/AccessProtectionCard.js.map +1 -1
- package/dist/views/settings/SecurityTab.d.ts.map +1 -1
- package/dist/views/settings/SecurityTab.js +5 -4
- package/dist/views/settings/SecurityTab.js.map +1 -1
- package/dist/views/settings/SessionPolicyCard.d.ts +1 -2
- package/dist/views/settings/SessionPolicyCard.d.ts.map +1 -1
- package/dist/views/settings/SessionPolicyCard.js +12 -5
- package/dist/views/settings/SessionPolicyCard.js.map +1 -1
- package/dist/views/settings/useSecuritySettings.d.ts +13 -1
- package/dist/views/settings/useSecuritySettings.d.ts.map +1 -1
- package/dist/views/settings/useSecuritySettings.js +47 -4
- package/dist/views/settings/useSecuritySettings.js.map +1 -1
- package/package.json +2 -2
- package/src/AdminRoot.tsx +88 -18
- package/src/__tests__/hooks/useIdleLogout.test.tsx +188 -0
- package/src/__tests__/lib/post-types-service.test.ts +112 -0
- package/src/__tests__/router/query-string.test.ts +54 -0
- package/src/__tests__/views/collection-list-nav.render.test.tsx +51 -5
- package/src/__tests__/views/forms-list.render.test.tsx +34 -6
- package/src/__tests__/views/page-builder-sections-panel.render.test.tsx +138 -0
- package/src/__tests__/views/page-templates.render.test.tsx +142 -0
- package/src/__tests__/views/post-type-editor.render.test.tsx +116 -0
- package/src/__tests__/views/security-settings.render.test.tsx +111 -4
- package/src/__tests__/views/seo-content-pane.render.test.tsx +142 -0
- package/src/__tests__/views/users-selected-deeplink.render.test.tsx +102 -0
- package/src/components/collections/EntryDetailPane.tsx +182 -0
- package/src/components/forms/FieldsPanel.tsx +3 -1
- package/src/components/forms/FormSchemaPane.tsx +192 -0
- package/src/components/seo/SeoEditorPane.tsx +533 -0
- package/src/hooks/useIdleLogout.ts +129 -0
- package/src/lib/api.ts +8 -2
- package/src/lib/post-types-service.ts +129 -0
- package/src/router/index.ts +25 -4
- package/src/views/CollectionList.tsx +158 -110
- package/src/views/Forms.tsx +154 -126
- package/src/views/MediaBrowser.tsx +28 -5
- package/src/views/Posts/PostTypeEditor.tsx +568 -0
- package/src/views/Posts/PostTypesView.tsx +5 -4
- package/src/views/SEO.tsx +2 -1
- package/src/views/Settings.tsx +7 -2
- package/src/views/Users.tsx +23 -2
- package/src/views/page-builder/BuilderSectionsPanel.tsx +304 -0
- package/src/views/page-builder/PageBuilder.tsx +69 -2
- package/src/views/page-builder/PageTemplates.tsx +340 -57
- package/src/views/seo/ContentTab.tsx +113 -80
- package/src/views/settings/AccessProtectionCard.tsx +83 -53
- package/src/views/settings/SecurityTab.tsx +25 -3
- package/src/views/settings/SessionPolicyCard.tsx +45 -9
- package/src/views/settings/useSecuritySettings.ts +78 -22
- package/dist/components/forms/FormSchemaDrawer.d.ts +0 -9
- package/dist/components/forms/FormSchemaDrawer.d.ts.map +0 -1
- package/dist/components/forms/FormSchemaDrawer.js +0 -99
- package/dist/components/forms/FormSchemaDrawer.js.map +0 -1
- package/dist/components/seo/SeoEditorDrawer.d.ts +0 -8
- package/dist/components/seo/SeoEditorDrawer.d.ts.map +0 -1
- package/dist/components/seo/SeoEditorDrawer.js +0 -172
- package/dist/components/seo/SeoEditorDrawer.js.map +0 -1
- package/src/components/forms/FormSchemaDrawer.tsx +0 -192
- package/src/components/seo/SeoEditorDrawer.tsx +0 -529
|
@@ -1,13 +1,120 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useMemo, useState } from 'react';
|
|
4
|
+
import { AlertTriangle, Layers, Pencil, RefreshCw, Trash2 } from 'lucide-react';
|
|
5
|
+
import { toast } from 'sonner';
|
|
6
|
+
import { cmsApi } from '../../lib/api.js';
|
|
4
7
|
import { useApiData } from '../../lib/useApiData.js';
|
|
8
|
+
import { ConfirmDialog } from '../../components/ui/ConfirmDialog.js';
|
|
9
|
+
import { EmptyState } from '../../components/ui/EmptyState.js';
|
|
10
|
+
import { FilterPill } from '../../components/ui/FilterPill.js';
|
|
11
|
+
import { Input } from '../../components/ui/Input.js';
|
|
12
|
+
import { Modal } from '../../components/ui/Modal.js';
|
|
13
|
+
import { Button } from '../../components/ui/Button.js';
|
|
14
|
+
import { RowActions, RowActionButton } from '../../components/ui/RowActions.js';
|
|
15
|
+
import { SearchInput } from '../../components/ui/SearchInput.js';
|
|
16
|
+
import { Select } from '../../components/ui/Select.js';
|
|
17
|
+
import { Skeleton } from '../../components/ui/Skeleton.js';
|
|
18
|
+
const CATEGORIES = {
|
|
19
|
+
content: { label: 'Content', dot: 'bg-[var(--acc)]', text: 'text-[var(--acc)]' },
|
|
20
|
+
landing: { label: 'Landing', dot: 'bg-[var(--suc)]', text: 'text-[var(--suc)]' },
|
|
21
|
+
utility: { label: 'Utility', dot: 'bg-[var(--warn)]', text: 'text-[var(--warn)]' },
|
|
22
|
+
};
|
|
23
|
+
function categoryOf(template) {
|
|
24
|
+
const cat = template.category;
|
|
25
|
+
return cat === 'landing' || cat === 'utility' ? cat : 'content';
|
|
26
|
+
}
|
|
27
|
+
function sectionCountOf(template) {
|
|
28
|
+
return Array.isArray(template.tree?.children) ? template.tree.children.length : 0;
|
|
29
|
+
}
|
|
5
30
|
export function PageTemplates({ onNavigate }) {
|
|
6
31
|
const { data, loading, error, refetch } = useApiData('/page-templates');
|
|
7
|
-
const templates = data ?? [];
|
|
8
|
-
|
|
9
|
-
|
|
32
|
+
const templates = useMemo(() => data ?? [], [data]);
|
|
33
|
+
const [search, setSearch] = useState('');
|
|
34
|
+
const [category, setCategory] = useState('all');
|
|
35
|
+
const [editTarget, setEditTarget] = useState(null);
|
|
36
|
+
const [deleteTarget, setDeleteTarget] = useState(null);
|
|
37
|
+
const filtered = useMemo(() => {
|
|
38
|
+
const q = search.trim().toLowerCase();
|
|
39
|
+
return templates.filter((t) => {
|
|
40
|
+
const matchesQuery = !q ||
|
|
41
|
+
(t.name ?? '').toLowerCase().includes(q) ||
|
|
42
|
+
(t.description ?? '').toLowerCase().includes(q);
|
|
43
|
+
const matchesCategory = category === 'all' || categoryOf(t) === category;
|
|
44
|
+
return matchesQuery && matchesCategory;
|
|
45
|
+
});
|
|
46
|
+
}, [templates, search, category]);
|
|
47
|
+
async function confirmDelete() {
|
|
48
|
+
if (!deleteTarget)
|
|
49
|
+
return;
|
|
50
|
+
const res = await cmsApi(`/page-templates/${deleteTarget.id}`, { method: 'DELETE' });
|
|
51
|
+
if (res.error) {
|
|
52
|
+
toast.error(res.error);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
toast.success('Template deleted');
|
|
56
|
+
refetch();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return (_jsxs("div", { className: "p-4 pr-8", children: [error && (_jsxs("div", { className: "mb-4 flex items-center gap-3 rounded-[var(--r)] border border-[var(--bdr)] bg-[var(--card)] p-3", children: [_jsx(AlertTriangle, { className: "h-5 w-5 shrink-0 text-[var(--muted)]", "aria-hidden": true }), _jsx("span", { className: "flex-1 text-sm text-[var(--txt)]", children: error }), _jsx("button", { type: "button", onClick: refetch, className: "rounded-md border border-[var(--bdr)] px-3 py-1 text-sm text-[var(--txt)] hover:bg-[var(--acc-l)]", children: "Retry" })] })), _jsxs("div", { className: "mb-4 flex items-center justify-between gap-4", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-foreground mb-1", children: "Page Templates" }), _jsxs("p", { className: "text-sm text-[var(--sub)]", children: [templates.length, " saved template", templates.length === 1 ? '' : 's'] })] }), _jsxs("button", { type: "button", onClick: refetch, className: "inline-flex items-center gap-2 rounded-md border border-[var(--bdr)] px-3 py-2 text-sm font-medium text-[var(--txt)] transition-colors duration-[var(--motion-fast)] hover:border-[var(--acc-m)] hover:bg-[var(--acc-l)] hover:text-[var(--acc)]", children: [_jsx(RefreshCw, { className: "h-4 w-4", "aria-hidden": true }), "Refresh"] })] }), !loading && templates.length > 0 && (_jsxs("div", { className: "mb-4 flex flex-wrap items-center gap-2", children: [_jsx("div", { className: "max-w-xs min-w-52 flex-1", children: _jsx(SearchInput, { value: search, onChange: setSearch, placeholder: "Search templates\u2026" }) }), _jsxs("div", { className: "flex gap-1.5", role: "group", "aria-label": "Filter templates by category", children: [_jsx(FilterPill, { active: category === 'all', onClick: () => setCategory('all'), children: "All" }), Object.keys(CATEGORIES).map((cat) => (_jsx(FilterPill, { active: category === cat, onClick: () => setCategory(cat), children: CATEGORIES[cat].label }, cat)))] }), _jsxs("span", { className: "ml-auto text-[12.5px] whitespace-nowrap text-[var(--muted)]", children: [filtered.length, " of ", templates.length] })] })), loading ? (_jsx("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3", role: "status", "aria-live": "polite", "aria-label": "Loading page templates", children: Array.from({ length: 6 }, (_, i) => (_jsx(Skeleton, { variant: "card" }, i))) })) : templates.length === 0 ? (_jsx(EmptyState, { icon: _jsx(Layers, { size: 24, "aria-hidden": true }), title: "No page templates yet", description: "Built-in templates are seeded by the CMS when the templates API is available.", actionLabel: "View Saved Sections", onAction: () => onNavigate?.('/saved-sections') })) : filtered.length === 0 ? (_jsx("p", { className: "py-12 text-center text-sm text-[var(--muted)]", children: "No templates match your search." })) : (_jsx("ul", { className: "grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3", "aria-label": "Page templates", children: filtered.map((template) => (_jsx(TemplateCard, { template: template, onEdit: () => setEditTarget(template), onDelete: () => setDeleteTarget(template) }, template.id))) })), _jsx(TemplateEditModal, { template: editTarget, onClose: () => setEditTarget(null), onSaved: () => {
|
|
60
|
+
setEditTarget(null);
|
|
61
|
+
refetch();
|
|
62
|
+
} }), _jsx(ConfirmDialog, { open: deleteTarget !== null, onClose: () => setDeleteTarget(null), onConfirm: () => void confirmDelete(), title: "Delete template?", description: `"${deleteTarget?.name ?? 'This template'}" will be permanently removed. Pages already created from it are not affected.`, confirmLabel: "Delete", destructive: true })] }));
|
|
63
|
+
}
|
|
64
|
+
function TemplateCard({ template, onEdit, onDelete, }) {
|
|
65
|
+
const cat = CATEGORIES[categoryOf(template)];
|
|
66
|
+
const sections = sectionCountOf(template);
|
|
67
|
+
const name = template.name ?? 'Untitled template';
|
|
68
|
+
const updated = template.updatedAt ? new Date(template.updatedAt).toLocaleDateString() : null;
|
|
69
|
+
return (_jsxs("li", { className: "group relative flex flex-col overflow-hidden rounded-[var(--r)] border border-[var(--bdr)] bg-[var(--card)] shadow-[var(--shd)] transition-[border-color,box-shadow,transform] duration-[var(--motion-fast)] focus-within:border-[var(--acc-m)] focus-within:shadow-[var(--shd2)] hover:-translate-y-0.5 hover:border-[var(--acc-m)] hover:shadow-[var(--shd2)] motion-reduce:hover:translate-y-0", children: [_jsx(TemplatePreview, { template: template, sections: sections }), _jsxs("div", { className: "flex flex-1 flex-col p-4 pb-0", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsx("h2", { className: "text-[15px] leading-snug font-medium text-[var(--txt)]", children: name }), template.builtIn && (_jsx("span", { className: "shrink-0 rounded-full border border-[var(--bdr)] bg-[var(--bg)] px-2 py-0.5 text-[10.5px] font-medium whitespace-nowrap text-[var(--sub)]", children: "Built-in" }))] }), _jsx("p", { className: "mt-2 min-h-10 text-[12.5px] leading-relaxed text-[var(--sub)]", children: template.description ?? 'No description provided.' }), _jsxs("div", { className: "relative mt-3 flex h-[46px] items-center justify-between border-t border-[var(--bdr)] py-2.5", children: [_jsxs("span", { className: `inline-flex items-center gap-1.5 text-[11.5px] font-medium ${cat.text}`, children: [_jsx("span", { "aria-hidden": true, className: `h-[7px] w-[7px] shrink-0 rounded-full ${cat.dot}` }), cat.label] }), _jsxs("span", { className: "text-[11.5px] text-[var(--muted)] transition-opacity duration-[var(--motion-fast)] group-focus-within:opacity-0 group-hover:opacity-0", children: [sections, " section", sections === 1 ? '' : 's', updated ? ` · ${updated}` : ''] }), !template.builtIn && (_jsxs(RowActions, { className: "absolute top-1/2 right-0 -translate-y-1/2", children: [_jsx(RowActionButton, { "aria-label": `Edit ${name}`, onClick: onEdit, children: _jsx(Pencil, { size: 14, "aria-hidden": true }) }), _jsx(RowActionButton, { "aria-label": `Delete ${name}`, danger: true, onClick: onDelete, children: _jsx(Trash2, { size: 14, "aria-hidden": true }) })] }))] })] })] }));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Card preview area. Uses the stored thumbnail when one exists; otherwise
|
|
73
|
+
* renders an honest wireframe placeholder built from the template's section
|
|
74
|
+
* count (a hero band plus one bar per section) — no fake screenshots.
|
|
75
|
+
*/
|
|
76
|
+
function TemplatePreview({ template, sections }) {
|
|
77
|
+
if (template.thumbnail) {
|
|
78
|
+
return (_jsx("div", { className: "h-28 shrink-0 overflow-hidden border-b border-[var(--bdr)] bg-[var(--bg)]", children: _jsx("img", { src: template.thumbnail, alt: `Preview of ${template.name ?? 'template'}`, className: "h-full w-full object-cover" }) }));
|
|
79
|
+
}
|
|
80
|
+
const bars = Math.max(0, Math.min(sections - 1, 3));
|
|
81
|
+
return (_jsxs("div", { "aria-hidden": true, className: "flex h-28 shrink-0 flex-col gap-1.5 border-b border-[var(--bdr)] bg-[var(--bg)] p-3", children: [_jsx("div", { className: "h-9 rounded-[5px] bg-[var(--acc-l)]" }), Array.from({ length: bars }, (_, i) => (_jsx("div", { className: "h-3.5 rounded-[4px] bg-[var(--bdr)]", style: { width: `${88 - i * 14}%` } }, i)))] }));
|
|
82
|
+
}
|
|
83
|
+
function TemplateEditModal({ template, onClose, onSaved, }) {
|
|
84
|
+
// Keyed remount seeds the form state from the selected template each time
|
|
85
|
+
// the modal opens, without effect-based state syncing.
|
|
86
|
+
if (!template)
|
|
87
|
+
return null;
|
|
88
|
+
return (_jsx(TemplateEditForm, { template: template, onClose: onClose, onSaved: onSaved }, template.id));
|
|
89
|
+
}
|
|
90
|
+
function TemplateEditForm({ template, onClose, onSaved, }) {
|
|
91
|
+
const [name, setName] = useState(template.name ?? '');
|
|
92
|
+
const [description, setDescription] = useState(template.description ?? '');
|
|
93
|
+
const [category, setCategory] = useState(categoryOf(template));
|
|
94
|
+
const [saving, setSaving] = useState(false);
|
|
95
|
+
async function handleSave() {
|
|
96
|
+
if (!name.trim()) {
|
|
97
|
+
toast.error('Template name is required');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
setSaving(true);
|
|
101
|
+
const res = await cmsApi(`/page-templates/${template.id}`, {
|
|
102
|
+
method: 'PUT',
|
|
103
|
+
body: JSON.stringify({
|
|
104
|
+
name: name.trim(),
|
|
105
|
+
description: description.trim() || null,
|
|
106
|
+
category,
|
|
107
|
+
}),
|
|
108
|
+
});
|
|
109
|
+
setSaving(false);
|
|
110
|
+
if (res.error) {
|
|
111
|
+
toast.error(res.error);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
toast.success('Template updated');
|
|
115
|
+
onSaved();
|
|
116
|
+
}
|
|
10
117
|
}
|
|
11
|
-
return (
|
|
118
|
+
return (_jsx(Modal, { open: true, onClose: onClose, title: "Edit template", actions: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "ghost", onClick: onClose, disabled: saving, children: "Cancel" }), _jsx(Button, { variant: "primary", onClick: () => void handleSave(), loading: saving, children: "Save changes" })] }), children: _jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("label", { className: "flex flex-col gap-1.5", children: [_jsx("span", { className: "text-sm font-medium text-[var(--txt)]", children: "Name" }), _jsx(Input, { value: name, onChange: (e) => setName(e.target.value), placeholder: "Template name" })] }), _jsxs("label", { className: "flex flex-col gap-1.5", children: [_jsx("span", { className: "text-sm font-medium text-[var(--txt)]", children: "Description" }), _jsx("textarea", { value: description, onChange: (e) => setDescription(e.target.value), rows: 3, placeholder: "What this template is for\u2026", className: "w-full resize-y rounded-[var(--r-control)] border border-[var(--bdr)] bg-[var(--input-background)] px-3 py-2 text-sm text-[var(--txt)] placeholder:text-[var(--muted)] focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:outline-none" })] }), _jsxs("label", { className: "flex flex-col gap-1.5", children: [_jsx("span", { className: "text-sm font-medium text-[var(--txt)]", children: "Category" }), _jsx(Select, { value: category, onChange: (e) => setCategory(e.target.value), children: Object.keys(CATEGORIES).map((cat) => (_jsx("option", { value: cat, children: CATEGORIES[cat].label }, cat))) })] })] }) }));
|
|
12
119
|
}
|
|
13
120
|
//# sourceMappingURL=PageTemplates.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PageTemplates.js","sourceRoot":"","sources":["../../../src/views/page-builder/PageTemplates.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"PageTemplates.js","sourceRoot":"","sources":["../../../src/views/page-builder/PageTemplates.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContentTab.d.ts","sourceRoot":"","sources":["../../../src/views/seo/ContentTab.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ContentTab.d.ts","sourceRoot":"","sources":["../../../src/views/seo/ContentTab.tsx"],"names":[],"mappings":"AAsCA,wBAAgB,UAAU,CAAC,EACzB,iBAAiB,EACjB,eAAe,GAChB,EAAE;IACD,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAChC,2CAwJA"}
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Content SEO tab — pages/posts list with per-item SEO editing in an inline
|
|
5
|
+
* 380px inspector pane (design split-pane pattern; previously an overlay
|
|
6
|
+
* drawer). Row click toggles the pane; deep links
|
|
7
|
+
* (`?tab=content&entityType=…&entityId=…`) open it on mount.
|
|
8
|
+
*/
|
|
3
9
|
import { useMemo, useState } from 'react';
|
|
4
|
-
import { Search, FileText
|
|
10
|
+
import { Search, FileText } from 'lucide-react';
|
|
5
11
|
import { fetchSeoContentRecords, applyContentSeoQuery, } from '../../lib/seo-service.js';
|
|
6
12
|
import { SectionCard, SeoLoading, SeoEmptyState, SeoErrorState, ScorePill, SeoStatusBadge, } from '../../components/seo/primitives.js';
|
|
7
|
-
import {
|
|
13
|
+
import { InspectorPane, SplitPaneLayout, SplitPaneList } from '../../components/ui/InspectorPane.js';
|
|
14
|
+
import { SeoEditorPane } from '../../components/seo/SeoEditorPane.js';
|
|
8
15
|
import { useSeoResource } from './useSeoResource.js';
|
|
9
16
|
const FILTERS = [
|
|
10
17
|
{ id: 'all', label: 'All' },
|
|
@@ -16,12 +23,21 @@ export function ContentTab({ initialEntityType, initialEntityId, }) {
|
|
|
16
23
|
const { data, loading, error, refetch } = useSeoResource(fetchSeoContentRecords, []);
|
|
17
24
|
const [search, setSearch] = useState('');
|
|
18
25
|
const [filter, setFilter] = useState('all');
|
|
19
|
-
const [
|
|
26
|
+
const [selection, setSelection] = useState(initialEntityType && initialEntityId ? { type: initialEntityType, id: initialEntityId } : null);
|
|
20
27
|
const rows = useMemo(() => applyContentSeoQuery(data ?? [], { search, filter }), [data, search, filter]);
|
|
28
|
+
const toggleRow = (type, id) => {
|
|
29
|
+
setSelection((prev) => (prev?.type === type && prev.id === id ? null : { type, id }));
|
|
30
|
+
};
|
|
21
31
|
return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { className: "relative max-w-sm flex-1", children: [_jsx(Search, { className: "text-muted-foreground pointer-events-none absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2", "aria-hidden": true }), _jsx("input", { className: inputCls, placeholder: "Search content\u2026", value: search, onChange: (e) => setSearch(e.target.value), "aria-label": "Search content" })] }), _jsx("div", { className: "flex gap-1", role: "group", "aria-label": "Content type filter", children: FILTERS.map((f) => (_jsx("button", { type: "button", "aria-pressed": filter === f.id, onClick: () => setFilter(f.id), className: `focus-visible:ring-ring rounded-md px-3 py-1.5 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none ${filter === f.id
|
|
22
32
|
? 'bg-primary text-primary-foreground'
|
|
23
|
-
: 'text-muted-foreground hover:bg-accent hover:text-foreground'}`, children: f.label }, f.id))) })] }), _jsx(SectionCard, { children: loading ? (_jsx(SeoLoading, { label: "Loading content\u2026" })) : error ? (_jsx(SeoErrorState, { message: error, onRetry: refetch })) : rows.length === 0 ? (_jsx(SeoEmptyState, { icon: _jsx(FileText, { size: 24 }), title: search || filter !== 'all' ? 'No matching content' : 'No content yet', description: search || filter !== 'all'
|
|
24
|
-
|
|
25
|
-
|
|
33
|
+
: 'text-muted-foreground hover:bg-accent hover:text-foreground'}`, children: f.label }, f.id))) })] }), _jsxs(SplitPaneLayout, { className: "h-[calc(100vh-280px)] min-h-[440px]", children: [_jsx(SplitPaneList, { children: _jsx(SectionCard, { children: loading ? (_jsx(SeoLoading, { label: "Loading content\u2026" })) : error ? (_jsx(SeoErrorState, { message: error, onRetry: refetch })) : rows.length === 0 ? (_jsx(SeoEmptyState, { icon: _jsx(FileText, { size: 24 }), title: search || filter !== 'all' ? 'No matching content' : 'No content yet', description: search || filter !== 'all'
|
|
34
|
+
? 'Try adjusting your search or filter.'
|
|
35
|
+
: 'Publish pages or posts to manage their SEO here.' })) : (_jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full text-sm", "aria-label": "Content SEO", children: [_jsx("thead", { children: _jsxs("tr", { className: "text-muted-foreground border-border border-b text-left", children: [_jsx("th", { className: "py-2 pr-4 font-medium", children: "Title" }), _jsx("th", { className: "py-2 pr-4 font-medium", children: "Type" }), _jsx("th", { className: "py-2 pr-4 font-medium", children: "Meta" }), _jsx("th", { className: "py-2 text-right font-medium", children: "Score" })] }) }), _jsx("tbody", { className: "divide-border divide-y", children: rows.map((r) => {
|
|
36
|
+
const isSelected = selection?.type === r.entityType && selection.id === r.entityId;
|
|
37
|
+
return (_jsxs("tr", { className: `cursor-pointer transition-colors ${isSelected ? 'bg-[var(--acc-l)]' : 'hover:bg-accent/40'}`, onClick: () => toggleRow(r.entityType, r.entityId), children: [_jsx("td", { className: "py-2.5 pr-4", children: _jsxs("button", { type: "button", onClick: (e) => {
|
|
38
|
+
e.stopPropagation();
|
|
39
|
+
toggleRow(r.entityType, r.entityId);
|
|
40
|
+
}, "aria-expanded": isSelected, className: "text-left", children: [_jsx("span", { className: "text-foreground block font-medium", children: r.title }), _jsx("span", { className: "text-muted-foreground block truncate", children: r.url })] }) }), _jsx("td", { className: "py-2.5 pr-4 capitalize", children: _jsx("span", { className: "text-muted-foreground", children: r.entityType }) }), _jsx("td", { className: "py-2.5 pr-4", children: _jsxs("span", { className: "flex flex-wrap gap-1.5", children: [_jsx(SeoStatusBadge, { label: r.metaTitle ? 'Title' : 'No title', tone: r.metaTitle ? 'good' : 'critical' }), _jsx(SeoStatusBadge, { label: r.metaDescription ? 'Desc' : 'No desc', tone: r.metaDescription ? 'good' : 'critical' }), r.noindex && _jsx(SeoStatusBadge, { label: "No-index", tone: "fair" })] }) }), _jsx("td", { className: "py-2.5 text-right", children: _jsx(ScorePill, { score: r.seoScore }) })] }, `${r.entityType}-${r.entityId}`));
|
|
41
|
+
}) })] }) })) }) }), _jsx(InspectorPane, { open: selection !== null, width: 380, "aria-label": "SEO editor", children: selection && (_jsx(SeoEditorPane, { entityType: selection.type, entityId: selection.id, onClose: () => setSelection(null), onSaved: refetch }, `${selection.type}-${selection.id}`)) })] })] }));
|
|
26
42
|
}
|
|
27
43
|
//# sourceMappingURL=ContentTab.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContentTab.js","sourceRoot":"","sources":["../../../src/views/seo/ContentTab.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"ContentTab.js","sourceRoot":"","sources":["../../../src/views/seo/ContentTab.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { type IpAllowlistStatus, type SecretsStatus, type SecuritySourceMetadata } from '@actuate-media/cms-core/security-center';
|
|
2
|
-
|
|
2
|
+
import { type SecurityForm } from './useSecuritySettings.js';
|
|
3
|
+
export declare function AccessProtectionCard({ form, setField, canEdit, ipAllowlist, secrets, csrfEnabled, uploadValidation, sources, onNavigate, }: {
|
|
4
|
+
form: SecurityForm;
|
|
5
|
+
setField: <K extends keyof SecurityForm>(key: K, value: SecurityForm[K]) => void;
|
|
6
|
+
canEdit: boolean;
|
|
3
7
|
ipAllowlist: IpAllowlistStatus;
|
|
4
8
|
secrets: SecretsStatus;
|
|
5
9
|
csrfEnabled: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccessProtectionCard.d.ts","sourceRoot":"","sources":["../../../src/views/settings/AccessProtectionCard.tsx"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAC5B,MAAM,yCAAyC,CAAA;
|
|
1
|
+
{"version":3,"file":"AccessProtectionCard.d.ts","sourceRoot":"","sources":["../../../src/views/settings/AccessProtectionCard.tsx"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAC5B,MAAM,yCAAyC,CAAA;AAGhD,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEhF,wBAAgB,oBAAoB,CAAC,EACnC,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,WAAW,EACX,OAAO,EACP,WAAW,EACX,gBAAgB,EAChB,OAAO,EACP,UAAU,GACX,EAAE;IACD,IAAI,EAAE,YAAY,CAAA;IAClB,QAAQ,EAAE,CAAC,CAAC,SAAS,MAAM,YAAY,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IAChF,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,iBAAiB,CAAA;IAC9B,OAAO,EAAE,aAAa,CAAA;IACtB,WAAW,EAAE,OAAO,CAAA;IACpB,gBAAgB,EAAE,OAAO,CAAA;IACzB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAA;IAC/C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC,2CA0IA"}
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useId
|
|
4
|
-
import {
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useId } from 'react';
|
|
4
|
+
import { AlertTriangle } from 'lucide-react';
|
|
5
5
|
import { validateIpRange, } from '@actuate-media/cms-core/security-center';
|
|
6
|
-
import { SettingsSourceBadge } from './components.js';
|
|
6
|
+
import { SettingsSourceBadge, SettingsToggleRow } from './components.js';
|
|
7
7
|
import { SecurityCard, StatusRow } from './securityPrimitives.js';
|
|
8
|
-
|
|
8
|
+
import { parseAllowlistText } from './useSecuritySettings.js';
|
|
9
|
+
export function AccessProtectionCard({ form, setField, canEdit, ipAllowlist, secrets, csrfEnabled, uploadValidation, sources, onNavigate, }) {
|
|
9
10
|
const headingId = useId();
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
11
|
+
const allowlistId = useId();
|
|
12
|
+
const allowlistHintId = useId();
|
|
13
|
+
const entries = parseAllowlistText(form.ipAllowlistText);
|
|
14
|
+
const invalidEntries = entries.filter((entry) => !validateIpRange(entry));
|
|
15
|
+
const configRanges = ipAllowlist.configRanges ?? [];
|
|
13
16
|
const distributed = secrets.rateLimitBackend === 'distributed';
|
|
14
|
-
return (_jsxs(SecurityCard, { headingId: headingId, title: "Access
|
|
17
|
+
return (_jsxs(SecurityCard, { headingId: headingId, title: "Access Control", description: "Restrict where the admin and CMS API can be reached from, and control the audit trail.", children: [_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("label", { htmlFor: allowlistId, className: "text-foreground text-base font-medium", children: "Allowed IP addresses" }), _jsx(SettingsSourceBadge, { meta: sources.ipAllowlist })] }), _jsx("textarea", { id: allowlistId, rows: 3, value: form.ipAllowlistText, disabled: !canEdit, onChange: (e) => setField('ipAllowlistText', e.target.value), placeholder: '203.0.113.0/24\n198.51.100.42', "aria-describedby": allowlistHintId, "aria-invalid": invalidEntries.length > 0 || undefined, className: "border-border bg-input-background text-foreground focus-visible:ring-ring w-full resize-y rounded-md border px-3 py-2 font-mono text-sm focus-visible:ring-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50" }), _jsx("p", { id: allowlistHintId, className: "text-muted-foreground text-sm", children: "Restrict admin access to these ranges. One IP or CIDR range per line; leave empty to allow all addresses." }), invalidEntries.length > 0 && (_jsxs("p", { className: "text-destructive text-sm", role: "alert", children: ["Invalid entries: ", invalidEntries.join(', ')] })), entries.length > 0 && (_jsxs("div", { className: "border-warning/30 bg-warning/10 text-warning flex items-start gap-2 rounded-md border p-2.5 text-sm", children: [_jsx(AlertTriangle, { size: 15, className: "mt-0.5 shrink-0", "aria-hidden": "true" }), _jsxs("span", { children: ["Make sure your own IP", ipAllowlist.currentIp ? (_jsxs(_Fragment, { children: [' ', "(", _jsx("span", { className: "font-mono", children: ipAllowlist.currentIp }), ")"] })) : null, ' ', "is covered \u2014 saving a list that excludes it will lock you out of the admin."] })] })), configRanges.length > 0 && (_jsxs("div", { className: "pt-1", children: [_jsxs("p", { className: "text-muted-foreground text-sm", children: ["Always enforced from ", _jsx("code", { className: "font-mono", children: "admin.ipAllowlist" }), " in actuate.config.ts:"] }), _jsx("div", { className: "text-muted-foreground mt-1 flex flex-wrap gap-1.5", children: configRanges.map((r) => (_jsx("code", { className: "bg-muted rounded px-1.5 py-0.5 font-mono text-xs", children: r }, r))) })] })), ipAllowlist.currentIp && ipAllowlist.enabled && (_jsx(StatusRow, { status: ipAllowlist.currentIpAllowed === null
|
|
15
18
|
? 'unknown'
|
|
16
19
|
: ipAllowlist.currentIpAllowed
|
|
17
20
|
? 'pass'
|
|
18
|
-
: 'fail', label: "Your current IP", value: _jsxs("span", { className: "font-mono text-xs", children: [ipAllowlist.currentIp, ipAllowlist.currentIpAllowed === false && ' (blocked)'] }) })),
|
|
21
|
+
: 'fail', label: "Your current IP", value: _jsxs("span", { className: "font-mono text-xs", children: [ipAllowlist.currentIp, ipAllowlist.currentIpAllowed === false && ' (blocked)'] }) }))] }), _jsx("div", { className: "border-border mt-4 border-t pt-4", children: _jsx(SettingsToggleRow, { label: "Audit logging", description: "Record every content change, login and settings update. Security-critical events (failed logins, MFA and security policy changes) are always recorded.", checked: form.auditLoggingEnabled, disabled: !canEdit, onChange: (value) => setField('auditLoggingEnabled', value) }) }), _jsxs("div", { className: "border-border mt-4 space-y-2 border-t pt-4", children: [_jsx(StatusRow, { status: csrfEnabled ? 'pass' : 'fail', label: "CSRF protection", value: csrfEnabled ? 'Enabled' : 'Disabled', badge: _jsx(SettingsSourceBadge, { meta: sources.csrf }) }), _jsx(StatusRow, { status: distributed ? 'pass' : 'warning', label: "Rate limiting", value: distributed ? 'Distributed (Upstash)' : 'In-memory', badge: _jsx(SettingsSourceBadge, { meta: sources.rateLimiting }) }), _jsx(StatusRow, { status: uploadValidation ? 'pass' : 'warning', label: "Upload validation", value: "MIME + magic bytes + SVG sanitization", badge: _jsx(SettingsSourceBadge, { meta: sources.uploadValidation }) }), onNavigate && (_jsx("button", { type: "button", onClick: () => onNavigate('/media'), className: "text-brand mt-1 text-sm hover:underline", children: "Manage upload rules in Media \u2192" }))] })] }));
|
|
19
22
|
}
|
|
20
23
|
//# sourceMappingURL=AccessProtectionCard.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccessProtectionCard.js","sourceRoot":"","sources":["../../../src/views/settings/AccessProtectionCard.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"AccessProtectionCard.js","sourceRoot":"","sources":["../../../src/views/settings/AccessProtectionCard.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecurityTab.d.ts","sourceRoot":"","sources":["../../../src/views/settings/SecurityTab.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SecurityTab.d.ts","sourceRoot":"","sources":["../../../src/views/settings/SecurityTab.tsx"],"names":[],"mappings":"AA8BA,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,UAAU,GACX,EAAE;IACD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC,2CAoJA"}
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect } from 'react';
|
|
4
4
|
import { ShieldAlert } from 'lucide-react';
|
|
5
|
-
import {
|
|
5
|
+
import { parseRouteQuery } from '../../router/index.js';
|
|
6
|
+
import { ConfirmDangerousSettingDialog, SettingsSaveBar, SettingsValidationSummary, } from './components.js';
|
|
6
7
|
import { AccessProtectionCard } from './AccessProtectionCard.js';
|
|
7
8
|
import { AuthMfaCard } from './AuthMfaCard.js';
|
|
8
9
|
import { SecretsAuditCard } from './SecretsAuditCard.js';
|
|
@@ -14,12 +15,12 @@ function CardSkeleton() {
|
|
|
14
15
|
}
|
|
15
16
|
export function SecurityTab({ role, onNavigate, }) {
|
|
16
17
|
const canEdit = role === 'ADMIN';
|
|
17
|
-
const { loading, loadError, data, form, setField, dirty, issues, hasErrors, saveState, save, reset, revokeState, revokeAllOtherSessions, } = useSecuritySettings(canEdit);
|
|
18
|
+
const { loading, loadError, data, form, setField, dirty, issues, hasErrors, saveState, save, reset, lockoutPrompt, dismissLockoutPrompt, revokeState, revokeAllOtherSessions, } = useSecuritySettings(canEdit);
|
|
18
19
|
// Deep-link support: /settings?tab=security§ion=sessions|ip-allowlist|audit
|
|
19
20
|
useEffect(() => {
|
|
20
21
|
if (loading || typeof window === 'undefined')
|
|
21
22
|
return;
|
|
22
|
-
const section =
|
|
23
|
+
const section = parseRouteQuery(window.location.search).get('section');
|
|
23
24
|
if (!section)
|
|
24
25
|
return;
|
|
25
26
|
const el = document.getElementById(`security-${section}`);
|
|
@@ -36,6 +37,6 @@ export function SecurityTab({ role, onNavigate, }) {
|
|
|
36
37
|
? 'Ask an administrator to review the Security tab.'
|
|
37
38
|
: (loadError ?? 'The security service could not be reached. Try again shortly.') })] })] }));
|
|
38
39
|
}
|
|
39
|
-
return (_jsxs("div", { className: "space-y-4 pb-4", children: [_jsx(SecurityStatusCard, { status: data.status, onNavigate: onNavigate }), issues.length > 0 && _jsx(SettingsValidationSummary, { issues: issues }), !canEdit && (_jsx("p", { className: "text-muted-foreground text-sm", children: "You have read-only access. Only administrators can change security settings." })), _jsx(AuthMfaCard, { form: form, setField: setField, canEdit: canEdit, viewerHasMfa: data.viewerHasMfa, withMfa: data.users.withMfa, totalAdmins: data.users.totalAdmins, onNavigate: onNavigate }), _jsx("div", { id: "security-sessions", children: _jsx(SessionPolicyCard, { form: form, setField: setField, canEdit: canEdit,
|
|
40
|
+
return (_jsxs("div", { className: "space-y-4 pb-4", children: [_jsx(SecurityStatusCard, { status: data.status, onNavigate: onNavigate }), issues.length > 0 && _jsx(SettingsValidationSummary, { issues: issues }), !canEdit && (_jsx("p", { className: "text-muted-foreground text-sm", children: "You have read-only access. Only administrators can change security settings." })), _jsx(AuthMfaCard, { form: form, setField: setField, canEdit: canEdit, viewerHasMfa: data.viewerHasMfa, withMfa: data.users.withMfa, totalAdmins: data.users.totalAdmins, onNavigate: onNavigate }), _jsx("div", { id: "security-sessions", children: _jsx(SessionPolicyCard, { form: form, setField: setField, canEdit: canEdit, activeSessions: data.activeSessions, revokeState: revokeState, onRevokeAll: () => void revokeAllOtherSessions() }) }), _jsx("div", { id: "security-ip-allowlist", children: _jsx(AccessProtectionCard, { form: form, setField: setField, canEdit: canEdit, ipAllowlist: data.ipAllowlist, secrets: data.secrets, csrfEnabled: data.csrfEnabled, uploadValidation: data.uploadValidation, sources: data.sources, onNavigate: onNavigate }) }), _jsx("div", { id: "security-audit", children: _jsx(SecretsAuditCard, { secrets: data.secrets, auditEnabled: data.auditEnabled, failedLogins24h: data.failedLogins24h, users: data.users, apiKeys: data.apiKeys, sources: data.sources, onNavigate: onNavigate }) }), saveState === 'error' && (_jsx("p", { className: "text-destructive text-sm", role: "alert", children: "Failed to save security settings. Please try again." })), _jsx(ConfirmDangerousSettingDialog, { open: lockoutPrompt !== null, title: "This allowlist may lock you out", description: lockoutPrompt ?? 'Your current IP is not covered by the allowlist you are about to save.', confirmLabel: "Save anyway", onConfirm: () => void save({ confirmLockout: true }), onCancel: dismissLockoutPrompt }), _jsx(SettingsSaveBar, { dirty: dirty, canEdit: canEdit, hasErrors: hasErrors, saveState: saveState, onSave: () => void save(), onReset: reset })] }));
|
|
40
41
|
}
|
|
41
42
|
//# sourceMappingURL=SecurityTab.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecurityTab.js","sourceRoot":"","sources":["../../../src/views/settings/SecurityTab.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACjC,OAAO,EAAW,WAAW,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,EAAE,eAAe,EAAE,yBAAyB,
|
|
1
|
+
{"version":3,"file":"SecurityTab.js","sourceRoot":"","sources":["../../../src/views/settings/SecurityTab.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { SecurityForm } from './useSecuritySettings.js';
|
|
2
|
-
export declare function SessionPolicyCard({ form, setField, canEdit,
|
|
2
|
+
export declare function SessionPolicyCard({ form, setField, canEdit, activeSessions, revokeState, onRevokeAll, }: {
|
|
3
3
|
form: SecurityForm;
|
|
4
4
|
setField: <K extends keyof SecurityForm>(key: K, value: SecurityForm[K]) => void;
|
|
5
5
|
canEdit: boolean;
|
|
6
|
-
sessionMaxAgeHours: number;
|
|
7
6
|
activeSessions: number;
|
|
8
7
|
revokeState: 'idle' | 'working' | 'done' | 'error';
|
|
9
8
|
onRevokeAll: () => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionPolicyCard.d.ts","sourceRoot":"","sources":["../../../src/views/settings/SessionPolicyCard.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SessionPolicyCard.d.ts","sourceRoot":"","sources":["../../../src/views/settings/SessionPolicyCard.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAY5D,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,cAAc,EACd,WAAW,EACX,WAAW,GACZ,EAAE;IACD,IAAI,EAAE,YAAY,CAAA;IAClB,QAAQ,EAAE,CAAC,CAAC,SAAS,MAAM,YAAY,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;IAChF,OAAO,EAAE,OAAO,CAAA;IAChB,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAA;IAClD,WAAW,EAAE,MAAM,IAAI,CAAA;CACxB,2CA0HA"}
|
|
@@ -2,18 +2,25 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useId, useState } from 'react';
|
|
4
4
|
import { Loader2 } from 'lucide-react';
|
|
5
|
-
import { MAX_CONCURRENT_SESSION_OPTIONS } from '@actuate-media/cms-core/security-center';
|
|
6
|
-
import { ConfirmDangerousSettingDialog } from './components.js';
|
|
5
|
+
import { MAX_CONCURRENT_SESSION_OPTIONS, SESSION_TIMEOUT_HOURS_OPTIONS, } from '@actuate-media/cms-core/security-center';
|
|
6
|
+
import { ConfirmDangerousSettingDialog, SettingsToggleRow } from './components.js';
|
|
7
7
|
import { SecurityCard, StatusRow } from './securityPrimitives.js';
|
|
8
8
|
function sessionsLabel(n) {
|
|
9
9
|
return n === 0 ? 'Unlimited' : String(n);
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
function timeoutLabel(hours) {
|
|
12
|
+
if (hours === 1)
|
|
13
|
+
return '1 hour';
|
|
14
|
+
if (hours === 168)
|
|
15
|
+
return '7 days';
|
|
16
|
+
return `${hours} hours`;
|
|
17
|
+
}
|
|
18
|
+
export function SessionPolicyCard({ form, setField, canEdit, activeSessions, revokeState, onRevokeAll, }) {
|
|
12
19
|
const headingId = useId();
|
|
13
20
|
const maxId = useId();
|
|
21
|
+
const timeoutId = useId();
|
|
14
22
|
const [confirmRevoke, setConfirmRevoke] = useState(false);
|
|
15
|
-
|
|
16
|
-
return (_jsxs(SecurityCard, { headingId: headingId, title: "Session Policy", description: "Control how many sessions an account can hold and revoke active sessions.", children: [_jsx("div", { className: "space-y-4", children: _jsxs("div", { className: "flex items-center justify-between gap-4", children: [_jsxs("label", { htmlFor: maxId, className: "flex-1", children: [_jsx("span", { className: "text-foreground text-base font-medium", children: "Maximum concurrent sessions" }), _jsx("p", { className: "text-muted-foreground mt-0.5 text-sm", children: "Oldest sessions are signed out when a user exceeds this. Choose Unlimited to disable the cap." })] }), _jsx("select", { id: maxId, value: form.maxConcurrentSessions, disabled: !canEdit, onChange: (e) => setField('maxConcurrentSessions', Number(e.target.value)), className: "border-border bg-input-background text-foreground focus-visible:ring-ring rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50", children: MAX_CONCURRENT_SESSION_OPTIONS.map((n) => (_jsx("option", { value: n, children: sessionsLabel(n) }, n))) })] }) }), _jsxs("div", { className: "border-border mt-4 space-y-2 border-t pt-4", children: [_jsx(StatusRow, { status: "pass", label: "Active admin sessions", value: String(activeSessions) }), _jsx(StatusRow, { status: "pass", label: "Maximum session age", value: `${maxAgeDays} day${maxAgeDays === 1 ? '' : 's'}` }), _jsx(StatusRow, { status: "pass", label: "Revoke sessions on password change", value: "Enabled" })] }), _jsxs("div", { className: "border-border mt-4 flex items-center justify-between gap-3 border-t pt-4", children: [_jsxs("div", { children: [_jsx("p", { className: "text-foreground text-sm font-medium", children: "Revoke other sessions" }), _jsx("p", { className: "text-muted-foreground text-sm", children: "Sign out every session except this one. Useful after a suspected compromise." }), revokeState === 'done' && (_jsx("p", { className: "text-success mt-1 text-sm", "aria-live": "polite", children: "Other sessions revoked." })), revokeState === 'error' && (_jsx("p", { className: "text-destructive mt-1 text-sm", "aria-live": "polite", children: "Could not revoke sessions. Try again." }))] }), _jsxs("button", { type: "button", disabled: !canEdit || revokeState === 'working', onClick: () => setConfirmRevoke(true), className: "border-border text-foreground hover:bg-accent inline-flex shrink-0 items-center gap-2 rounded-md border px-3 py-2 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50", children: [revokeState === 'working' && (_jsx(Loader2, { size: 15, className: "animate-spin", "aria-hidden": "true" })), "Revoke other sessions"] })] }), _jsx(ConfirmDangerousSettingDialog, { open: confirmRevoke, title: "Revoke all other sessions?", description: "Every active session except your current one will be signed out immediately. Those users (and your other devices) will need to sign in again.", confirmLabel: "Revoke sessions", onConfirm: () => {
|
|
23
|
+
return (_jsxs(SecurityCard, { headingId: headingId, title: "Sessions", description: "Control session lifetime, idle sign-out, and how many sessions an account can hold.", children: [_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-4", children: [_jsxs("label", { htmlFor: timeoutId, className: "flex-1", children: [_jsx("span", { className: "text-foreground text-base font-medium", children: "Session timeout" }), _jsx("p", { className: "text-muted-foreground mt-0.5 text-sm", children: "Members are signed out automatically after this period of inactivity." })] }), _jsx("select", { id: timeoutId, value: form.timeoutHours, disabled: !canEdit, onChange: (e) => setField('timeoutHours', Number(e.target.value)), className: "border-border bg-input-background text-foreground focus-visible:ring-ring rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50", children: SESSION_TIMEOUT_HOURS_OPTIONS.map((h) => (_jsx("option", { value: h, children: timeoutLabel(h) }, h))) })] }), _jsx(SettingsToggleRow, { label: "Idle auto-logout", description: "End the session after the timeout above when the tab is inactive.", checked: form.idleLogoutEnabled, disabled: !canEdit, onChange: (value) => setField('idleLogoutEnabled', value) }), _jsxs("div", { className: "flex items-center justify-between gap-4", children: [_jsxs("label", { htmlFor: maxId, className: "flex-1", children: [_jsx("span", { className: "text-foreground text-base font-medium", children: "Maximum concurrent sessions" }), _jsx("p", { className: "text-muted-foreground mt-0.5 text-sm", children: "Oldest sessions are signed out when a user exceeds this. Choose Unlimited to disable the cap." })] }), _jsx("select", { id: maxId, value: form.maxConcurrentSessions, disabled: !canEdit, onChange: (e) => setField('maxConcurrentSessions', Number(e.target.value)), className: "border-border bg-input-background text-foreground focus-visible:ring-ring rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50", children: MAX_CONCURRENT_SESSION_OPTIONS.map((n) => (_jsx("option", { value: n, children: sessionsLabel(n) }, n))) })] })] }), _jsxs("div", { className: "border-border mt-4 space-y-2 border-t pt-4", children: [_jsx(StatusRow, { status: "pass", label: "Active admin sessions", value: String(activeSessions) }), _jsx(StatusRow, { status: "pass", label: "Maximum session age", value: timeoutLabel(form.timeoutHours) }), _jsx(StatusRow, { status: "pass", label: "Revoke sessions on password change", value: "Enabled" })] }), _jsxs("div", { className: "border-border mt-4 flex items-center justify-between gap-3 border-t pt-4", children: [_jsxs("div", { children: [_jsx("p", { className: "text-foreground text-sm font-medium", children: "Revoke other sessions" }), _jsx("p", { className: "text-muted-foreground text-sm", children: "Sign out every session except this one. Useful after a suspected compromise." }), revokeState === 'done' && (_jsx("p", { className: "text-success mt-1 text-sm", "aria-live": "polite", children: "Other sessions revoked." })), revokeState === 'error' && (_jsx("p", { className: "text-destructive mt-1 text-sm", "aria-live": "polite", children: "Could not revoke sessions. Try again." }))] }), _jsxs("button", { type: "button", disabled: !canEdit || revokeState === 'working', onClick: () => setConfirmRevoke(true), className: "border-border text-foreground hover:bg-accent inline-flex shrink-0 items-center gap-2 rounded-md border px-3 py-2 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50", children: [revokeState === 'working' && (_jsx(Loader2, { size: 15, className: "animate-spin", "aria-hidden": "true" })), "Revoke other sessions"] })] }), _jsx(ConfirmDangerousSettingDialog, { open: confirmRevoke, title: "Revoke all other sessions?", description: "Every active session except your current one will be signed out immediately. Those users (and your other devices) will need to sign in again.", confirmLabel: "Revoke sessions", onConfirm: () => {
|
|
17
24
|
setConfirmRevoke(false);
|
|
18
25
|
onRevokeAll();
|
|
19
26
|
}, onCancel: () => setConfirmRevoke(false) })] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionPolicyCard.js","sourceRoot":"","sources":["../../../src/views/settings/SessionPolicyCard.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,
|
|
1
|
+
{"version":3,"file":"SessionPolicyCard.js","sourceRoot":"","sources":["../../../src/views/settings/SessionPolicyCard.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EACL,8BAA8B,EAC9B,6BAA6B,GAC9B,MAAM,yCAAyC,CAAA;AAChD,OAAO,EAAE,6BAA6B,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAClF,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAGjE,SAAS,aAAa,CAAC,CAAS;IAC9B,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;AAC1C,CAAC;AAED,SAAS,YAAY,CAAC,KAAa;IACjC,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAA;IAChC,IAAI,KAAK,KAAK,GAAG;QAAE,OAAO,QAAQ,CAAA;IAClC,OAAO,GAAG,KAAK,QAAQ,CAAA;AACzB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,EAChC,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,cAAc,EACd,WAAW,EACX,WAAW,GAQZ;IACC,MAAM,SAAS,GAAG,KAAK,EAAE,CAAA;IACzB,MAAM,KAAK,GAAG,KAAK,EAAE,CAAA;IACrB,MAAM,SAAS,GAAG,KAAK,EAAE,CAAA;IACzB,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAEzD,OAAO,CACL,MAAC,YAAY,IACX,SAAS,EAAE,SAAS,EACpB,KAAK,EAAC,UAAU,EAChB,WAAW,EAAC,qFAAqF,aAEjG,eAAK,SAAS,EAAC,WAAW,aACxB,eAAK,SAAS,EAAC,yCAAyC,aACtD,iBAAO,OAAO,EAAE,SAAS,EAAE,SAAS,EAAC,QAAQ,aAC3C,eAAM,SAAS,EAAC,uCAAuC,gCAAuB,EAC9E,YAAG,SAAS,EAAC,sCAAsC,sFAE/C,IACE,EACR,iBACE,EAAE,EAAE,SAAS,EACb,KAAK,EAAE,IAAI,CAAC,YAAY,EACxB,QAAQ,EAAE,CAAC,OAAO,EAClB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EACjE,SAAS,EAAC,+MAA+M,YAExN,6BAA6B,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CACxC,iBAAgB,KAAK,EAAE,CAAC,YACrB,YAAY,CAAC,CAAC,CAAC,IADL,CAAC,CAEL,CACV,CAAC,GACK,IACL,EAEN,KAAC,iBAAiB,IAChB,KAAK,EAAC,kBAAkB,EACxB,WAAW,EAAC,mEAAmE,EAC/E,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAC/B,QAAQ,EAAE,CAAC,OAAO,EAClB,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,mBAAmB,EAAE,KAAK,CAAC,GACzD,EAEF,eAAK,SAAS,EAAC,yCAAyC,aACtD,iBAAO,OAAO,EAAE,KAAK,EAAE,SAAS,EAAC,QAAQ,aACvC,eAAM,SAAS,EAAC,uCAAuC,4CAEhD,EACP,YAAG,SAAS,EAAC,sCAAsC,8GAG/C,IACE,EACR,iBACE,EAAE,EAAE,KAAK,EACT,KAAK,EAAE,IAAI,CAAC,qBAAqB,EACjC,QAAQ,EAAE,CAAC,OAAO,EAClB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAC1E,SAAS,EAAC,+MAA+M,YAExN,8BAA8B,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CACzC,iBAAgB,KAAK,EAAE,CAAC,YACrB,aAAa,CAAC,CAAC,CAAC,IADN,CAAC,CAEL,CACV,CAAC,GACK,IACL,IACF,EAEN,eAAK,SAAS,EAAC,4CAA4C,aACzD,KAAC,SAAS,IAAC,MAAM,EAAC,MAAM,EAAC,KAAK,EAAC,uBAAuB,EAAC,KAAK,EAAE,MAAM,CAAC,cAAc,CAAC,GAAI,EACxF,KAAC,SAAS,IACR,MAAM,EAAC,MAAM,EACb,KAAK,EAAC,qBAAqB,EAC3B,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,GACtC,EACF,KAAC,SAAS,IAAC,MAAM,EAAC,MAAM,EAAC,KAAK,EAAC,oCAAoC,EAAC,KAAK,EAAC,SAAS,GAAG,IAClF,EAEN,eAAK,SAAS,EAAC,0EAA0E,aACvF,0BACE,YAAG,SAAS,EAAC,qCAAqC,sCAA0B,EAC5E,YAAG,SAAS,EAAC,+BAA+B,6FAExC,EACH,WAAW,KAAK,MAAM,IAAI,CACzB,YAAG,SAAS,EAAC,2BAA2B,eAAW,QAAQ,wCAEvD,CACL,EACA,WAAW,KAAK,OAAO,IAAI,CAC1B,YAAG,SAAS,EAAC,+BAA+B,eAAW,QAAQ,sDAE3D,CACL,IACG,EACN,kBACE,IAAI,EAAC,QAAQ,EACb,QAAQ,EAAE,CAAC,OAAO,IAAI,WAAW,KAAK,SAAS,EAC/C,OAAO,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,EACrC,SAAS,EAAC,yMAAyM,aAElN,WAAW,KAAK,SAAS,IAAI,CAC5B,KAAC,OAAO,IAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAC,cAAc,iBAAa,MAAM,GAAG,CAClE,6BAEM,IACL,EAEN,KAAC,6BAA6B,IAC5B,IAAI,EAAE,aAAa,EACnB,KAAK,EAAC,4BAA4B,EAClC,WAAW,EAAC,+IAA+I,EAC3J,YAAY,EAAC,iBAAiB,EAC9B,SAAS,EAAE,GAAG,EAAE;oBACd,gBAAgB,CAAC,KAAK,CAAC,CAAA;oBACvB,WAAW,EAAE,CAAA;gBACf,CAAC,EACD,QAAQ,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC,GACvC,IACW,CAChB,CAAA;AACH,CAAC"}
|
|
@@ -5,8 +5,15 @@ export interface SecurityForm {
|
|
|
5
5
|
mfaRequired: boolean;
|
|
6
6
|
mfaGracePeriodDays: number;
|
|
7
7
|
maxConcurrentSessions: number;
|
|
8
|
+
timeoutHours: number;
|
|
9
|
+
idleLogoutEnabled: boolean;
|
|
10
|
+
/** Raw textarea contents — one IP/CIDR entry per line. */
|
|
11
|
+
ipAllowlistText: string;
|
|
12
|
+
auditLoggingEnabled: boolean;
|
|
8
13
|
}
|
|
9
14
|
type SaveState = 'idle' | 'saving' | 'success' | 'error';
|
|
15
|
+
/** Split the textarea into trimmed, de-duplicated entries (one per line). */
|
|
16
|
+
export declare function parseAllowlistText(text: string): string[];
|
|
10
17
|
export interface UseSecuritySettings {
|
|
11
18
|
loading: boolean;
|
|
12
19
|
loadError: string | null;
|
|
@@ -19,9 +26,14 @@ export interface UseSecuritySettings {
|
|
|
19
26
|
canEdit: boolean;
|
|
20
27
|
saveState: SaveState;
|
|
21
28
|
saveError: string | null;
|
|
22
|
-
save: (
|
|
29
|
+
save: (options?: {
|
|
30
|
+
confirmLockout?: boolean;
|
|
31
|
+
}) => Promise<void>;
|
|
23
32
|
reset: () => void;
|
|
24
33
|
refetch: () => void;
|
|
34
|
+
/** Server rejected the save with IP_LOCKOUT_RISK — message to confirm. */
|
|
35
|
+
lockoutPrompt: string | null;
|
|
36
|
+
dismissLockoutPrompt: () => void;
|
|
25
37
|
revokeState: 'idle' | 'working' | 'done' | 'error';
|
|
26
38
|
revokeAllOtherSessions: () => Promise<number | null>;
|
|
27
39
|
}
|