@actuate-media/cms-admin 0.56.0 → 0.57.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 +21 -0
- package/dist/__tests__/components/posts-badges.test.js +3 -3
- package/dist/__tests__/components/posts-badges.test.js.map +1 -1
- package/dist/__tests__/views/ai-settings.render.test.js +34 -0
- package/dist/__tests__/views/ai-settings.render.test.js.map +1 -1
- package/dist/actuate-admin.css +1 -1
- package/dist/components/AICoauthorBubbleMenu.d.ts.map +1 -1
- package/dist/components/AICoauthorBubbleMenu.js +7 -3
- package/dist/components/AICoauthorBubbleMenu.js.map +1 -1
- package/dist/components/BrandVoiceAlignmentBadge.d.ts +6 -0
- package/dist/components/BrandVoiceAlignmentBadge.d.ts.map +1 -0
- package/dist/components/BrandVoiceAlignmentBadge.js +11 -0
- package/dist/components/BrandVoiceAlignmentBadge.js.map +1 -0
- package/dist/components/Breadcrumbs.js +2 -2
- package/dist/components/Breadcrumbs.js.map +1 -1
- package/dist/components/CoauthorResultPopover.d.ts +5 -1
- package/dist/components/CoauthorResultPopover.d.ts.map +1 -1
- package/dist/components/CoauthorResultPopover.js +35 -6
- package/dist/components/CoauthorResultPopover.js.map +1 -1
- package/dist/components/CommandPalette.d.ts.map +1 -1
- package/dist/components/CommandPalette.js +16 -12
- package/dist/components/CommandPalette.js.map +1 -1
- package/dist/components/CommentSidePanel.js +11 -11
- package/dist/components/CommentSidePanel.js.map +1 -1
- package/dist/components/ContentOverviewChart.js +2 -2
- package/dist/components/ContentOverviewChart.js.map +1 -1
- package/dist/components/FocalPointPicker.js +1 -1
- package/dist/components/FocalPointPicker.js.map +1 -1
- package/dist/components/LivePreview.d.ts.map +1 -1
- package/dist/components/LivePreview.js +5 -5
- package/dist/components/LivePreview.js.map +1 -1
- package/dist/components/LogoPlaceholder.js +1 -1
- package/dist/components/LogoPlaceholder.js.map +1 -1
- package/dist/components/MentionableTextarea.js +3 -3
- package/dist/components/MentionableTextarea.js.map +1 -1
- package/dist/components/NotificationBell.js +1 -1
- package/dist/components/NotificationBell.js.map +1 -1
- package/dist/components/OfflineStatus.js +8 -8
- package/dist/components/OfflineStatus.js.map +1 -1
- package/dist/components/PresenceChips.d.ts.map +1 -1
- package/dist/components/PresenceChips.js +10 -6
- package/dist/components/PresenceChips.js.map +1 -1
- package/dist/components/SEOConfigPanel.d.ts.map +1 -1
- package/dist/components/SEOConfigPanel.js +10 -10
- package/dist/components/SEOConfigPanel.js.map +1 -1
- package/dist/components/SEOPanel.js +12 -12
- package/dist/components/SEOPanel.js.map +1 -1
- package/dist/components/SEOPerformance.d.ts.map +1 -1
- package/dist/components/SEOPerformance.js +3 -3
- package/dist/components/SEOPerformance.js.map +1 -1
- package/dist/components/TipTapEditor.js +6 -6
- package/dist/components/TipTapEditor.js.map +1 -1
- package/dist/components/pages/tag-badges.js +6 -6
- package/dist/components/pages/tag-badges.js.map +1 -1
- package/dist/components/posts/badges.d.ts +0 -4
- package/dist/components/posts/badges.d.ts.map +1 -1
- package/dist/components/posts/badges.js +28 -42
- package/dist/components/posts/badges.js.map +1 -1
- package/dist/components/seo/SeoEditorDrawer.d.ts.map +1 -1
- package/dist/components/seo/SeoEditorDrawer.js +43 -2
- package/dist/components/seo/SeoEditorDrawer.js.map +1 -1
- package/dist/components/ui/Badge.d.ts +1 -1
- package/dist/components/ui/Badge.js +5 -5
- package/dist/components/ui/Badge.js.map +1 -1
- package/dist/components/ui/Toast.d.ts.map +1 -1
- package/dist/components/ui/Toast.js +4 -4
- package/dist/components/ui/Toast.js.map +1 -1
- package/dist/fields/ComponentBlockField.js +2 -2
- package/dist/fields/ComponentBlockField.js.map +1 -1
- package/dist/fields/FieldRenderer.js +1 -1
- package/dist/fields/FieldRenderer.js.map +1 -1
- package/dist/fields/PropInput.js +1 -1
- package/dist/fields/PropInput.js.map +1 -1
- package/dist/fields/RelationshipField.js +5 -5
- package/dist/fields/RelationshipField.js.map +1 -1
- package/dist/fields/ToggleField.js +1 -1
- package/dist/fields/ToggleField.js.map +1 -1
- package/dist/lib/brand-voice-client.d.ts +21 -0
- package/dist/lib/brand-voice-client.d.ts.map +1 -0
- package/dist/lib/brand-voice-client.js +27 -0
- package/dist/lib/brand-voice-client.js.map +1 -0
- package/dist/lib/coauthor-client.d.ts +2 -0
- package/dist/lib/coauthor-client.d.ts.map +1 -1
- package/dist/lib/coauthor-client.js +1 -0
- package/dist/lib/coauthor-client.js.map +1 -1
- package/dist/lib/seo-service.d.ts +12 -0
- package/dist/lib/seo-service.d.ts.map +1 -1
- package/dist/lib/seo-service.js +0 -10
- package/dist/lib/seo-service.js.map +1 -1
- package/dist/views/Dashboard.js +5 -5
- package/dist/views/Dashboard.js.map +1 -1
- package/dist/views/DocumentEdit.d.ts.map +1 -1
- package/dist/views/DocumentEdit.js +5 -5
- package/dist/views/DocumentEdit.js.map +1 -1
- package/dist/views/ForgotPassword.d.ts.map +1 -1
- package/dist/views/ForgotPassword.js +3 -2
- package/dist/views/ForgotPassword.js.map +1 -1
- package/dist/views/FormEditor.d.ts.map +1 -1
- package/dist/views/FormEditor.js +5 -5
- package/dist/views/FormEditor.js.map +1 -1
- package/dist/views/Login.d.ts.map +1 -1
- package/dist/views/Login.js +17 -17
- package/dist/views/Login.js.map +1 -1
- package/dist/views/PageEditor.d.ts.map +1 -1
- package/dist/views/PageEditor.js +2 -2
- package/dist/views/PageEditor.js.map +1 -1
- package/dist/views/Pages/PagesListView.js +6 -6
- package/dist/views/Pages/PagesListView.js.map +1 -1
- package/dist/views/Pages.d.ts.map +1 -1
- package/dist/views/Pages.js +16 -19
- package/dist/views/Pages.js.map +1 -1
- package/dist/views/ResetPassword.d.ts.map +1 -1
- package/dist/views/ResetPassword.js +3 -2
- package/dist/views/ResetPassword.js.map +1 -1
- package/dist/views/Settings.js +2 -2
- package/dist/views/Settings.js.map +1 -1
- package/dist/views/SetupWizard.d.ts.map +1 -1
- package/dist/views/SetupWizard.js +6 -5
- package/dist/views/SetupWizard.js.map +1 -1
- package/dist/views/page-builder/DesignScore.js +12 -12
- package/dist/views/page-builder/DesignScore.js.map +1 -1
- package/dist/views/page-builder/SEOPanel.js +9 -9
- package/dist/views/page-builder/SEOPanel.js.map +1 -1
- package/dist/views/page-editor/EditorCanvas.js +1 -1
- package/dist/views/page-editor/EditorCanvas.js.map +1 -1
- package/dist/views/page-editor/EditorTopBar.js +2 -2
- package/dist/views/page-editor/EditorTopBar.js.map +1 -1
- package/dist/views/page-editor/PagePreview.js +1 -1
- package/dist/views/page-editor/PagePreview.js.map +1 -1
- package/dist/views/page-editor/SectionInspector.js +1 -1
- package/dist/views/page-editor/SectionInspector.js.map +1 -1
- package/dist/views/page-editor/SitePreviewFrame.js +1 -1
- package/dist/views/page-editor/SitePreviewFrame.js.map +1 -1
- package/dist/views/page-editor/sections/ArticleBodySection.js +1 -1
- package/dist/views/page-editor/sections/ArticleBodySection.js.map +1 -1
- package/dist/views/page-editor/sections/AuthorBioSection.js +2 -2
- package/dist/views/page-editor/sections/AuthorBioSection.js.map +1 -1
- package/dist/views/page-editor/sections/ByTheNumbersSection.js +1 -1
- package/dist/views/page-editor/sections/ByTheNumbersSection.js.map +1 -1
- package/dist/views/page-editor/sections/CtaBannerSection.js +1 -1
- package/dist/views/page-editor/sections/CtaBannerSection.js.map +1 -1
- package/dist/views/page-editor/sections/EmbedSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/EmbedSection.js +1 -1
- package/dist/views/page-editor/sections/EmbedSection.js.map +1 -1
- package/dist/views/page-editor/sections/FeatureHighlightSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/FeatureHighlightSection.js +2 -2
- package/dist/views/page-editor/sections/FeatureHighlightSection.js.map +1 -1
- package/dist/views/page-editor/sections/GallerySection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/GallerySection.js +1 -1
- package/dist/views/page-editor/sections/GallerySection.js.map +1 -1
- package/dist/views/page-editor/sections/HeroBannerSection.js +1 -1
- package/dist/views/page-editor/sections/HeroBannerSection.js.map +1 -1
- package/dist/views/page-editor/sections/MissionSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/MissionSection.js +1 -1
- package/dist/views/page-editor/sections/MissionSection.js.map +1 -1
- package/dist/views/page-editor/sections/PullQuoteSection.js +1 -1
- package/dist/views/page-editor/sections/PullQuoteSection.js.map +1 -1
- package/dist/views/page-editor/sections/RelatedPostsSection.js +1 -1
- package/dist/views/page-editor/sections/RelatedPostsSection.js.map +1 -1
- package/dist/views/page-editor/sections/SectionRenderer.js +1 -1
- package/dist/views/page-editor/sections/SectionRenderer.js.map +1 -1
- package/dist/views/page-editor/sections/parts.js +6 -6
- package/dist/views/page-editor/sections/parts.js.map +1 -1
- package/dist/views/post-editor/PostEditorCanvas.js +2 -2
- package/dist/views/post-editor/PostEditorCanvas.js.map +1 -1
- package/dist/views/post-editor/PostHeader.js +2 -2
- package/dist/views/post-editor/PostHeader.js.map +1 -1
- package/dist/views/post-editor/PostHeaderPanel.js +1 -1
- package/dist/views/post-editor/PostHeaderPanel.js.map +1 -1
- package/dist/views/post-editor/PostPreview.js +1 -1
- package/dist/views/post-editor/PostPreview.js.map +1 -1
- package/dist/views/profile/AccountSecurity.js +1 -1
- package/dist/views/profile/AccountSecurity.js.map +1 -1
- package/dist/views/settings/AISettingsTab.d.ts +3 -1
- package/dist/views/settings/AISettingsTab.d.ts.map +1 -1
- package/dist/views/settings/AISettingsTab.js +2 -2
- package/dist/views/settings/AISettingsTab.js.map +1 -1
- package/dist/views/settings/BrandVoiceCard.d.ts +39 -2
- package/dist/views/settings/BrandVoiceCard.d.ts.map +1 -1
- package/dist/views/settings/BrandVoiceCard.js +215 -3
- package/dist/views/settings/BrandVoiceCard.js.map +1 -1
- package/dist/views/settings/brand-voice/BrandVoiceAlignmentPanel.d.ts +13 -0
- package/dist/views/settings/brand-voice/BrandVoiceAlignmentPanel.d.ts.map +1 -0
- package/dist/views/settings/brand-voice/BrandVoiceAlignmentPanel.js +74 -0
- package/dist/views/settings/brand-voice/BrandVoiceAlignmentPanel.js.map +1 -0
- package/dist/views/settings/brand-voice/BrandVoiceRulesSections.d.ts +7 -0
- package/dist/views/settings/brand-voice/BrandVoiceRulesSections.d.ts.map +1 -0
- package/dist/views/settings/brand-voice/BrandVoiceRulesSections.js +54 -0
- package/dist/views/settings/brand-voice/BrandVoiceRulesSections.js.map +1 -0
- package/dist/views/settings/components.js +1 -1
- package/dist/views/settings/components.js.map +1 -1
- package/dist/views/settings/useAiSettings.d.ts +32 -0
- package/dist/views/settings/useAiSettings.d.ts.map +1 -1
- package/dist/views/settings/useAiSettings.js +85 -3
- package/dist/views/settings/useAiSettings.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/components/posts-badges.test.tsx +3 -3
- package/src/__tests__/views/ai-settings.render.test.tsx +50 -0
- package/src/components/AICoauthorBubbleMenu.tsx +16 -4
- package/src/components/BrandVoiceAlignmentBadge.tsx +39 -0
- package/src/components/Breadcrumbs.tsx +5 -5
- package/src/components/CoauthorResultPopover.tsx +88 -27
- package/src/components/CommandPalette.tsx +53 -39
- package/src/components/CommentSidePanel.tsx +20 -20
- package/src/components/ContentOverviewChart.tsx +4 -4
- package/src/components/FocalPointPicker.tsx +2 -2
- package/src/components/LivePreview.tsx +20 -17
- package/src/components/LogoPlaceholder.tsx +1 -1
- package/src/components/MentionableTextarea.tsx +5 -5
- package/src/components/NotificationBell.tsx +10 -10
- package/src/components/OfflineStatus.tsx +8 -8
- package/src/components/PresenceChips.tsx +12 -8
- package/src/components/SEOConfigPanel.tsx +40 -38
- package/src/components/SEOPanel.tsx +15 -15
- package/src/components/SEOPerformance.tsx +25 -23
- package/src/components/TipTapEditor.tsx +7 -7
- package/src/components/pages/tag-badges.tsx +6 -6
- package/src/components/posts/badges.tsx +28 -46
- package/src/components/seo/SeoEditorDrawer.tsx +65 -1
- package/src/components/ui/Badge.tsx +5 -5
- package/src/components/ui/Toast.tsx +4 -5
- package/src/fields/ComponentBlockField.tsx +2 -2
- package/src/fields/FieldRenderer.tsx +1 -1
- package/src/fields/PropInput.tsx +1 -1
- package/src/fields/RelationshipField.tsx +5 -5
- package/src/fields/ToggleField.tsx +1 -1
- package/src/lib/brand-voice-client.ts +38 -0
- package/src/lib/coauthor-client.ts +4 -0
- package/src/lib/seo-service.ts +2 -0
- package/src/views/Dashboard.tsx +5 -5
- package/src/views/DocumentEdit.tsx +13 -11
- package/src/views/ForgotPassword.tsx +21 -18
- package/src/views/FormEditor.tsx +74 -69
- package/src/views/Login.tsx +45 -39
- package/src/views/PageEditor.tsx +26 -22
- package/src/views/Pages/PagesListView.tsx +6 -6
- package/src/views/Pages.tsx +64 -65
- package/src/views/ResetPassword.tsx +32 -29
- package/src/views/Settings.tsx +3 -3
- package/src/views/SetupWizard.tsx +42 -33
- package/src/views/page-builder/DesignScore.tsx +12 -12
- package/src/views/page-builder/SEOPanel.tsx +12 -12
- package/src/views/page-editor/EditorCanvas.tsx +1 -1
- package/src/views/page-editor/EditorTopBar.tsx +2 -2
- package/src/views/page-editor/PagePreview.tsx +1 -1
- package/src/views/page-editor/SectionInspector.tsx +1 -1
- package/src/views/page-editor/SitePreviewFrame.tsx +1 -1
- package/src/views/page-editor/sections/ArticleBodySection.tsx +5 -5
- package/src/views/page-editor/sections/AuthorBioSection.tsx +6 -6
- package/src/views/page-editor/sections/ByTheNumbersSection.tsx +5 -5
- package/src/views/page-editor/sections/CtaBannerSection.tsx +1 -1
- package/src/views/page-editor/sections/EmbedSection.tsx +6 -4
- package/src/views/page-editor/sections/FeatureHighlightSection.tsx +7 -5
- package/src/views/page-editor/sections/GallerySection.tsx +6 -4
- package/src/views/page-editor/sections/HeroBannerSection.tsx +2 -2
- package/src/views/page-editor/sections/MissionSection.tsx +4 -2
- package/src/views/page-editor/sections/PullQuoteSection.tsx +4 -4
- package/src/views/page-editor/sections/RelatedPostsSection.tsx +5 -5
- package/src/views/page-editor/sections/SectionRenderer.tsx +2 -2
- package/src/views/page-editor/sections/parts.tsx +6 -6
- package/src/views/post-editor/PostEditorCanvas.tsx +4 -4
- package/src/views/post-editor/PostHeader.tsx +5 -5
- package/src/views/post-editor/PostHeaderPanel.tsx +1 -1
- package/src/views/post-editor/PostPreview.tsx +1 -1
- package/src/views/profile/AccountSecurity.tsx +1 -1
- package/src/views/settings/AISettingsTab.tsx +13 -2
- package/src/views/settings/BrandVoiceCard.tsx +592 -76
- package/src/views/settings/brand-voice/BrandVoiceAlignmentPanel.tsx +205 -0
- package/src/views/settings/brand-voice/BrandVoiceRulesSections.tsx +321 -0
- package/src/views/settings/components.tsx +1 -1
- package/src/views/settings/useAiSettings.ts +134 -3
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
4
|
+
import {
|
|
5
|
+
defaultStructuredProfile,
|
|
6
|
+
parseStructuredProfile,
|
|
7
|
+
type BrandAlignmentScoreResult,
|
|
8
|
+
type BrandVoiceStructuredProfile,
|
|
9
|
+
} from '@actuate-media/cms-core/brand-voice'
|
|
4
10
|
import { cmsApi } from '../../lib/api.js'
|
|
5
11
|
|
|
6
12
|
/**
|
|
@@ -101,8 +107,15 @@ export interface BrandVoiceForm {
|
|
|
101
107
|
audience: string
|
|
102
108
|
guidelines: string
|
|
103
109
|
sampleText: string
|
|
110
|
+
document: string
|
|
111
|
+
version: number
|
|
112
|
+
status: 'draft' | 'reviewed' | 'approved'
|
|
113
|
+
generatedFrom: string[]
|
|
114
|
+
structured: BrandVoiceStructuredProfile
|
|
104
115
|
}
|
|
105
116
|
|
|
117
|
+
export type BrandVoiceEditMode = 'form' | 'document'
|
|
118
|
+
|
|
106
119
|
const EMPTY_BRAND_VOICE: BrandVoiceForm = {
|
|
107
120
|
enabled: false,
|
|
108
121
|
name: '',
|
|
@@ -110,6 +123,11 @@ const EMPTY_BRAND_VOICE: BrandVoiceForm = {
|
|
|
110
123
|
audience: '',
|
|
111
124
|
guidelines: '',
|
|
112
125
|
sampleText: '',
|
|
126
|
+
document: '',
|
|
127
|
+
version: 1,
|
|
128
|
+
status: 'draft',
|
|
129
|
+
generatedFrom: [],
|
|
130
|
+
structured: defaultStructuredProfile(),
|
|
113
131
|
}
|
|
114
132
|
|
|
115
133
|
/** The buffered (Save-bar) portion of AI settings. */
|
|
@@ -134,6 +152,10 @@ interface AiSettingsResponse {
|
|
|
134
152
|
audience?: string
|
|
135
153
|
guidelines?: string
|
|
136
154
|
sampleText?: string
|
|
155
|
+
document?: string
|
|
156
|
+
version?: number
|
|
157
|
+
status?: 'draft' | 'reviewed' | 'approved'
|
|
158
|
+
generatedFrom?: string[]
|
|
137
159
|
}
|
|
138
160
|
}
|
|
139
161
|
providers: AiProviderView[]
|
|
@@ -162,6 +184,13 @@ function formFromResponse(res: AiSettingsResponse): AiSettingsForm {
|
|
|
162
184
|
audience: res.settings.brandVoice?.audience ?? '',
|
|
163
185
|
guidelines: res.settings.brandVoice?.guidelines ?? '',
|
|
164
186
|
sampleText: res.settings.brandVoice?.sampleText ?? '',
|
|
187
|
+
document: res.settings.brandVoice?.document ?? '',
|
|
188
|
+
version: res.settings.brandVoice?.version ?? 1,
|
|
189
|
+
status: res.settings.brandVoice?.status ?? 'draft',
|
|
190
|
+
generatedFrom: res.settings.brandVoice?.generatedFrom ?? [],
|
|
191
|
+
structured: res.settings.brandVoice?.document?.trim()
|
|
192
|
+
? parseStructuredProfile(res.settings.brandVoice.document)
|
|
193
|
+
: defaultStructuredProfile(),
|
|
165
194
|
},
|
|
166
195
|
}
|
|
167
196
|
}
|
|
@@ -182,7 +211,8 @@ function brandVoiceIsEmpty(bv: BrandVoiceForm): boolean {
|
|
|
182
211
|
!bv.tone.trim() &&
|
|
183
212
|
!bv.audience.trim() &&
|
|
184
213
|
!bv.guidelines.trim() &&
|
|
185
|
-
!bv.sampleText.trim()
|
|
214
|
+
!bv.sampleText.trim() &&
|
|
215
|
+
!bv.document.trim()
|
|
186
216
|
)
|
|
187
217
|
}
|
|
188
218
|
|
|
@@ -213,6 +243,25 @@ export interface UseAiSettings {
|
|
|
213
243
|
setFeatureModel: (key: string, modelId: string) => void
|
|
214
244
|
/** Patch one or more brand voice fields. */
|
|
215
245
|
setBrandVoice: (patch: Partial<BrandVoiceForm>) => void
|
|
246
|
+
/** Whether saves should trust the markdown document or quick-edit fields. */
|
|
247
|
+
brandVoiceEditMode: BrandVoiceEditMode
|
|
248
|
+
setBrandVoiceEditMode: (mode: BrandVoiceEditMode) => void
|
|
249
|
+
/** Scan published content and draft brand voice fields (does not save). */
|
|
250
|
+
analyzeBrandVoice: (input: {
|
|
251
|
+
collections: string[]
|
|
252
|
+
urls?: string[]
|
|
253
|
+
uploads?: Array<{ name: string; text: string }>
|
|
254
|
+
}) => Promise<{
|
|
255
|
+
ok: boolean
|
|
256
|
+
draft?: Partial<BrandVoiceForm>
|
|
257
|
+
sampleCount?: number
|
|
258
|
+
error?: string
|
|
259
|
+
}>
|
|
260
|
+
scoreBrandVoice: (input: { content: string; channel?: string }) => Promise<{
|
|
261
|
+
ok: boolean
|
|
262
|
+
result?: BrandAlignmentScoreResult
|
|
263
|
+
error?: string
|
|
264
|
+
}>
|
|
216
265
|
dirty: boolean
|
|
217
266
|
budgetError: string | null
|
|
218
267
|
saveState: SaveState
|
|
@@ -248,6 +297,7 @@ export function useAiSettings(canEdit: boolean): UseAiSettings {
|
|
|
248
297
|
const [saveState, setSaveState] = useState<SaveState>('idle')
|
|
249
298
|
const [saveError, setSaveError] = useState<string | null>(null)
|
|
250
299
|
const [busyProviderId, setBusyProviderId] = useState<string | null>(null)
|
|
300
|
+
const [brandVoiceEditMode, setBrandVoiceEditMode] = useState<BrandVoiceEditMode>('form')
|
|
251
301
|
const [reloadKey, setReloadKey] = useState(0)
|
|
252
302
|
|
|
253
303
|
const loadModels = useCallback(async () => {
|
|
@@ -329,6 +379,56 @@ export function useAiSettings(canEdit: boolean): UseAiSettings {
|
|
|
329
379
|
setSaveState('idle')
|
|
330
380
|
}, [])
|
|
331
381
|
|
|
382
|
+
const analyzeBrandVoice = useCallback(
|
|
383
|
+
async (input: {
|
|
384
|
+
collections: string[]
|
|
385
|
+
urls?: string[]
|
|
386
|
+
uploads?: Array<{ name: string; text: string }>
|
|
387
|
+
}) => {
|
|
388
|
+
if (!canEdit) return { ok: false, error: 'You do not have permission to do this.' }
|
|
389
|
+
try {
|
|
390
|
+
const res = await cmsApi<{
|
|
391
|
+
draft: Partial<BrandVoiceForm> & { document?: string }
|
|
392
|
+
sampleCount: number
|
|
393
|
+
}>('/ai/brand-voice/analyze', {
|
|
394
|
+
method: 'POST',
|
|
395
|
+
body: JSON.stringify(input),
|
|
396
|
+
})
|
|
397
|
+
if (res.error) return { ok: false, error: res.error }
|
|
398
|
+
if (!res.data?.draft) return { ok: false, error: 'No draft profile was returned.' }
|
|
399
|
+
const structured = res.data.draft.document?.trim()
|
|
400
|
+
? parseStructuredProfile(res.data.draft.document)
|
|
401
|
+
: defaultStructuredProfile()
|
|
402
|
+
return {
|
|
403
|
+
ok: true,
|
|
404
|
+
draft: { ...res.data.draft, structured },
|
|
405
|
+
sampleCount: res.data.sampleCount,
|
|
406
|
+
}
|
|
407
|
+
} catch {
|
|
408
|
+
return { ok: false, error: 'Brand voice analysis failed.' }
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
[canEdit],
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
const scoreBrandVoice = useCallback(
|
|
415
|
+
async (input: { content: string; channel?: string }) => {
|
|
416
|
+
if (!canEdit) return { ok: false, error: 'You do not have permission to do this.' }
|
|
417
|
+
try {
|
|
418
|
+
const res = await cmsApi<BrandAlignmentScoreResult>('/ai/brand-voice/score', {
|
|
419
|
+
method: 'POST',
|
|
420
|
+
body: JSON.stringify(input),
|
|
421
|
+
})
|
|
422
|
+
if (res.error) return { ok: false, error: res.error }
|
|
423
|
+
if (!res.data) return { ok: false, error: 'No score was returned.' }
|
|
424
|
+
return { ok: true, result: res.data }
|
|
425
|
+
} catch {
|
|
426
|
+
return { ok: false, error: 'Brand voice scoring failed.' }
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
[canEdit],
|
|
430
|
+
)
|
|
431
|
+
|
|
332
432
|
const budgetError = useMemo(() => {
|
|
333
433
|
if (form.monthlyTokenLimit === '') return null
|
|
334
434
|
const n = Number(form.monthlyTokenLimit)
|
|
@@ -348,8 +448,30 @@ export function useAiSettings(canEdit: boolean): UseAiSettings {
|
|
|
348
448
|
'audience',
|
|
349
449
|
'guidelines',
|
|
350
450
|
'sampleText',
|
|
451
|
+
'document',
|
|
452
|
+
'version',
|
|
453
|
+
'status',
|
|
454
|
+
'generatedFrom',
|
|
455
|
+
'structured',
|
|
351
456
|
]
|
|
352
|
-
if (
|
|
457
|
+
if (
|
|
458
|
+
bvKeys.some((k) => {
|
|
459
|
+
if (k === 'generatedFrom') {
|
|
460
|
+
return (
|
|
461
|
+
JSON.stringify(form.brandVoice.generatedFrom) !==
|
|
462
|
+
JSON.stringify(baseline.brandVoice.generatedFrom)
|
|
463
|
+
)
|
|
464
|
+
}
|
|
465
|
+
if (k === 'structured') {
|
|
466
|
+
return (
|
|
467
|
+
JSON.stringify(form.brandVoice.structured) !==
|
|
468
|
+
JSON.stringify(baseline.brandVoice.structured)
|
|
469
|
+
)
|
|
470
|
+
}
|
|
471
|
+
return form.brandVoice[k] !== baseline.brandVoice[k]
|
|
472
|
+
})
|
|
473
|
+
)
|
|
474
|
+
return true
|
|
353
475
|
return form.features.some((f, i) => {
|
|
354
476
|
const base = baseline.features[i]
|
|
355
477
|
return f.enabled !== base?.enabled || (f.modelId ?? '') !== (base?.modelId ?? '')
|
|
@@ -384,6 +506,11 @@ export function useAiSettings(canEdit: boolean): UseAiSettings {
|
|
|
384
506
|
audience: form.brandVoice.audience,
|
|
385
507
|
guidelines: form.brandVoice.guidelines,
|
|
386
508
|
sampleText: form.brandVoice.sampleText,
|
|
509
|
+
document: form.brandVoice.document,
|
|
510
|
+
version: form.brandVoice.version,
|
|
511
|
+
status: form.brandVoice.status,
|
|
512
|
+
generatedFrom: form.brandVoice.generatedFrom,
|
|
513
|
+
preferDocument: brandVoiceEditMode === 'document',
|
|
387
514
|
},
|
|
388
515
|
}),
|
|
389
516
|
})
|
|
@@ -397,7 +524,7 @@ export function useAiSettings(canEdit: boolean): UseAiSettings {
|
|
|
397
524
|
setSaveState('error')
|
|
398
525
|
setSaveError(err instanceof Error ? err.message : 'Failed to save AI settings.')
|
|
399
526
|
}
|
|
400
|
-
}, [canEdit, budgetError, form])
|
|
527
|
+
}, [canEdit, budgetError, form, brandVoiceEditMode])
|
|
401
528
|
|
|
402
529
|
const reset = useCallback(() => {
|
|
403
530
|
setForm(baseline)
|
|
@@ -535,6 +662,10 @@ export function useAiSettings(canEdit: boolean): UseAiSettings {
|
|
|
535
662
|
toggleFeature,
|
|
536
663
|
setFeatureModel,
|
|
537
664
|
setBrandVoice,
|
|
665
|
+
analyzeBrandVoice,
|
|
666
|
+
scoreBrandVoice,
|
|
667
|
+
brandVoiceEditMode,
|
|
668
|
+
setBrandVoiceEditMode,
|
|
538
669
|
dirty,
|
|
539
670
|
budgetError,
|
|
540
671
|
saveState,
|