@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.
Files changed (180) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/AdminRoot.d.ts.map +1 -1
  3. package/dist/AdminRoot.js +59 -15
  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 +119 -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__/router/query-string.test.d.ts +2 -0
  14. package/dist/__tests__/router/query-string.test.d.ts.map +1 -0
  15. package/dist/__tests__/router/query-string.test.js +44 -0
  16. package/dist/__tests__/router/query-string.test.js.map +1 -0
  17. package/dist/__tests__/views/collection-list-nav.render.test.js +43 -5
  18. package/dist/__tests__/views/collection-list-nav.render.test.js.map +1 -1
  19. package/dist/__tests__/views/forms-list.render.test.js +25 -6
  20. package/dist/__tests__/views/forms-list.render.test.js.map +1 -1
  21. package/dist/__tests__/views/page-builder-sections-panel.render.test.d.ts +2 -0
  22. package/dist/__tests__/views/page-builder-sections-panel.render.test.d.ts.map +1 -0
  23. package/dist/__tests__/views/page-builder-sections-panel.render.test.js +121 -0
  24. package/dist/__tests__/views/page-builder-sections-panel.render.test.js.map +1 -0
  25. package/dist/__tests__/views/page-templates.render.test.d.ts +2 -0
  26. package/dist/__tests__/views/page-templates.render.test.d.ts.map +1 -0
  27. package/dist/__tests__/views/page-templates.render.test.js +105 -0
  28. package/dist/__tests__/views/page-templates.render.test.js.map +1 -0
  29. package/dist/__tests__/views/post-type-editor.render.test.d.ts +2 -0
  30. package/dist/__tests__/views/post-type-editor.render.test.d.ts.map +1 -0
  31. package/dist/__tests__/views/post-type-editor.render.test.js +102 -0
  32. package/dist/__tests__/views/post-type-editor.render.test.js.map +1 -0
  33. package/dist/__tests__/views/security-settings.render.test.js +85 -4
  34. package/dist/__tests__/views/security-settings.render.test.js.map +1 -1
  35. package/dist/__tests__/views/seo-content-pane.render.test.d.ts +2 -0
  36. package/dist/__tests__/views/seo-content-pane.render.test.d.ts.map +1 -0
  37. package/dist/__tests__/views/seo-content-pane.render.test.js +117 -0
  38. package/dist/__tests__/views/seo-content-pane.render.test.js.map +1 -0
  39. package/dist/__tests__/views/users-selected-deeplink.render.test.d.ts +2 -0
  40. package/dist/__tests__/views/users-selected-deeplink.render.test.d.ts.map +1 -0
  41. package/dist/__tests__/views/users-selected-deeplink.render.test.js +91 -0
  42. package/dist/__tests__/views/users-selected-deeplink.render.test.js.map +1 -0
  43. package/dist/actuate-admin.css +1 -1
  44. package/dist/components/collections/EntryDetailPane.d.ts +10 -0
  45. package/dist/components/collections/EntryDetailPane.d.ts.map +1 -0
  46. package/dist/components/collections/EntryDetailPane.js +92 -0
  47. package/dist/components/collections/EntryDetailPane.js.map +1 -0
  48. package/dist/components/forms/FieldsPanel.js +1 -1
  49. package/dist/components/forms/FieldsPanel.js.map +1 -1
  50. package/dist/components/forms/FormSchemaPane.d.ts +8 -0
  51. package/dist/components/forms/FormSchemaPane.d.ts.map +1 -0
  52. package/dist/components/forms/FormSchemaPane.js +95 -0
  53. package/dist/components/forms/FormSchemaPane.js.map +1 -0
  54. package/dist/components/seo/SeoEditorPane.d.ts +9 -0
  55. package/dist/components/seo/SeoEditorPane.d.ts.map +1 -0
  56. package/dist/components/seo/SeoEditorPane.js +175 -0
  57. package/dist/components/seo/SeoEditorPane.js.map +1 -0
  58. package/dist/components/ui/Badge.d.ts +1 -1
  59. package/dist/components/ui/Button.d.ts +1 -1
  60. package/dist/components/ui/StatusBadge.d.ts +1 -1
  61. package/dist/hooks/useIdleLogout.d.ts +33 -0
  62. package/dist/hooks/useIdleLogout.d.ts.map +1 -0
  63. package/dist/hooks/useIdleLogout.js +99 -0
  64. package/dist/hooks/useIdleLogout.js.map +1 -0
  65. package/dist/lib/api.d.ts +1 -0
  66. package/dist/lib/api.d.ts.map +1 -1
  67. package/dist/lib/api.js +7 -1
  68. package/dist/lib/api.js.map +1 -1
  69. package/dist/lib/post-types-service.d.ts +44 -0
  70. package/dist/lib/post-types-service.d.ts.map +1 -0
  71. package/dist/lib/post-types-service.js +83 -0
  72. package/dist/lib/post-types-service.js.map +1 -0
  73. package/dist/router/index.d.ts +7 -0
  74. package/dist/router/index.d.ts.map +1 -1
  75. package/dist/router/index.js +23 -4
  76. package/dist/router/index.js.map +1 -1
  77. package/dist/views/CollectionList.d.ts.map +1 -1
  78. package/dist/views/CollectionList.js +23 -4
  79. package/dist/views/CollectionList.js.map +1 -1
  80. package/dist/views/Forms.d.ts.map +1 -1
  81. package/dist/views/Forms.js +25 -21
  82. package/dist/views/Forms.js.map +1 -1
  83. package/dist/views/MediaBrowser.d.ts +4 -1
  84. package/dist/views/MediaBrowser.d.ts.map +1 -1
  85. package/dist/views/MediaBrowser.js +27 -5
  86. package/dist/views/MediaBrowser.js.map +1 -1
  87. package/dist/views/Posts/PostTypeEditor.d.ts +16 -0
  88. package/dist/views/Posts/PostTypeEditor.d.ts.map +1 -0
  89. package/dist/views/Posts/PostTypeEditor.js +159 -0
  90. package/dist/views/Posts/PostTypeEditor.js.map +1 -0
  91. package/dist/views/Posts/PostTypesView.d.ts +3 -2
  92. package/dist/views/Posts/PostTypesView.d.ts.map +1 -1
  93. package/dist/views/Posts/PostTypesView.js +1 -1
  94. package/dist/views/Posts/PostTypesView.js.map +1 -1
  95. package/dist/views/SEO.d.ts.map +1 -1
  96. package/dist/views/SEO.js +2 -1
  97. package/dist/views/SEO.js.map +1 -1
  98. package/dist/views/Settings.d.ts +4 -1
  99. package/dist/views/Settings.d.ts.map +1 -1
  100. package/dist/views/Settings.js +4 -3
  101. package/dist/views/Settings.js.map +1 -1
  102. package/dist/views/Users.d.ts +4 -1
  103. package/dist/views/Users.d.ts.map +1 -1
  104. package/dist/views/Users.js +22 -2
  105. package/dist/views/Users.js.map +1 -1
  106. package/dist/views/page-builder/BuilderSectionsPanel.d.ts +20 -0
  107. package/dist/views/page-builder/BuilderSectionsPanel.d.ts.map +1 -0
  108. package/dist/views/page-builder/BuilderSectionsPanel.js +105 -0
  109. package/dist/views/page-builder/BuilderSectionsPanel.js.map +1 -0
  110. package/dist/views/page-builder/PageBuilder.d.ts.map +1 -1
  111. package/dist/views/page-builder/PageBuilder.js +51 -2
  112. package/dist/views/page-builder/PageBuilder.js.map +1 -1
  113. package/dist/views/page-builder/PageTemplates.d.ts.map +1 -1
  114. package/dist/views/page-builder/PageTemplates.js +113 -6
  115. package/dist/views/page-builder/PageTemplates.js.map +1 -1
  116. package/dist/views/seo/ContentTab.d.ts.map +1 -1
  117. package/dist/views/seo/ContentTab.js +22 -6
  118. package/dist/views/seo/ContentTab.js.map +1 -1
  119. package/dist/views/settings/AccessProtectionCard.d.ts +5 -1
  120. package/dist/views/settings/AccessProtectionCard.d.ts.map +1 -1
  121. package/dist/views/settings/AccessProtectionCard.js +13 -10
  122. package/dist/views/settings/AccessProtectionCard.js.map +1 -1
  123. package/dist/views/settings/SecurityTab.d.ts.map +1 -1
  124. package/dist/views/settings/SecurityTab.js +5 -4
  125. package/dist/views/settings/SecurityTab.js.map +1 -1
  126. package/dist/views/settings/SessionPolicyCard.d.ts +1 -2
  127. package/dist/views/settings/SessionPolicyCard.d.ts.map +1 -1
  128. package/dist/views/settings/SessionPolicyCard.js +12 -5
  129. package/dist/views/settings/SessionPolicyCard.js.map +1 -1
  130. package/dist/views/settings/useSecuritySettings.d.ts +13 -1
  131. package/dist/views/settings/useSecuritySettings.d.ts.map +1 -1
  132. package/dist/views/settings/useSecuritySettings.js +47 -4
  133. package/dist/views/settings/useSecuritySettings.js.map +1 -1
  134. package/package.json +2 -2
  135. package/src/AdminRoot.tsx +88 -18
  136. package/src/__tests__/hooks/useIdleLogout.test.tsx +188 -0
  137. package/src/__tests__/lib/post-types-service.test.ts +112 -0
  138. package/src/__tests__/router/query-string.test.ts +54 -0
  139. package/src/__tests__/views/collection-list-nav.render.test.tsx +51 -5
  140. package/src/__tests__/views/forms-list.render.test.tsx +34 -6
  141. package/src/__tests__/views/page-builder-sections-panel.render.test.tsx +138 -0
  142. package/src/__tests__/views/page-templates.render.test.tsx +142 -0
  143. package/src/__tests__/views/post-type-editor.render.test.tsx +116 -0
  144. package/src/__tests__/views/security-settings.render.test.tsx +111 -4
  145. package/src/__tests__/views/seo-content-pane.render.test.tsx +142 -0
  146. package/src/__tests__/views/users-selected-deeplink.render.test.tsx +102 -0
  147. package/src/components/collections/EntryDetailPane.tsx +182 -0
  148. package/src/components/forms/FieldsPanel.tsx +3 -1
  149. package/src/components/forms/FormSchemaPane.tsx +192 -0
  150. package/src/components/seo/SeoEditorPane.tsx +533 -0
  151. package/src/hooks/useIdleLogout.ts +129 -0
  152. package/src/lib/api.ts +8 -2
  153. package/src/lib/post-types-service.ts +129 -0
  154. package/src/router/index.ts +25 -4
  155. package/src/views/CollectionList.tsx +158 -110
  156. package/src/views/Forms.tsx +154 -126
  157. package/src/views/MediaBrowser.tsx +28 -5
  158. package/src/views/Posts/PostTypeEditor.tsx +568 -0
  159. package/src/views/Posts/PostTypesView.tsx +5 -4
  160. package/src/views/SEO.tsx +2 -1
  161. package/src/views/Settings.tsx +7 -2
  162. package/src/views/Users.tsx +23 -2
  163. package/src/views/page-builder/BuilderSectionsPanel.tsx +304 -0
  164. package/src/views/page-builder/PageBuilder.tsx +69 -2
  165. package/src/views/page-builder/PageTemplates.tsx +340 -57
  166. package/src/views/seo/ContentTab.tsx +113 -80
  167. package/src/views/settings/AccessProtectionCard.tsx +83 -53
  168. package/src/views/settings/SecurityTab.tsx +25 -3
  169. package/src/views/settings/SessionPolicyCard.tsx +45 -9
  170. package/src/views/settings/useSecuritySettings.ts +78 -22
  171. package/dist/components/forms/FormSchemaDrawer.d.ts +0 -9
  172. package/dist/components/forms/FormSchemaDrawer.d.ts.map +0 -1
  173. package/dist/components/forms/FormSchemaDrawer.js +0 -99
  174. package/dist/components/forms/FormSchemaDrawer.js.map +0 -1
  175. package/dist/components/seo/SeoEditorDrawer.d.ts +0 -8
  176. package/dist/components/seo/SeoEditorDrawer.d.ts.map +0 -1
  177. package/dist/components/seo/SeoEditorDrawer.js +0 -172
  178. package/dist/components/seo/SeoEditorDrawer.js.map +0 -1
  179. package/src/components/forms/FormSchemaDrawer.tsx +0 -192
  180. package/src/components/seo/SeoEditorDrawer.tsx +0 -529
