@actuate-media/cms-admin 0.21.1 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/layout/sidebar-forms-badge.render.test.d.ts +2 -0
- package/dist/__tests__/layout/sidebar-forms-badge.render.test.d.ts.map +1 -0
- package/dist/__tests__/layout/sidebar-forms-badge.render.test.js +30 -0
- package/dist/__tests__/layout/sidebar-forms-badge.render.test.js.map +1 -0
- package/dist/__tests__/layout/sidebar-submenu.render.test.js +8 -2
- package/dist/__tests__/layout/sidebar-submenu.render.test.js.map +1 -1
- package/dist/__tests__/views/forms-list.render.test.d.ts +2 -0
- package/dist/__tests__/views/forms-list.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/forms-list.render.test.js +122 -0
- package/dist/__tests__/views/forms-list.render.test.js.map +1 -0
- package/dist/actuate-admin.css +1 -1
- package/dist/components/forms/Drawer.d.ts +13 -0
- package/dist/components/forms/Drawer.d.ts.map +1 -0
- package/dist/components/forms/Drawer.js +13 -0
- package/dist/components/forms/Drawer.js.map +1 -0
- package/dist/components/forms/EmbedPanel.d.ts +7 -0
- package/dist/components/forms/EmbedPanel.d.ts.map +1 -0
- package/dist/components/forms/EmbedPanel.js +91 -0
- package/dist/components/forms/EmbedPanel.js.map +1 -0
- package/dist/components/forms/FieldsPanel.d.ts +8 -0
- package/dist/components/forms/FieldsPanel.d.ts.map +1 -0
- package/dist/components/forms/FieldsPanel.js +123 -0
- package/dist/components/forms/FieldsPanel.js.map +1 -0
- package/dist/components/forms/FormSchemaDrawer.d.ts +9 -0
- package/dist/components/forms/FormSchemaDrawer.d.ts.map +1 -0
- package/dist/components/forms/FormSchemaDrawer.js +96 -0
- package/dist/components/forms/FormSchemaDrawer.js.map +1 -0
- package/dist/components/forms/NotificationsPanel.d.ts +6 -0
- package/dist/components/forms/NotificationsPanel.d.ts.map +1 -0
- package/dist/components/forms/NotificationsPanel.js +80 -0
- package/dist/components/forms/NotificationsPanel.js.map +1 -0
- package/dist/components/forms/primitives.d.ts +42 -0
- package/dist/components/forms/primitives.d.ts.map +1 -0
- package/dist/components/forms/primitives.js +96 -0
- package/dist/components/forms/primitives.js.map +1 -0
- package/dist/layout/Sidebar.d.ts +5 -0
- package/dist/layout/Sidebar.d.ts.map +1 -1
- package/dist/layout/Sidebar.js +43 -3
- package/dist/layout/Sidebar.js.map +1 -1
- package/dist/lib/forms-events.d.ts +17 -0
- package/dist/lib/forms-events.d.ts.map +1 -0
- package/dist/lib/forms-events.js +20 -0
- package/dist/lib/forms-events.js.map +1 -0
- package/dist/lib/forms-service.d.ts +80 -0
- package/dist/lib/forms-service.d.ts.map +1 -0
- package/dist/lib/forms-service.js +144 -0
- package/dist/lib/forms-service.js.map +1 -0
- package/dist/views/Forms.d.ts.map +1 -1
- package/dist/views/Forms.js +119 -17
- package/dist/views/Forms.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/layout/sidebar-forms-badge.render.test.tsx +50 -0
- package/src/__tests__/layout/sidebar-submenu.render.test.tsx +9 -2
- package/src/__tests__/views/forms-list.render.test.tsx +141 -0
- package/src/components/forms/Drawer.tsx +70 -0
- package/src/components/forms/EmbedPanel.tsx +173 -0
- package/src/components/forms/FieldsPanel.tsx +385 -0
- package/src/components/forms/FormSchemaDrawer.tsx +185 -0
- package/src/components/forms/NotificationsPanel.tsx +240 -0
- package/src/components/forms/primitives.tsx +200 -0
- package/src/layout/Sidebar.tsx +72 -6
- package/src/lib/forms-events.ts +32 -0
- package/src/lib/forms-service.ts +244 -0
- package/src/views/Forms.tsx +343 -106
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forms service — typed client over the cms-core Forms REST endpoints.
|
|
3
|
+
*
|
|
4
|
+
* Every Forms admin component reads/writes through this module (never raw
|
|
5
|
+
* `cmsApi` / `fetch`), so the network contract lives in one place. Read
|
|
6
|
+
* fetchers re-surface `cmsApi`'s `{ error }` as a thrown error (so the
|
|
7
|
+
* resource hook can show error/retry UI); mutations return `{ error? }`.
|
|
8
|
+
*
|
|
9
|
+
* The endpoint paths here are the contract implemented by cms-core
|
|
10
|
+
* `packages/cms-core/src/api/handlers.ts` (PR 2). Domain types are re-used
|
|
11
|
+
* from `@actuate-media/cms-core` so the admin can never drift from the server.
|
|
12
|
+
*/
|
|
13
|
+
import { cmsApi } from './api.js'
|
|
14
|
+
import type {
|
|
15
|
+
FormDefinition,
|
|
16
|
+
FormSchemaVersion,
|
|
17
|
+
FormField,
|
|
18
|
+
FormNotificationSettings,
|
|
19
|
+
FormEmbedSettings,
|
|
20
|
+
FormStatus,
|
|
21
|
+
FormStats,
|
|
22
|
+
FormsSidebarCount,
|
|
23
|
+
FetchFormsParams,
|
|
24
|
+
} from '@actuate-media/cms-core'
|
|
25
|
+
|
|
26
|
+
export type {
|
|
27
|
+
FormDefinition,
|
|
28
|
+
FormSchemaVersion,
|
|
29
|
+
FormField,
|
|
30
|
+
FormFieldType,
|
|
31
|
+
FormFieldOption,
|
|
32
|
+
FieldValidationRules,
|
|
33
|
+
FormNotificationSettings,
|
|
34
|
+
FormEmbedSettings,
|
|
35
|
+
FormStatus,
|
|
36
|
+
FormStats,
|
|
37
|
+
FormsSidebarCount,
|
|
38
|
+
} from '@actuate-media/cms-core'
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* `cmsApi` never throws — it returns `{ error, status }` on a network/HTTP
|
|
42
|
+
* failure. Read (GET) fetchers must re-surface that as a thrown error so the
|
|
43
|
+
* resource hook can distinguish "request failed" from "empty result".
|
|
44
|
+
*/
|
|
45
|
+
function throwIfError(res: { error?: string }): void {
|
|
46
|
+
if (res.error) throw new Error(res.error)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A form as returned by `GET /forms` — the typed definition plus the legacy
|
|
51
|
+
* numeric aliases the list endpoint adds for backward compatibility.
|
|
52
|
+
*/
|
|
53
|
+
export interface FormListItem extends FormDefinition {
|
|
54
|
+
/** `fields.length` precomputed by the server. */
|
|
55
|
+
fieldCount?: number
|
|
56
|
+
/** Alias of `totalEntries`. */
|
|
57
|
+
submissions?: number
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface FormSchemaResult {
|
|
61
|
+
fields: FormField[]
|
|
62
|
+
activeVersionId: string | null
|
|
63
|
+
versions: FormSchemaVersion[]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ─── Forms list / stats / sidebar ────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
function buildFormsQuery(params: FetchFormsParams): string {
|
|
69
|
+
const qs = new URLSearchParams()
|
|
70
|
+
if (params.search) qs.set('search', params.search)
|
|
71
|
+
if (params.status) qs.set('status', params.status)
|
|
72
|
+
if (typeof params.notifyEnabled === 'boolean') {
|
|
73
|
+
qs.set('notifyEnabled', String(params.notifyEnabled))
|
|
74
|
+
}
|
|
75
|
+
if (params.sortBy) qs.set('sortBy', params.sortBy)
|
|
76
|
+
if (params.sortDirection) qs.set('sortDirection', params.sortDirection)
|
|
77
|
+
if (params.page) qs.set('page', String(params.page))
|
|
78
|
+
if (params.pageSize) qs.set('pageSize', String(params.pageSize))
|
|
79
|
+
const str = qs.toString()
|
|
80
|
+
return str ? `?${str}` : ''
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function fetchForms(params: FetchFormsParams = {}): Promise<FormListItem[]> {
|
|
84
|
+
const res = await cmsApi<FormListItem[]>(`/forms${buildFormsQuery(params)}`)
|
|
85
|
+
throwIfError(res)
|
|
86
|
+
return res.data ?? []
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export async function fetchFormById(id: string): Promise<FormDefinition | null> {
|
|
90
|
+
const res = await cmsApi<FormDefinition>(`/forms/${encodeURIComponent(id)}`)
|
|
91
|
+
throwIfError(res)
|
|
92
|
+
return res.data ?? null
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const EMPTY_STATS: FormStats = {
|
|
96
|
+
totalForms: 0,
|
|
97
|
+
activeForms: 0,
|
|
98
|
+
totalEntries: 0,
|
|
99
|
+
unreadEntries: 0,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function fetchFormStats(): Promise<FormStats> {
|
|
103
|
+
const res = await cmsApi<FormStats>('/forms/stats')
|
|
104
|
+
throwIfError(res)
|
|
105
|
+
return res.data ?? EMPTY_STATS
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function fetchFormsSidebarCounts(): Promise<FormsSidebarCount[]> {
|
|
109
|
+
const res = await cmsApi<FormsSidebarCount[]>('/forms/sidebar-counts')
|
|
110
|
+
throwIfError(res)
|
|
111
|
+
return res.data ?? []
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ─── Form mutations ──────────────────────────────────────────────────────
|
|
115
|
+
|
|
116
|
+
export interface CreateFormPayload {
|
|
117
|
+
name: string
|
|
118
|
+
slug?: string
|
|
119
|
+
description?: string
|
|
120
|
+
status?: FormStatus
|
|
121
|
+
template?: string
|
|
122
|
+
fields?: FormField[]
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export async function createForm(
|
|
126
|
+
payload: CreateFormPayload,
|
|
127
|
+
): Promise<{ data?: FormDefinition; error?: string }> {
|
|
128
|
+
const res = await cmsApi<FormDefinition>('/forms', {
|
|
129
|
+
method: 'POST',
|
|
130
|
+
body: JSON.stringify(payload),
|
|
131
|
+
})
|
|
132
|
+
return { data: res.data, error: res.error }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export async function updateForm(
|
|
136
|
+
id: string,
|
|
137
|
+
payload: Partial<Pick<FormDefinition, 'name' | 'slug' | 'description' | 'status'>>,
|
|
138
|
+
): Promise<{ data?: FormDefinition; error?: string }> {
|
|
139
|
+
const res = await cmsApi<FormDefinition>(`/forms/${encodeURIComponent(id)}`, {
|
|
140
|
+
method: 'PATCH',
|
|
141
|
+
body: JSON.stringify(payload),
|
|
142
|
+
})
|
|
143
|
+
return { data: res.data, error: res.error }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export async function duplicateForm(
|
|
147
|
+
id: string,
|
|
148
|
+
): Promise<{ data?: FormDefinition; error?: string }> {
|
|
149
|
+
const res = await cmsApi<FormDefinition>(`/forms/${encodeURIComponent(id)}/duplicate`, {
|
|
150
|
+
method: 'POST',
|
|
151
|
+
})
|
|
152
|
+
return { data: res.data, error: res.error }
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Move a form to the archived state (hidden from embed pickers, no new submissions). */
|
|
156
|
+
export async function archiveForm(id: string): Promise<{ error?: string }> {
|
|
157
|
+
return updateForm(id, { status: 'archived' })
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Restore an archived form back to draft. */
|
|
161
|
+
export async function restoreForm(id: string): Promise<{ error?: string }> {
|
|
162
|
+
return updateForm(id, { status: 'draft' })
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export async function deleteForm(
|
|
166
|
+
id: string,
|
|
167
|
+
opts: { force?: boolean } = {},
|
|
168
|
+
): Promise<{ error?: string }> {
|
|
169
|
+
const qs = opts.force ? '?force=true' : ''
|
|
170
|
+
const res = await cmsApi(`/forms/${encodeURIComponent(id)}${qs}`, { method: 'DELETE' })
|
|
171
|
+
return { error: res.error }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ─── Schema ──────────────────────────────────────────────────────────────
|
|
175
|
+
|
|
176
|
+
export async function fetchFormSchema(id: string): Promise<FormSchemaResult> {
|
|
177
|
+
const res = await cmsApi<FormSchemaResult>(`/forms/${encodeURIComponent(id)}/schema`)
|
|
178
|
+
throwIfError(res)
|
|
179
|
+
return res.data ?? { fields: [], activeVersionId: null, versions: [] }
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export async function saveFormSchema(
|
|
183
|
+
id: string,
|
|
184
|
+
fields: FormField[],
|
|
185
|
+
notes?: string,
|
|
186
|
+
): Promise<{ data?: { form: FormDefinition; version: FormSchemaVersion }; error?: string }> {
|
|
187
|
+
const res = await cmsApi<{ form: FormDefinition; version: FormSchemaVersion }>(
|
|
188
|
+
`/forms/${encodeURIComponent(id)}/schema`,
|
|
189
|
+
{ method: 'PUT', body: JSON.stringify({ fields, notes }) },
|
|
190
|
+
)
|
|
191
|
+
return { data: res.data, error: res.error }
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ─── Notifications ─────────────────────────────────────────────────────────
|
|
195
|
+
|
|
196
|
+
export async function fetchNotificationSettings(id: string): Promise<FormNotificationSettings> {
|
|
197
|
+
const res = await cmsApi<FormNotificationSettings>(
|
|
198
|
+
`/forms/${encodeURIComponent(id)}/notifications`,
|
|
199
|
+
)
|
|
200
|
+
throwIfError(res)
|
|
201
|
+
return res.data ?? { enabled: false, recipients: [] }
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export async function updateNotificationSettings(
|
|
205
|
+
id: string,
|
|
206
|
+
payload: Partial<FormNotificationSettings>,
|
|
207
|
+
): Promise<{ data?: FormNotificationSettings; error?: string }> {
|
|
208
|
+
const res = await cmsApi<FormNotificationSettings>(
|
|
209
|
+
`/forms/${encodeURIComponent(id)}/notifications`,
|
|
210
|
+
{ method: 'PUT', body: JSON.stringify(payload) },
|
|
211
|
+
)
|
|
212
|
+
return { data: res.data, error: res.error }
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Lightweight notify on/off toggle used by the forms-list switch. */
|
|
216
|
+
export async function setNotifyEnabled(
|
|
217
|
+
id: string,
|
|
218
|
+
enabled: boolean,
|
|
219
|
+
): Promise<{ data?: { notifyEnabled: boolean }; error?: string }> {
|
|
220
|
+
const res = await cmsApi<{ notifyEnabled: boolean }>(`/forms/${encodeURIComponent(id)}/notify`, {
|
|
221
|
+
method: 'PUT',
|
|
222
|
+
body: JSON.stringify({ enabled }),
|
|
223
|
+
})
|
|
224
|
+
return { data: res.data, error: res.error }
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// ─── Embed ───────────────────────────────────────────────────────────────
|
|
228
|
+
|
|
229
|
+
export async function fetchEmbedSettings(id: string): Promise<FormEmbedSettings> {
|
|
230
|
+
const res = await cmsApi<FormEmbedSettings>(`/forms/${encodeURIComponent(id)}/embed`)
|
|
231
|
+
throwIfError(res)
|
|
232
|
+
return res.data ?? {}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export async function updateEmbedSettings(
|
|
236
|
+
id: string,
|
|
237
|
+
payload: Partial<FormEmbedSettings>,
|
|
238
|
+
): Promise<{ data?: FormEmbedSettings; error?: string }> {
|
|
239
|
+
const res = await cmsApi<FormEmbedSettings>(`/forms/${encodeURIComponent(id)}/embed`, {
|
|
240
|
+
method: 'PUT',
|
|
241
|
+
body: JSON.stringify(payload),
|
|
242
|
+
})
|
|
243
|
+
return { data: res.data, error: res.error }
|
|
244
|
+
}
|