@actuate-media/cms-admin 0.35.0 → 0.36.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__/views/ai-settings.render.test.js +22 -0
- package/dist/__tests__/views/ai-settings.render.test.js.map +1 -1
- package/dist/actuate-admin.css +1 -1
- package/dist/views/settings/AISettingsTab.d.ts.map +1 -1
- package/dist/views/settings/AISettingsTab.js +2 -1
- package/dist/views/settings/AISettingsTab.js.map +1 -1
- package/dist/views/settings/BrandVoiceCard.d.ts +12 -0
- package/dist/views/settings/BrandVoiceCard.d.ts.map +1 -0
- package/dist/views/settings/BrandVoiceCard.js +27 -0
- package/dist/views/settings/BrandVoiceCard.js.map +1 -0
- package/dist/views/settings/useAiSettings.d.ts +12 -0
- package/dist/views/settings/useAiSettings.d.ts.map +1 -1
- package/dist/views/settings/useAiSettings.js +51 -0
- package/dist/views/settings/useAiSettings.js.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/views/ai-settings.render.test.tsx +36 -0
- package/src/views/settings/AISettingsTab.tsx +2 -0
- package/src/views/settings/BrandVoiceCard.tsx +152 -0
- package/src/views/settings/useAiSettings.ts +76 -0
|
@@ -93,12 +93,32 @@ export interface AiUsageView {
|
|
|
93
93
|
runCount: number
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
/** Editable brand voice fields (the buffered Save-bar portion). */
|
|
97
|
+
export interface BrandVoiceForm {
|
|
98
|
+
enabled: boolean
|
|
99
|
+
name: string
|
|
100
|
+
tone: string
|
|
101
|
+
audience: string
|
|
102
|
+
guidelines: string
|
|
103
|
+
sampleText: string
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const EMPTY_BRAND_VOICE: BrandVoiceForm = {
|
|
107
|
+
enabled: false,
|
|
108
|
+
name: '',
|
|
109
|
+
tone: '',
|
|
110
|
+
audience: '',
|
|
111
|
+
guidelines: '',
|
|
112
|
+
sampleText: '',
|
|
113
|
+
}
|
|
114
|
+
|
|
96
115
|
/** The buffered (Save-bar) portion of AI settings. */
|
|
97
116
|
export interface AiSettingsForm {
|
|
98
117
|
defaultProviderId: string
|
|
99
118
|
defaultModelId: string
|
|
100
119
|
features: AiFeatureView[]
|
|
101
120
|
monthlyTokenLimit: string
|
|
121
|
+
brandVoice: BrandVoiceForm
|
|
102
122
|
}
|
|
103
123
|
|
|
104
124
|
interface AiSettingsResponse {
|
|
@@ -107,6 +127,14 @@ interface AiSettingsResponse {
|
|
|
107
127
|
defaultModelId?: string
|
|
108
128
|
features: AiFeatureView[]
|
|
109
129
|
budget: { monthlyTokenLimit?: number }
|
|
130
|
+
brandVoice?: {
|
|
131
|
+
enabled?: boolean
|
|
132
|
+
name?: string
|
|
133
|
+
tone?: string
|
|
134
|
+
audience?: string
|
|
135
|
+
guidelines?: string
|
|
136
|
+
sampleText?: string
|
|
137
|
+
}
|
|
110
138
|
}
|
|
111
139
|
providers: AiProviderView[]
|
|
112
140
|
defaultModel: AiModelView | null
|
|
@@ -127,6 +155,14 @@ function formFromResponse(res: AiSettingsResponse): AiSettingsForm {
|
|
|
127
155
|
typeof res.settings.budget.monthlyTokenLimit === 'number'
|
|
128
156
|
? String(res.settings.budget.monthlyTokenLimit)
|
|
129
157
|
: '',
|
|
158
|
+
brandVoice: {
|
|
159
|
+
enabled: res.settings.brandVoice?.enabled ?? false,
|
|
160
|
+
name: res.settings.brandVoice?.name ?? '',
|
|
161
|
+
tone: res.settings.brandVoice?.tone ?? '',
|
|
162
|
+
audience: res.settings.brandVoice?.audience ?? '',
|
|
163
|
+
guidelines: res.settings.brandVoice?.guidelines ?? '',
|
|
164
|
+
sampleText: res.settings.brandVoice?.sampleText ?? '',
|
|
165
|
+
},
|
|
130
166
|
}
|
|
131
167
|
}
|
|
132
168
|
|
|
@@ -135,6 +171,19 @@ const EMPTY_FORM: AiSettingsForm = {
|
|
|
135
171
|
defaultModelId: '',
|
|
136
172
|
features: [],
|
|
137
173
|
monthlyTokenLimit: '',
|
|
174
|
+
brandVoice: { ...EMPTY_BRAND_VOICE },
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** A brand voice with no content and disabled clears the stored profile (send null). */
|
|
178
|
+
function brandVoiceIsEmpty(bv: BrandVoiceForm): boolean {
|
|
179
|
+
return (
|
|
180
|
+
!bv.enabled &&
|
|
181
|
+
!bv.name.trim() &&
|
|
182
|
+
!bv.tone.trim() &&
|
|
183
|
+
!bv.audience.trim() &&
|
|
184
|
+
!bv.guidelines.trim() &&
|
|
185
|
+
!bv.sampleText.trim()
|
|
186
|
+
)
|
|
138
187
|
}
|
|
139
188
|
|
|
140
189
|
export interface ProviderInput {
|
|
@@ -162,6 +211,8 @@ export interface UseAiSettings {
|
|
|
162
211
|
toggleFeature: (key: string, enabled: boolean) => void
|
|
163
212
|
/** Set a per-feature model override. Empty string reverts to the default model. */
|
|
164
213
|
setFeatureModel: (key: string, modelId: string) => void
|
|
214
|
+
/** Patch one or more brand voice fields. */
|
|
215
|
+
setBrandVoice: (patch: Partial<BrandVoiceForm>) => void
|
|
165
216
|
dirty: boolean
|
|
166
217
|
budgetError: string | null
|
|
167
218
|
saveState: SaveState
|
|
@@ -273,6 +324,11 @@ export function useAiSettings(canEdit: boolean): UseAiSettings {
|
|
|
273
324
|
setSaveState('idle')
|
|
274
325
|
}, [])
|
|
275
326
|
|
|
327
|
+
const setBrandVoice = useCallback((patch: Partial<BrandVoiceForm>) => {
|
|
328
|
+
setForm((prev) => ({ ...prev, brandVoice: { ...prev.brandVoice, ...patch } }))
|
|
329
|
+
setSaveState('idle')
|
|
330
|
+
}, [])
|
|
331
|
+
|
|
276
332
|
const budgetError = useMemo(() => {
|
|
277
333
|
if (form.monthlyTokenLimit === '') return null
|
|
278
334
|
const n = Number(form.monthlyTokenLimit)
|
|
@@ -285,6 +341,15 @@ export function useAiSettings(canEdit: boolean): UseAiSettings {
|
|
|
285
341
|
if (form.defaultModelId !== baseline.defaultModelId) return true
|
|
286
342
|
if (form.monthlyTokenLimit !== baseline.monthlyTokenLimit) return true
|
|
287
343
|
if (form.features.length !== baseline.features.length) return true
|
|
344
|
+
const bvKeys: (keyof BrandVoiceForm)[] = [
|
|
345
|
+
'enabled',
|
|
346
|
+
'name',
|
|
347
|
+
'tone',
|
|
348
|
+
'audience',
|
|
349
|
+
'guidelines',
|
|
350
|
+
'sampleText',
|
|
351
|
+
]
|
|
352
|
+
if (bvKeys.some((k) => form.brandVoice[k] !== baseline.brandVoice[k])) return true
|
|
288
353
|
return form.features.some((f, i) => {
|
|
289
354
|
const base = baseline.features[i]
|
|
290
355
|
return f.enabled !== base?.enabled || (f.modelId ?? '') !== (base?.modelId ?? '')
|
|
@@ -310,6 +375,16 @@ export function useAiSettings(canEdit: boolean): UseAiSettings {
|
|
|
310
375
|
monthlyTokenLimit:
|
|
311
376
|
form.monthlyTokenLimit === '' ? null : Number(form.monthlyTokenLimit),
|
|
312
377
|
},
|
|
378
|
+
brandVoice: brandVoiceIsEmpty(form.brandVoice)
|
|
379
|
+
? null
|
|
380
|
+
: {
|
|
381
|
+
enabled: form.brandVoice.enabled,
|
|
382
|
+
name: form.brandVoice.name,
|
|
383
|
+
tone: form.brandVoice.tone,
|
|
384
|
+
audience: form.brandVoice.audience,
|
|
385
|
+
guidelines: form.brandVoice.guidelines,
|
|
386
|
+
sampleText: form.brandVoice.sampleText,
|
|
387
|
+
},
|
|
313
388
|
}),
|
|
314
389
|
})
|
|
315
390
|
if (res.error) throw new Error(res.error)
|
|
@@ -459,6 +534,7 @@ export function useAiSettings(canEdit: boolean): UseAiSettings {
|
|
|
459
534
|
setMonthlyTokenLimit,
|
|
460
535
|
toggleFeature,
|
|
461
536
|
setFeatureModel,
|
|
537
|
+
setBrandVoice,
|
|
462
538
|
dirty,
|
|
463
539
|
budgetError,
|
|
464
540
|
saveState,
|