@@ -0,0 +1,192 @@
1
+ 'use client'
2
+
3
+ /**
4
+ * Form Schema pane — inline inspector opened from the Forms list "Schema"
5
+ * action (design's 390px schema panel; previously the `FormSchemaDrawer`
6
+ * overlay). Four tabs: Fields (versioned schema editor), Embed (public
7
+ * snippets + domain restrictions), Notifications (email settings),
8
+ * Integrations (CRM webhooks / Zapier).
9
+ *
10
+ * Saving the Fields tab creates a new schema version server-side; existing
11
+ * submissions keep referencing the version they were captured against, so
12
+ * historical entries never break when the schema changes.
13
+ */
14
+ import * as Tabs from '@radix-ui/react-tabs'
15
+ import { useEffect, useState } from 'react'
16
+ import { toast } from 'sonner'
17
+ import { fetchFormSchema, saveFormSchema, type FormField } from '../../lib/forms-service.js'
18
+ import type { FormListItem } from '../../lib/forms-service.js'
19
+ import { emitFormsChanged } from '../../lib/forms-events.js'
20
+ import { ConfirmDialog } from '../ui/ConfirmDialog.js'
21
+ import { InspectorPaneBody, InspectorPaneFooter, InspectorPaneHeader } from '../ui/InspectorPane.js'
22
+ import { FieldsPanel } from './FieldsPanel.js'
23
+ import { EmbedPanel } from './EmbedPanel.js'
24
+ import { NotificationsPanel } from './NotificationsPanel.js'
25
+ import { WebhooksPanel } from './WebhooksPanel.js'
26
+ import { FormsErrorState, FormsLoading, btnPrimary } from './primitives.js'
27
+
28
+ const TABS = [
29
+ { id: 'fields', label: 'Fields' },
30
+ { id: 'embed', label: 'Embed' },
31
+ { id: 'notifications', label: 'Notifications' },
32
+ { id: 'integrations', label: 'Integrations' },
33
+ ] as const
34
+
35
+ export function FormSchemaPane({
36
+ form,
37
+ onClose,
38
+ onSaved,
39
+ }: {
40
+ form: FormListItem
41
+ onClose: () => void
42
+ /** Called after a schema/settings change so the list can refetch. */
43
+ onSaved?: () => void
44
+ }) {
45
+ const [tab, setTab] = useState<string>('fields')
46
+ const [fields, setFields] = useState<FormField[]>([])
47
+ const [loading, setLoading] = useState(false)
48
+ const [error, setError] = useState<string | null>(null)
49
+ const [dirty, setDirty] = useState(false)
50
+ const [saving, setSaving] = useState(false)
51
+ const [confirmDiscard, setConfirmDiscard] = useState(false)
52
+
53
+ const formId = form.id
54
+ const hasSubmissions = (form.totalEntries ?? 0) > 0
55
+
56
+ useEffect(() => {
57
+ setTab('fields')
58
+ setDirty(false)
59
+ setLoading(true)
60
+ setError(null)
61
+ let active = true
62
+ fetchFormSchema(formId)
63
+ .then((schema) => {
64
+ if (active) setFields(schema.fields)
65
+ })
66
+ .catch((e: unknown) => {
67
+ if (active) setError(e instanceof Error ? e.message : 'Failed to load schema')
68
+ })
69
+ .finally(() => {
70
+ if (active) setLoading(false)
71
+ })
72
+ return () => {
73
+ active = false
74
+ }
75
+ }, [formId])
76
+
77
+ function handleFieldsChange(next: FormField[]) {
78
+ setFields(next)
79
+ setDirty(true)
80
+ }
81
+
82
+ async function handleSave() {
83
+ setSaving(true)
84
+ const res = await saveFormSchema(
85
+ formId,
86
+ fields.map((f, i) => ({ ...f, sortOrder: i })),
87
+ )
88
+ setSaving(false)
89
+ if (res.error) {
90
+ toast.error(res.error)
91
+ return
92
+ }
93
+ setDirty(false)
94
+ toast.success('Schema saved.')
95
+ emitFormsChanged()
96
+ onSaved?.()
97
+ }
98
+
99
+ function requestClose() {
100
+ if (dirty) {
101
+ setConfirmDiscard(true)
102
+ return
103
+ }
104
+ onClose()
105
+ }
106
+
107
+ return (
108
+ <>
109
+ <InspectorPaneHeader onClose={requestClose} closeLabel="Close schema panel">
110
+ <div className="truncate text-[13.5px] font-semibold text-[var(--txt)]">{form.name}</div>
111
+ <div className="mt-0.5 truncate font-mono text-[11.5px] text-[var(--muted)]">
112
+ /{form.slug}
113
+ </div>
114
+ </InspectorPaneHeader>
115
+
116
+ <Tabs.Root value={tab} onValueChange={setTab} className="flex min-h-0 flex-1 flex-col">
117
+ <Tabs.List
118
+ className="flex shrink-0 gap-0.5 overflow-x-auto border-b border-[var(--bdr)] px-2"
119
+ aria-label="Form schema sections"
120
+ >
121
+ {TABS.map((t) => (
122
+ <Tabs.Trigger
123
+ key={t.id}
124
+ value={t.id}
125
+ 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-2.5 py-2 text-[12.5px] font-medium whitespace-nowrap transition-colors focus-visible:ring-2 focus-visible:outline-none"
126
+ >
127
+ {t.label}
128
+ </Tabs.Trigger>
129
+ ))}
130
+ </Tabs.List>
131
+
132
+ <InspectorPaneBody>
133
+ <div className="px-4 py-3.5">
134
+ <Tabs.Content value="fields" tabIndex={-1}>
135
+ {loading ? (
136
+ <FormsLoading label="Loading schema…" />
137
+ ) : error ? (
138
+ <FormsErrorState message={error} onRetry={() => setTab('fields')} />
139
+ ) : (
140
+ <FieldsPanel
141
+ fields={fields}
142
+ onChange={handleFieldsChange}
143
+ hasSubmissions={hasSubmissions}
144
+ />
145
+ )}
146
+ </Tabs.Content>
147
+
148
+ <Tabs.Content value="embed" tabIndex={-1}>
149
+ <EmbedPanel formId={formId} slug={form.slug} status={form.status} />
150
+ </Tabs.Content>
151
+
152
+ <Tabs.Content value="notifications" tabIndex={-1}>
153
+ <NotificationsPanel formId={formId} onChanged={onSaved} />
154
+ </Tabs.Content>
155
+
156
+ <Tabs.Content value="integrations" tabIndex={-1}>
157
+ <WebhooksPanel formId={formId} formSlug={form.slug} />
158
+ </Tabs.Content>
159
+ </div>
160
+ </InspectorPaneBody>
161
+
162
+ {tab === 'fields' && (
163
+ <InspectorPaneFooter>
164
+ <button
165
+ type="button"
166
+ className={`${btnPrimary} w-full`}
167
+ onClick={() => void handleSave()}
168
+ disabled={saving || loading}
169
+ >
170
+ {saving ? 'Saving…' : 'Save Schema'}
171
+ </button>
172
+ </InspectorPaneFooter>
173
+ )}
174
+ </Tabs.Root>
175
+
176
+ <ConfirmDialog
177
+ open={confirmDiscard}
178
+ onClose={() => setConfirmDiscard(false)}
179
+ onConfirm={() => {
180
+ setDirty(false)
181
+ setConfirmDiscard(false)
182
+ onClose()
183
+ }}
184
+ title="Discard unsaved changes?"
185
+ description="You have unsaved schema changes. Closing now will discard them."
186
+ confirmLabel="Discard"
187
+ cancelLabel="Keep editing"
188
+ destructive
189
+ />
190
+ </>
191
+ )
192
+ }