@actuate-media/cms-admin 0.67.0 → 0.68.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.
Files changed (146) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/AdminRoot.d.ts.map +1 -1
  3. package/dist/AdminRoot.js +29 -5
  4. package/dist/AdminRoot.js.map +1 -1
  5. package/dist/__tests__/hooks/useIdleLogout.test.d.ts +2 -0
  6. package/dist/__tests__/hooks/useIdleLogout.test.d.ts.map +1 -0
  7. package/dist/__tests__/hooks/useIdleLogout.test.js +64 -0
  8. package/dist/__tests__/hooks/useIdleLogout.test.js.map +1 -0
  9. package/dist/__tests__/lib/post-types-service.test.d.ts +2 -0
  10. package/dist/__tests__/lib/post-types-service.test.d.ts.map +1 -0
  11. package/dist/__tests__/lib/post-types-service.test.js +88 -0
  12. package/dist/__tests__/lib/post-types-service.test.js.map +1 -0
  13. package/dist/__tests__/views/collection-list-nav.render.test.js +43 -5
  14. package/dist/__tests__/views/collection-list-nav.render.test.js.map +1 -1
  15. package/dist/__tests__/views/forms-list.render.test.js +25 -6
  16. package/dist/__tests__/views/forms-list.render.test.js.map +1 -1
  17. package/dist/__tests__/views/page-builder-sections-panel.render.test.d.ts +2 -0
  18. package/dist/__tests__/views/page-builder-sections-panel.render.test.d.ts.map +1 -0
  19. package/dist/__tests__/views/page-builder-sections-panel.render.test.js +121 -0
  20. package/dist/__tests__/views/page-builder-sections-panel.render.test.js.map +1 -0
  21. package/dist/__tests__/views/page-templates.render.test.d.ts +2 -0
  22. package/dist/__tests__/views/page-templates.render.test.d.ts.map +1 -0
  23. package/dist/__tests__/views/page-templates.render.test.js +105 -0
  24. package/dist/__tests__/views/page-templates.render.test.js.map +1 -0
  25. package/dist/__tests__/views/post-type-editor.render.test.d.ts +2 -0
  26. package/dist/__tests__/views/post-type-editor.render.test.d.ts.map +1 -0
  27. package/dist/__tests__/views/post-type-editor.render.test.js +102 -0
  28. package/dist/__tests__/views/post-type-editor.render.test.js.map +1 -0
  29. package/dist/__tests__/views/security-settings.render.test.js +85 -4
  30. package/dist/__tests__/views/security-settings.render.test.js.map +1 -1
  31. package/dist/__tests__/views/seo-content-pane.render.test.d.ts +2 -0
  32. package/dist/__tests__/views/seo-content-pane.render.test.d.ts.map +1 -0
  33. package/dist/__tests__/views/seo-content-pane.render.test.js +117 -0
  34. package/dist/__tests__/views/seo-content-pane.render.test.js.map +1 -0
  35. package/dist/actuate-admin.css +1 -1
  36. package/dist/components/collections/EntryDetailPane.d.ts +10 -0
  37. package/dist/components/collections/EntryDetailPane.d.ts.map +1 -0
  38. package/dist/components/collections/EntryDetailPane.js +92 -0
  39. package/dist/components/collections/EntryDetailPane.js.map +1 -0
  40. package/dist/components/forms/FieldsPanel.js +1 -1
  41. package/dist/components/forms/FieldsPanel.js.map +1 -1
  42. package/dist/components/forms/FormSchemaPane.d.ts +8 -0
  43. package/dist/components/forms/FormSchemaPane.d.ts.map +1 -0
  44. package/dist/components/forms/FormSchemaPane.js +95 -0
  45. package/dist/components/forms/FormSchemaPane.js.map +1 -0
  46. package/dist/components/seo/SeoEditorPane.d.ts +9 -0
  47. package/dist/components/seo/SeoEditorPane.d.ts.map +1 -0
  48. package/dist/components/seo/SeoEditorPane.js +175 -0
  49. package/dist/components/seo/SeoEditorPane.js.map +1 -0
  50. package/dist/components/ui/Badge.d.ts +1 -1
  51. package/dist/components/ui/Button.d.ts +1 -1
  52. package/dist/components/ui/StatusBadge.d.ts +1 -1
  53. package/dist/hooks/useIdleLogout.d.ts +18 -0
  54. package/dist/hooks/useIdleLogout.d.ts.map +1 -0
  55. package/dist/hooks/useIdleLogout.js +66 -0
  56. package/dist/hooks/useIdleLogout.js.map +1 -0
  57. package/dist/lib/api.d.ts +1 -0
  58. package/dist/lib/api.d.ts.map +1 -1
  59. package/dist/lib/api.js +7 -1
  60. package/dist/lib/api.js.map +1 -1
  61. package/dist/lib/post-types-service.d.ts +44 -0
  62. package/dist/lib/post-types-service.d.ts.map +1 -0
  63. package/dist/lib/post-types-service.js +83 -0
  64. package/dist/lib/post-types-service.js.map +1 -0
  65. package/dist/views/CollectionList.d.ts.map +1 -1
  66. package/dist/views/CollectionList.js +23 -4
  67. package/dist/views/CollectionList.js.map +1 -1
  68. package/dist/views/Forms.d.ts.map +1 -1
  69. package/dist/views/Forms.js +25 -21
  70. package/dist/views/Forms.js.map +1 -1
  71. package/dist/views/Posts/PostTypeEditor.d.ts +16 -0
  72. package/dist/views/Posts/PostTypeEditor.d.ts.map +1 -0
  73. package/dist/views/Posts/PostTypeEditor.js +159 -0
  74. package/dist/views/Posts/PostTypeEditor.js.map +1 -0
  75. package/dist/views/Posts/PostTypesView.d.ts +3 -2
  76. package/dist/views/Posts/PostTypesView.d.ts.map +1 -1
  77. package/dist/views/Posts/PostTypesView.js +1 -1
  78. package/dist/views/Posts/PostTypesView.js.map +1 -1
  79. package/dist/views/page-builder/BuilderSectionsPanel.d.ts +20 -0
  80. package/dist/views/page-builder/BuilderSectionsPanel.d.ts.map +1 -0
  81. package/dist/views/page-builder/BuilderSectionsPanel.js +105 -0
  82. package/dist/views/page-builder/BuilderSectionsPanel.js.map +1 -0
  83. package/dist/views/page-builder/PageBuilder.d.ts.map +1 -1
  84. package/dist/views/page-builder/PageBuilder.js +51 -2
  85. package/dist/views/page-builder/PageBuilder.js.map +1 -1
  86. package/dist/views/page-builder/PageTemplates.d.ts.map +1 -1
  87. package/dist/views/page-builder/PageTemplates.js +113 -6
  88. package/dist/views/page-builder/PageTemplates.js.map +1 -1
  89. package/dist/views/seo/ContentTab.d.ts.map +1 -1
  90. package/dist/views/seo/ContentTab.js +22 -6
  91. package/dist/views/seo/ContentTab.js.map +1 -1
  92. package/dist/views/settings/AccessProtectionCard.d.ts +5 -1
  93. package/dist/views/settings/AccessProtectionCard.d.ts.map +1 -1
  94. package/dist/views/settings/AccessProtectionCard.js +13 -10
  95. package/dist/views/settings/AccessProtectionCard.js.map +1 -1
  96. package/dist/views/settings/SecurityTab.d.ts.map +1 -1
  97. package/dist/views/settings/SecurityTab.js +3 -3
  98. package/dist/views/settings/SecurityTab.js.map +1 -1
  99. package/dist/views/settings/SessionPolicyCard.d.ts +1 -2
  100. package/dist/views/settings/SessionPolicyCard.d.ts.map +1 -1
  101. package/dist/views/settings/SessionPolicyCard.js +12 -5
  102. package/dist/views/settings/SessionPolicyCard.js.map +1 -1
  103. package/dist/views/settings/useSecuritySettings.d.ts +13 -1
  104. package/dist/views/settings/useSecuritySettings.d.ts.map +1 -1
  105. package/dist/views/settings/useSecuritySettings.js +47 -4
  106. package/dist/views/settings/useSecuritySettings.js.map +1 -1
  107. package/package.json +2 -2
  108. package/src/AdminRoot.tsx +36 -7
  109. package/src/__tests__/hooks/useIdleLogout.test.tsx +86 -0
  110. package/src/__tests__/lib/post-types-service.test.ts +112 -0
  111. package/src/__tests__/views/collection-list-nav.render.test.tsx +51 -5
  112. package/src/__tests__/views/forms-list.render.test.tsx +34 -6
  113. package/src/__tests__/views/page-builder-sections-panel.render.test.tsx +138 -0
  114. package/src/__tests__/views/page-templates.render.test.tsx +142 -0
  115. package/src/__tests__/views/post-type-editor.render.test.tsx +116 -0
  116. package/src/__tests__/views/security-settings.render.test.tsx +111 -4
  117. package/src/__tests__/views/seo-content-pane.render.test.tsx +142 -0
  118. package/src/components/collections/EntryDetailPane.tsx +182 -0
  119. package/src/components/forms/FieldsPanel.tsx +3 -1
  120. package/src/components/forms/FormSchemaPane.tsx +192 -0
  121. package/src/components/seo/SeoEditorPane.tsx +533 -0
  122. package/src/hooks/useIdleLogout.ts +77 -0
  123. package/src/lib/api.ts +8 -2
  124. package/src/lib/post-types-service.ts +129 -0
  125. package/src/views/CollectionList.tsx +158 -110
  126. package/src/views/Forms.tsx +154 -126
  127. package/src/views/Posts/PostTypeEditor.tsx +568 -0
  128. package/src/views/Posts/PostTypesView.tsx +5 -4
  129. package/src/views/page-builder/BuilderSectionsPanel.tsx +304 -0
  130. package/src/views/page-builder/PageBuilder.tsx +69 -2
  131. package/src/views/page-builder/PageTemplates.tsx +340 -57
  132. package/src/views/seo/ContentTab.tsx +113 -80
  133. package/src/views/settings/AccessProtectionCard.tsx +83 -53
  134. package/src/views/settings/SecurityTab.tsx +23 -2
  135. package/src/views/settings/SessionPolicyCard.tsx +45 -9
  136. package/src/views/settings/useSecuritySettings.ts +78 -22
  137. package/dist/components/forms/FormSchemaDrawer.d.ts +0 -9
  138. package/dist/components/forms/FormSchemaDrawer.d.ts.map +0 -1
  139. package/dist/components/forms/FormSchemaDrawer.js +0 -99
  140. package/dist/components/forms/FormSchemaDrawer.js.map +0 -1
  141. package/dist/components/seo/SeoEditorDrawer.d.ts +0 -8
  142. package/dist/components/seo/SeoEditorDrawer.d.ts.map +0 -1
  143. package/dist/components/seo/SeoEditorDrawer.js +0 -172
  144. package/dist/components/seo/SeoEditorDrawer.js.map +0 -1
  145. package/src/components/forms/FormSchemaDrawer.tsx +0 -192
  146. package/src/components/seo/SeoEditorDrawer.tsx +0 -529
@@ -14,6 +14,11 @@ export interface SecurityForm {
14
14
  mfaRequired: boolean
15
15
  mfaGracePeriodDays: number
16
16
  maxConcurrentSessions: number
17
+ timeoutHours: number
18
+ idleLogoutEnabled: boolean
19
+ /** Raw textarea contents — one IP/CIDR entry per line. */
20
+ ipAllowlistText: string
21
+ auditLoggingEnabled: boolean
17
22
  }
18
23
 
19
24
  type SaveState = 'idle' | 'saving' | 'success' | 'error'
@@ -22,6 +27,20 @@ const EMPTY_FORM: SecurityForm = {
22
27
  mfaRequired: false,
23
28
  mfaGracePeriodDays: 7,
24
29
  maxConcurrentSessions: 5,
30
+ timeoutHours: 168,
31
+ idleLogoutEnabled: false,
32
+ ipAllowlistText: '',
33
+ auditLoggingEnabled: true,
34
+ }
35
+
36
+ /** Split the textarea into trimmed, de-duplicated entries (one per line). */
37
+ export function parseAllowlistText(text: string): string[] {
38
+ const out: string[] = []
39
+ for (const line of text.split('\n')) {
40
+ const entry = line.trim()
41
+ if (entry && !out.includes(entry)) out.push(entry)
42
+ }
43
+ return out
25
44
  }
26
45
 
27
46
  function formFromSettings(s: SecuritySettings): SecurityForm {
@@ -29,13 +48,25 @@ function formFromSettings(s: SecuritySettings): SecurityForm {
29
48
  mfaRequired: s.mfa.required,
30
49
  mfaGracePeriodDays: s.mfa.gracePeriodDays,
31
50
  maxConcurrentSessions: s.session.maxConcurrentSessions,
51
+ timeoutHours: s.session.timeoutHours ?? 168,
52
+ idleLogoutEnabled: s.session.idleLogoutEnabled ?? false,
53
+ ipAllowlistText: (s.access?.ipAllowlist ?? []).join('\n'),
54
+ auditLoggingEnabled: s.access?.auditLoggingEnabled ?? true,
32
55
  }
33
56
  }
34
57
 
35
58
  function formToSettings(f: SecurityForm): SecuritySettings {
36
59
  return {
37
60
  mfa: { required: f.mfaRequired, gracePeriodDays: f.mfaGracePeriodDays },
38
- session: { maxConcurrentSessions: f.maxConcurrentSessions },
61
+ session: {
62
+ maxConcurrentSessions: f.maxConcurrentSessions,
63
+ timeoutHours: f.timeoutHours,
64
+ idleLogoutEnabled: f.idleLogoutEnabled,
65
+ },
66
+ access: {
67
+ ipAllowlist: parseAllowlistText(f.ipAllowlistText),
68
+ auditLoggingEnabled: f.auditLoggingEnabled,
69
+ },
39
70
  }
40
71
  }
41
72
 
@@ -51,9 +82,12 @@ export interface UseSecuritySettings {
51
82
  canEdit: boolean
52
83
  saveState: SaveState
53
84
  saveError: string | null
54
- save: () => Promise<void>
85
+ save: (options?: { confirmLockout?: boolean }) => Promise<void>
55
86
  reset: () => void
56
87
  refetch: () => void
88
+ /** Server rejected the save with IP_LOCKOUT_RISK — message to confirm. */
89
+ lockoutPrompt: string | null
90
+ dismissLockoutPrompt: () => void
57
91
  revokeState: 'idle' | 'working' | 'done' | 'error'
58
92
  revokeAllOtherSessions: () => Promise<number | null>
59
93
  }
@@ -73,6 +107,7 @@ export function useSecuritySettings(canEdit: boolean): UseSecuritySettings {
73
107
  const [form, setForm] = useState<SecurityForm>(EMPTY_FORM)
74
108
  const [saveState, setSaveState] = useState<SaveState>('idle')
75
109
  const [saveError, setSaveError] = useState<string | null>(null)
110
+ const [lockoutPrompt, setLockoutPrompt] = useState<string | null>(null)
76
111
  const [revokeState, setRevokeState] = useState<'idle' | 'working' | 'done' | 'error'>('idle')
77
112
  const [reloadKey, setReloadKey] = useState(0)
78
113
 
@@ -130,31 +165,50 @@ export function useSecuritySettings(canEdit: boolean): UseSecuritySettings {
130
165
 
131
166
  const hasErrors = issues.some((i) => i.severity === 'error')
132
167
 
133
- const save = useCallback(async () => {
134
- if (!canEdit || hasErrors || !dirty) return
135
- setSaveState('saving')
136
- setSaveError(null)
137
- try {
138
- const res = await cmsApi<SecurityCenterData>('/security', {
139
- method: 'PUT',
140
- body: JSON.stringify(formToSettings(form)),
141
- })
142
- if (res.error || !res.data) throw new Error(res.error ?? 'Failed to save security settings.')
143
- setData(res.data)
144
- const f = formFromSettings(res.data.settings)
145
- setBaseline(f)
146
- setForm(f)
147
- setSaveState('success')
148
- } catch (err) {
149
- setSaveState('error')
150
- setSaveError(err instanceof Error ? err.message : 'Failed to save security settings.')
151
- }
152
- }, [canEdit, hasErrors, dirty, form])
168
+ const save = useCallback(
169
+ async (options?: { confirmLockout?: boolean }) => {
170
+ if (!canEdit || hasErrors || !dirty) return
171
+ setSaveState('saving')
172
+ setSaveError(null)
173
+ setLockoutPrompt(null)
174
+ try {
175
+ const res = await cmsApi<SecurityCenterData>('/security', {
176
+ method: 'PUT',
177
+ body: JSON.stringify({
178
+ ...formToSettings(form),
179
+ ...(options?.confirmLockout ? { confirmLockout: true } : {}),
180
+ }),
181
+ })
182
+ // The server's lockout guard: surface a confirm dialog instead of a
183
+ // generic failure so the operator can retry with confirmLockout.
184
+ if (res.code === 'IP_LOCKOUT_RISK') {
185
+ setSaveState('idle')
186
+ setLockoutPrompt(res.error ?? 'This allowlist may lock you out. Save anyway?')
187
+ return
188
+ }
189
+ if (res.error || !res.data) {
190
+ throw new Error(res.error ?? 'Failed to save security settings.')
191
+ }
192
+ setData(res.data)
193
+ const f = formFromSettings(res.data.settings)
194
+ setBaseline(f)
195
+ setForm(f)
196
+ setSaveState('success')
197
+ } catch (err) {
198
+ setSaveState('error')
199
+ setSaveError(err instanceof Error ? err.message : 'Failed to save security settings.')
200
+ }
201
+ },
202
+ [canEdit, hasErrors, dirty, form],
203
+ )
204
+
205
+ const dismissLockoutPrompt = useCallback(() => setLockoutPrompt(null), [])
153
206
 
154
207
  const reset = useCallback(() => {
155
208
  setForm(baseline)
156
209
  setSaveState('idle')
157
210
  setSaveError(null)
211
+ setLockoutPrompt(null)
158
212
  }, [baseline])
159
213
 
160
214
  const refetch = useCallback(() => setReloadKey((k) => k + 1), [])
@@ -191,6 +245,8 @@ export function useSecuritySettings(canEdit: boolean): UseSecuritySettings {
191
245
  save,
192
246
  reset,
193
247
  refetch,
248
+ lockoutPrompt,
249
+ dismissLockoutPrompt,
194
250
  revokeState,
195
251
  revokeAllOtherSessions,
196
252
  }
@@ -1,9 +0,0 @@
1
- import type { FormListItem } from '../../lib/forms-service.js';
2
- export declare function FormSchemaDrawer({ form, open, onOpenChange, onSaved, }: {
3
- form: FormListItem | null;
4
- open: boolean;
5
- onOpenChange: (open: boolean) => void;
6
- /** Called after a schema/settings change so the list can refetch. */
7
- onSaved?: () => void;
8
- }): import("react/jsx-runtime").JSX.Element | null;
9
- //# sourceMappingURL=FormSchemaDrawer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FormSchemaDrawer.d.ts","sourceRoot":"","sources":["../../../src/components/forms/FormSchemaDrawer.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAiB9D,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,IAAI,EACJ,YAAY,EACZ,OAAO,GACR,EAAE;IACD,IAAI,EAAE,YAAY,GAAG,IAAI,CAAA;IACzB,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACrB,kDAmJA"}
@@ -1,99 +0,0 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- /**
4
- * Form Schema drawer — the right-side panel opened from the Forms list "Schema"
5
- * action. Four tabs: Fields (versioned schema editor), Embed (public snippets
6
- * + domain restrictions), Notifications (email settings), Integrations (CRM
7
- * webhooks / Zapier).
8
- *
9
- * Saving the Fields tab creates a new schema version server-side; existing
10
- * submissions keep referencing the version they were captured against, so
11
- * historical entries never break when the schema changes.
12
- */
13
- import * as Tabs from '@radix-ui/react-tabs';
14
- import { useEffect, useState } from 'react';
15
- import { toast } from 'sonner';
16
- import { fetchFormSchema, saveFormSchema } from '../../lib/forms-service.js';
17
- import { emitFormsChanged } from '../../lib/forms-events.js';
18
- import { ConfirmDialog } from '../ui/ConfirmDialog.js';
19
- import { Drawer } from './Drawer.js';
20
- import { FieldsPanel } from './FieldsPanel.js';
21
- import { EmbedPanel } from './EmbedPanel.js';
22
- import { NotificationsPanel } from './NotificationsPanel.js';
23
- import { WebhooksPanel } from './WebhooksPanel.js';
24
- import { FormsErrorState, FormsLoading, btnPrimary, btnSecondary } from './primitives.js';
25
- const TABS = [
26
- { id: 'fields', label: 'Fields' },
27
- { id: 'embed', label: 'Embed' },
28
- { id: 'notifications', label: 'Notifications' },
29
- { id: 'integrations', label: 'Integrations' },
30
- ];
31
- export function FormSchemaDrawer({ form, open, onOpenChange, onSaved, }) {
32
- const [tab, setTab] = useState('fields');
33
- const [fields, setFields] = useState([]);
34
- const [loading, setLoading] = useState(false);
35
- const [error, setError] = useState(null);
36
- const [dirty, setDirty] = useState(false);
37
- const [saving, setSaving] = useState(false);
38
- const [confirmDiscard, setConfirmDiscard] = useState(false);
39
- const formId = form?.id ?? null;
40
- const hasSubmissions = (form?.totalEntries ?? 0) > 0;
41
- useEffect(() => {
42
- if (!open || !formId)
43
- return;
44
- setTab('fields');
45
- setDirty(false);
46
- setLoading(true);
47
- setError(null);
48
- let active = true;
49
- fetchFormSchema(formId)
50
- .then((schema) => {
51
- if (active)
52
- setFields(schema.fields);
53
- })
54
- .catch((e) => {
55
- if (active)
56
- setError(e instanceof Error ? e.message : 'Failed to load schema');
57
- })
58
- .finally(() => {
59
- if (active)
60
- setLoading(false);
61
- });
62
- return () => {
63
- active = false;
64
- };
65
- }, [open, formId]);
66
- function handleFieldsChange(next) {
67
- setFields(next);
68
- setDirty(true);
69
- }
70
- async function handleSave() {
71
- if (!formId)
72
- return;
73
- setSaving(true);
74
- const res = await saveFormSchema(formId, fields.map((f, i) => ({ ...f, sortOrder: i })));
75
- setSaving(false);
76
- if (res.error) {
77
- toast.error(res.error);
78
- return;
79
- }
80
- setDirty(false);
81
- toast.success('Schema saved.');
82
- emitFormsChanged();
83
- onSaved?.();
84
- }
85
- function requestClose(next) {
86
- if (!next && dirty) {
87
- setConfirmDiscard(true);
88
- return;
89
- }
90
- onOpenChange(next);
91
- }
92
- if (!form)
93
- return null;
94
- return (_jsxs(_Fragment, { children: [_jsx(Drawer, { open: open, onOpenChange: requestClose, title: form.name, description: `/${form.slug}`, width: "max-w-2xl", footer: tab === 'fields' ? (_jsxs(_Fragment, { children: [_jsx("button", { className: btnSecondary, onClick: () => requestClose(false), children: "Close" }), _jsx("button", { className: btnPrimary, onClick: handleSave, disabled: saving || loading, children: saving ? 'Saving…' : 'Save Schema' })] })) : undefined, children: _jsxs(Tabs.Root, { value: tab, onValueChange: setTab, children: [_jsx(Tabs.List, { className: "border-border mb-4 flex gap-1 border-b", "aria-label": "Form schema sections", children: TABS.map((t) => (_jsx(Tabs.Trigger, { value: t.id, className: "text-muted-foreground hover:text-foreground data-[state=active]:text-foreground data-[state=active]:border-primary focus-visible:ring-ring -mb-px border-b-2 border-transparent px-3 py-2 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none", children: t.label }, t.id))) }), _jsx(Tabs.Content, { value: "fields", tabIndex: -1, children: loading ? (_jsx(FormsLoading, { label: "Loading schema\u2026" })) : error ? (_jsx(FormsErrorState, { message: error, onRetry: () => formId && setTab('fields') })) : (_jsx(FieldsPanel, { fields: fields, onChange: handleFieldsChange, hasSubmissions: hasSubmissions })) }), _jsx(Tabs.Content, { value: "embed", tabIndex: -1, children: formId && _jsx(EmbedPanel, { formId: formId, slug: form.slug, status: form.status }) }), _jsx(Tabs.Content, { value: "notifications", tabIndex: -1, children: formId && _jsx(NotificationsPanel, { formId: formId, onChanged: onSaved }) }), _jsx(Tabs.Content, { value: "integrations", tabIndex: -1, children: formId && _jsx(WebhooksPanel, { formId: formId, formSlug: form.slug }) })] }) }), _jsx(ConfirmDialog, { open: confirmDiscard, onClose: () => setConfirmDiscard(false), onConfirm: () => {
95
- setDirty(false);
96
- onOpenChange(false);
97
- }, title: "Discard unsaved changes?", description: "You have unsaved schema changes. Closing now will discard them.", confirmLabel: "Discard", cancelLabel: "Keep editing", destructive: true })] }));
98
- }
99
- //# sourceMappingURL=FormSchemaDrawer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FormSchemaDrawer.js","sourceRoot":"","sources":["../../../src/components/forms/FormSchemaDrawer.tsx"],"names":[],"mappings":"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"}
@@ -1,8 +0,0 @@
1
- export declare function SeoEditorDrawer({ open, onOpenChange, entityType, entityId, onSaved, }: {
2
- open: boolean;
3
- onOpenChange: (open: boolean) => void;
4
- entityType: string | null;
5
- entityId: string | null;
6
- onSaved?: () => void;
7
- }): import("react/jsx-runtime").JSX.Element;
8
- //# sourceMappingURL=SeoEditorDrawer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SeoEditorDrawer.d.ts","sourceRoot":"","sources":["../../../src/components/seo/SeoEditorDrawer.tsx"],"names":[],"mappings":"AAoFA,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,OAAO,GACR,EAAE;IACD,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACrB,2CAgbA"}
@@ -1,172 +0,0 @@
1
- 'use client';
2
- import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { cloneElement, isValidElement, useEffect, useId, useState } from 'react';
4
- import { Sparkles, Check, Loader2, Wand2 } from 'lucide-react';
5
- import { toast } from 'sonner';
6
- import { fetchSeoContentRecord, updateSeoFields, generateSeoField, SEO_SCHEMA_TYPES, } from '../../lib/seo-service.js';
7
- import { fixBrandVoiceContent } from '../../lib/brand-voice-client.js';
8
- import { BrandVoiceAlignmentBadge } from '../BrandVoiceAlignmentBadge.js';
9
- import { Drawer } from './Drawer.js';
10
- import { SeoLoading, SeoErrorState, btnPrimary, btnSecondary, gradeMeta } from './primitives.js';
11
- import { Toggle } from '../ui/Toggle.js';
12
- const META_TITLE_MAX = 60;
13
- const META_DESC_MAX = 160;
14
- const KEY_TAKEAWAY_MAX = 240;
15
- const LLMS_DESC_MAX = 160;
16
- function seoBrandChannel(field) {
17
- if (field === 'metaDescription' || field === 'ogDescription')
18
- return 'meta_description';
19
- return undefined;
20
- }
21
- function CharCount({ value, max }) {
22
- const over = value.length > max;
23
- return (_jsxs("span", { className: `text-xs ${over ? 'text-destructive' : 'text-muted-foreground'}`, children: [value.length, "/", max] }));
24
- }
25
- function Field({ label, children, hint, }) {
26
- // Associate the visible label with its control for screen readers. We inject
27
- // a generated id onto the single child input/textarea (unless it already has
28
- // one) and point the <label htmlFor> at it.
29
- const generatedId = useId();
30
- let control = children;
31
- if (isValidElement(children) && children.props.id == null) {
32
- control = cloneElement(children, { id: generatedId });
33
- }
34
- const controlId = isValidElement(control) ? control.props.id : undefined;
35
- return (_jsxs("div", { className: "space-y-1.5", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("label", { htmlFor: controlId, className: "text-foreground text-sm font-medium", children: label }), hint] }), control] }));
36
- }
37
- const inputCls = 'w-full rounded-md border border-border bg-input-background px-3 py-2 text-base text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring';
38
- export function SeoEditorDrawer({ open, onOpenChange, entityType, entityId, onSaved, }) {
39
- const [record, setRecord] = useState(null);
40
- const [loading, setLoading] = useState(false);
41
- const [error, setError] = useState(null);
42
- const [saving, setSaving] = useState(false);
43
- const [aiBusy, setAiBusy] = useState(null);
44
- const [aiFixBusy, setAiFixBusy] = useState(null);
45
- const [aiAlignments, setAiAlignments] = useState({});
46
- const [form, setForm] = useState({});
47
- useEffect(() => {
48
- if (!open || !entityType || !entityId)
49
- return;
50
- let alive = true;
51
- setLoading(true);
52
- setError(null);
53
- fetchSeoContentRecord(entityType, entityId)
54
- .then((rec) => {
55
- if (!alive)
56
- return;
57
- if (!rec) {
58
- setError('Could not load this record.');
59
- return;
60
- }
61
- setRecord(rec);
62
- setForm({
63
- metaTitle: rec.metaTitle ?? '',
64
- metaDescription: rec.metaDescription ?? '',
65
- canonicalUrl: rec.canonicalUrl ?? '',
66
- focusKeyword: rec.focusKeyword ?? '',
67
- keyTakeaway: rec.keyTakeaway ?? '',
68
- ogTitle: rec.ogTitle ?? '',
69
- ogDescription: rec.ogDescription ?? '',
70
- structuredDataType: rec.structuredDataType ?? 'WebPage',
71
- noindex: rec.noindex,
72
- nofollow: rec.nofollow,
73
- llmsInclude: rec.llmsInclude ?? 'AUTO',
74
- llmsDescription: rec.llmsDescription ?? '',
75
- llmsSection: rec.llmsSection ?? '',
76
- llmsOptional: rec.llmsOptional ?? false,
77
- });
78
- })
79
- .catch((e) => alive && setError(e instanceof Error ? e.message : 'Failed to load'))
80
- .finally(() => alive && setLoading(false));
81
- return () => {
82
- alive = false;
83
- };
84
- }, [open, entityType, entityId]);
85
- const update = (patch) => setForm((f) => ({ ...f, ...patch }));
86
- async function handleAiGenerate(field) {
87
- if (!entityType || !entityId)
88
- return;
89
- setAiBusy(field);
90
- try {
91
- const result = await generateSeoField(field, { entityType, entityId });
92
- if (result.unavailable) {
93
- toast.error('AI is not configured. Add an AI provider to enable suggestions.');
94
- return;
95
- }
96
- if (result.text) {
97
- update({ [field]: result.text });
98
- if (result.brandAlignment) {
99
- setAiAlignments((prev) => ({ ...prev, [field]: result.brandAlignment }));
100
- }
101
- toast.success('AI suggestion applied — review before saving.');
102
- }
103
- else {
104
- toast.error('No suggestion was returned.');
105
- }
106
- }
107
- catch {
108
- toast.error('AI request failed.');
109
- }
110
- finally {
111
- setAiBusy(null);
112
- }
113
- }
114
- async function handleAiFix(field) {
115
- const text = form[field] ?? '';
116
- if (!text.trim())
117
- return;
118
- setAiFixBusy(field);
119
- try {
120
- const res = await fixBrandVoiceContent({
121
- content: text,
122
- channel: seoBrandChannel(field),
123
- });
124
- if (!res.ok || !res.result) {
125
- toast.error(res.error ?? 'Could not fix copy for brand voice.');
126
- return;
127
- }
128
- update({ [field]: res.result.text });
129
- setAiAlignments((prev) => ({ ...prev, [field]: res.result.afterScore }));
130
- toast.success('Copy adjusted to match brand voice.');
131
- }
132
- finally {
133
- setAiFixBusy(null);
134
- }
135
- }
136
- function alignmentRow(field) {
137
- const alignment = aiAlignments[field];
138
- if (!alignment)
139
- return null;
140
- const fixing = aiFixBusy === field;
141
- return (_jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-2", children: [_jsx(BrandVoiceAlignmentBadge, { alignment: alignment, compact: true }), alignment.score < 85 && (_jsxs("button", { type: "button", onClick: () => handleAiFix(field), disabled: fixing || aiBusy !== null, className: "text-primary hover:bg-accent focus-visible:ring-ring inline-flex items-center gap-1 rounded-md px-2 py-0.5 text-xs font-medium focus-visible:ring-2 focus-visible:outline-none disabled:opacity-60", children: [fixing ? (_jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": true })) : (_jsx(Wand2, { className: "h-3.5 w-3.5", "aria-hidden": true })), "Fix for brand voice"] }))] }));
142
- }
143
- async function handleSave() {
144
- if (!entityType || !entityId)
145
- return;
146
- setSaving(true);
147
- const res = await updateSeoFields(entityType, entityId, form);
148
- setSaving(false);
149
- if (res.error) {
150
- toast.error(res.error);
151
- return;
152
- }
153
- toast.success('SEO fields saved.');
154
- onOpenChange(false);
155
- onSaved?.();
156
- }
157
- const title = form.metaTitle || record?.title || 'Untitled';
158
- const desc = form.metaDescription || 'No meta description set. Search engines may generate one.';
159
- const url = record?.url ?? '';
160
- function aiButton(field) {
161
- return (_jsxs("button", { type: "button", onClick: () => handleAiGenerate(field), disabled: aiBusy !== null, className: "text-primary hover:bg-accent focus-visible:ring-ring inline-flex items-center gap-1 rounded-md px-2 py-0.5 text-xs font-medium focus-visible:ring-2 focus-visible:outline-none disabled:opacity-60", children: [_jsx(Sparkles, { className: `h-3.5 w-3.5 ${aiBusy === field ? 'animate-pulse' : ''}`, "aria-hidden": true }), aiBusy === field ? 'Generating…' : 'AI Generate'] }));
162
- }
163
- return (_jsx(Drawer, { open: open, onOpenChange: onOpenChange, title: "Edit SEO", description: record?.title, footer: _jsxs(_Fragment, { children: [_jsx("button", { className: btnSecondary, onClick: () => onOpenChange(false), disabled: saving, children: "Cancel" }), _jsx("button", { className: btnPrimary, onClick: handleSave, disabled: saving || loading, children: saving ? 'Saving…' : 'Save changes' })] }), children: loading ? (_jsx(SeoLoading, {})) : error ? (_jsx(SeoErrorState, { message: error })) : record ? (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "border-border rounded-lg border p-4", children: [_jsx("p", { className: "text-muted-foreground mb-2 text-xs font-medium tracking-wide uppercase", children: "Google preview" }), _jsx("p", { className: "text-success truncate text-sm", children: url }), _jsx("p", { className: "text-primary text-lg leading-snug", children: title }), _jsx("p", { className: "text-muted-foreground line-clamp-2 text-sm", children: desc })] }), _jsx(Field, { label: "Meta title", hint: _jsxs("span", { className: "flex items-center gap-2", children: [_jsx(CharCount, { value: form.metaTitle ?? '', max: META_TITLE_MAX }), aiButton('metaTitle')] }), children: _jsx("input", { className: inputCls, value: form.metaTitle ?? '', onChange: (e) => update({ metaTitle: e.target.value }), placeholder: "Page title for search results" }) }), _jsxs(Field, { label: "Meta description", hint: _jsxs("span", { className: "flex items-center gap-2", children: [_jsx(CharCount, { value: form.metaDescription ?? '', max: META_DESC_MAX }), aiButton('metaDescription')] }), children: [_jsx("textarea", { className: `${inputCls} min-h-20 resize-y`, value: form.metaDescription ?? '', onChange: (e) => update({ metaDescription: e.target.value }), placeholder: "Short summary shown under the title in search results" }), alignmentRow('metaDescription')] }), _jsx(Field, { label: "Focus keyword", hint: aiButton('focusKeyword'), children: _jsx("input", { className: inputCls, value: form.focusKeyword ?? '', onChange: (e) => update({ focusKeyword: e.target.value }), placeholder: "Primary keyword this page targets" }) }), _jsx(Field, { label: "Key takeaway", hint: _jsxs("span", { className: "flex items-center gap-2", children: [_jsx(CharCount, { value: form.keyTakeaway ?? '', max: KEY_TAKEAWAY_MAX }), aiButton('keyTakeaway')] }), children: _jsx("textarea", { className: `${inputCls} min-h-16 resize-y`, value: form.keyTakeaway ?? '', onChange: (e) => update({ keyTakeaway: e.target.value }), placeholder: "One or two sentences AI answer engines can quote \u2014 what is this page about?" }) }), _jsxs(Field, { label: "Canonical URL", children: [_jsx("input", { className: inputCls, value: form.canonicalUrl ?? '', onChange: (e) => update({ canonicalUrl: e.target.value }), placeholder: url || 'https://example.com/page' }), !form.canonicalUrl?.trim() && url ? (_jsxs("p", { className: "text-muted-foreground text-xs", children: ["Auto-canonical: ", record?.canonicalUrl ?? url] })) : null] }), _jsx(Field, { label: "Schema type", children: _jsx("select", { className: inputCls, value: form.structuredDataType ?? 'WebPage', onChange: (e) => update({ structuredDataType: e.target.value }), children: SEO_SCHEMA_TYPES.map((t) => (_jsx("option", { value: t, children: t }, t))) }) }), _jsxs("div", { className: "space-y-4", children: [_jsx("p", { className: "text-foreground text-sm font-medium", children: "Social sharing" }), _jsx(Field, { label: "Open Graph title", hint: aiButton('ogTitle'), children: _jsx("input", { className: inputCls, value: form.ogTitle ?? '', onChange: (e) => update({ ogTitle: e.target.value }), placeholder: form.metaTitle || 'Title when shared on social media' }) }), _jsxs(Field, { label: "Open Graph description", hint: aiButton('ogDescription'), children: [_jsx("textarea", { className: `${inputCls} min-h-16 resize-y`, value: form.ogDescription ?? '', onChange: (e) => update({ ogDescription: e.target.value }), placeholder: form.metaDescription || 'Description when shared on social media' }), alignmentRow('ogDescription')] }), _jsxs("div", { className: "border-border overflow-hidden rounded-lg border", children: [record.ogImage ? (
164
- // eslint-disable-next-line @next/next/no-img-element
165
- _jsx("img", { src: record.ogImage, alt: `Social preview for ${record.title}`, className: "bg-muted aspect-[1200/630] w-full object-cover" })) : (_jsx("div", { className: "bg-muted text-muted-foreground flex aspect-[1200/630] w-full items-center justify-center text-sm", children: "No OG image set" })), _jsxs("div", { className: "p-3", children: [_jsx("p", { className: "text-muted-foreground truncate text-xs uppercase", children: url.replace(/^https?:\/\//, '').split('/')[0] }), _jsx("p", { className: "text-foreground truncate text-sm font-medium", children: form.ogTitle || title }), _jsx("p", { className: "text-muted-foreground line-clamp-1 text-sm", children: form.ogDescription || desc })] })] })] }), _jsxs("div", { className: "border-border space-y-3 rounded-lg border p-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: "text-foreground block text-sm font-medium", children: "No-index" }), _jsx("span", { className: "text-muted-foreground block text-sm", children: "Hide this page from search engines." })] }), _jsx(Toggle, { checked: !!form.noindex, onCheckedChange: (v) => update({ noindex: v }), "aria-label": "No-index" })] }), _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: "text-foreground block text-sm font-medium", children: "No-follow" }), _jsx("span", { className: "text-muted-foreground block text-sm", children: "Tell crawlers not to follow links on this page." })] }), _jsx(Toggle, { checked: !!form.nofollow, onCheckedChange: (v) => update({ nofollow: v }), "aria-label": "No-follow" })] })] }), _jsxs("div", { className: "border-border space-y-4 rounded-lg border p-4", children: [_jsxs("div", { children: [_jsx("p", { className: "text-foreground text-sm font-medium", children: "AI discovery (llms.txt)" }), _jsxs("p", { className: "text-muted-foreground text-sm", children: ["Controls how this page appears in the curated ", _jsx("code", { children: "llms.txt" }), " guide that AI agents read. It does not affect search ranking."] })] }), _jsx(Field, { label: "Include in llms.txt", children: _jsxs("select", { className: inputCls, value: form.llmsInclude ?? 'AUTO', onChange: (e) => update({ llmsInclude: e.target.value }), children: [_jsx("option", { value: "AUTO", children: "Auto \u2014 include when eligible" }), _jsx("option", { value: "ALWAYS", children: "Always \u2014 pin it in" }), _jsx("option", { value: "NEVER", children: "Never \u2014 exclude" })] }) }), _jsx(Field, { label: "Agent description", hint: _jsx(CharCount, { value: form.llmsDescription ?? '', max: LLMS_DESC_MAX }), children: _jsx("textarea", { className: `${inputCls} min-h-16 resize-y`, value: form.llmsDescription ?? '', onChange: (e) => update({ llmsDescription: e.target.value }), placeholder: form.keyTakeaway ||
166
- form.metaDescription ||
167
- 'One line for an AI agent — what this page covers and why it matters.' }) }), _jsx(Field, { label: "Section", children: _jsx("input", { className: inputCls, value: form.llmsSection ?? '', onChange: (e) => update({ llmsSection: e.target.value }), placeholder: "Defaults to the content type" }) }), _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("span", { className: "min-w-0", children: [_jsx("span", { className: "text-foreground block text-sm font-medium", children: "Optional" }), _jsx("span", { className: "text-muted-foreground block text-sm", children: "Group under \u201COptional\u201D \u2014 agents skip these first when context is tight." })] }), _jsx(Toggle, { checked: !!form.llmsOptional, onCheckedChange: (v) => update({ llmsOptional: v }), "aria-label": "Optional in llms.txt" })] })] }), (() => {
168
- const meta = gradeMeta(record.seoGrade);
169
- return (_jsxs("div", { className: `flex items-center gap-2 rounded-lg px-3 py-2 text-sm ${meta.bg} ${meta.text}`, children: [_jsx(Check, { className: "h-4 w-4", "aria-hidden": true }), "Current SEO score: ", _jsx("span", { className: "font-medium", children: record.seoScore }), " (", meta.label, ")"] }));
170
- })()] })) : null }));
171
- }
172
- //# sourceMappingURL=SeoEditorDrawer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SeoEditorDrawer.js","sourceRoot":"","sources":["../../../src/components/seo/SeoEditorDrawer.tsx"],"names":[],"mappings":"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"}