@actuate-media/cms-admin 0.36.0 → 0.37.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/appearance-settings.render.test.js +4 -4
- package/dist/__tests__/views/updates-tab.render.test.d.ts +2 -0
- package/dist/__tests__/views/updates-tab.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/updates-tab.render.test.js +132 -0
- package/dist/__tests__/views/updates-tab.render.test.js.map +1 -0
- package/dist/actuate-admin.css +1 -1
- package/dist/views/ApiKeys.d.ts.map +1 -1
- package/dist/views/ApiKeys.js +14 -13
- package/dist/views/ApiKeys.js.map +1 -1
- package/dist/views/Profile.d.ts.map +1 -1
- package/dist/views/Profile.js +1 -1
- package/dist/views/Profile.js.map +1 -1
- package/dist/views/Settings.d.ts.map +1 -1
- package/dist/views/Settings.js +16 -119
- package/dist/views/Settings.js.map +1 -1
- package/dist/views/Users.js +1 -1
- package/dist/views/Users.js.map +1 -1
- package/dist/views/settings/AISettingsTab.js +40 -42
- package/dist/views/settings/AISettingsTab.js.map +1 -1
- package/dist/views/settings/AdminThemeCard.js +1 -1
- package/dist/views/settings/AiFeaturesCard.d.ts.map +1 -1
- package/dist/views/settings/AiFeaturesCard.js +2 -3
- package/dist/views/settings/AiFeaturesCard.js.map +1 -1
- package/dist/views/settings/BrandPreviewCard.js +1 -1
- package/dist/views/settings/BrandVoiceCard.d.ts.map +1 -1
- package/dist/views/settings/BrandVoiceCard.js +2 -4
- package/dist/views/settings/BrandVoiceCard.js.map +1 -1
- package/dist/views/settings/BrandingCard.d.ts.map +1 -1
- package/dist/views/settings/BrandingCard.js +34 -19
- package/dist/views/settings/BrandingCard.js.map +1 -1
- package/dist/views/settings/GeneralSettingsTab.js +1 -1
- package/dist/views/settings/LanguageRegionCard.d.ts.map +1 -1
- package/dist/views/settings/LanguageRegionCard.js +2 -1
- package/dist/views/settings/LanguageRegionCard.js.map +1 -1
- package/dist/views/settings/MonthlyUsageCard.d.ts.map +1 -1
- package/dist/views/settings/MonthlyUsageCard.js +3 -3
- package/dist/views/settings/MonthlyUsageCard.js.map +1 -1
- package/dist/views/settings/ProviderConnectionCard.js +1 -1
- package/dist/views/settings/ProviderConnectionCard.js.map +1 -1
- package/dist/views/settings/SecurityStatusCard.d.ts.map +1 -1
- package/dist/views/settings/SecurityStatusCard.js +2 -3
- package/dist/views/settings/SecurityStatusCard.js.map +1 -1
- package/dist/views/settings/SecurityTab.js +1 -1
- package/dist/views/settings/SeoRobotsDefaultsCard.d.ts.map +1 -1
- package/dist/views/settings/SeoRobotsDefaultsCard.js +3 -4
- package/dist/views/settings/SeoRobotsDefaultsCard.js.map +1 -1
- package/dist/views/settings/SeoSettingsTab.d.ts.map +1 -1
- package/dist/views/settings/SeoSettingsTab.js +5 -8
- package/dist/views/settings/SeoSettingsTab.js.map +1 -1
- package/dist/views/settings/SiteIndexStatusCard.d.ts.map +1 -1
- package/dist/views/settings/SiteIndexStatusCard.js +3 -2
- package/dist/views/settings/SiteIndexStatusCard.js.map +1 -1
- package/dist/views/settings/SiteInformationCard.d.ts.map +1 -1
- package/dist/views/settings/SiteInformationCard.js +4 -4
- package/dist/views/settings/SiteInformationCard.js.map +1 -1
- package/dist/views/settings/TypographyMotionCard.js +1 -1
- package/dist/views/settings/UpdatesTab.d.ts +6 -0
- package/dist/views/settings/UpdatesTab.d.ts.map +1 -0
- package/dist/views/settings/UpdatesTab.js +80 -0
- package/dist/views/settings/UpdatesTab.js.map +1 -0
- package/dist/views/settings/components.d.ts +51 -1
- package/dist/views/settings/components.d.ts.map +1 -1
- package/dist/views/settings/components.js +33 -0
- package/dist/views/settings/components.js.map +1 -1
- package/dist/views/settings/securityPrimitives.d.ts +11 -3
- package/dist/views/settings/securityPrimitives.d.ts.map +1 -1
- package/dist/views/settings/securityPrimitives.js +10 -3
- package/dist/views/settings/securityPrimitives.js.map +1 -1
- package/dist/views/settings/useUpdates.d.ts +67 -0
- package/dist/views/settings/useUpdates.d.ts.map +1 -0
- package/dist/views/settings/useUpdates.js +127 -0
- package/dist/views/settings/useUpdates.js.map +1 -0
- package/package.json +2 -2
- package/src/__tests__/views/appearance-settings.render.test.tsx +4 -4
- package/src/__tests__/views/updates-tab.render.test.tsx +161 -0
- package/src/views/ApiKeys.tsx +190 -154
- package/src/views/Profile.tsx +4 -2
- package/src/views/Settings.tsx +26 -451
- package/src/views/Users.tsx +1 -1
- package/src/views/settings/AISettingsTab.tsx +64 -76
- package/src/views/settings/AdminThemeCard.tsx +1 -1
- package/src/views/settings/AiFeaturesCard.tsx +6 -12
- package/src/views/settings/BrandPreviewCard.tsx +1 -1
- package/src/views/settings/BrandVoiceCard.tsx +6 -19
- package/src/views/settings/BrandingCard.tsx +179 -180
- package/src/views/settings/GeneralSettingsTab.tsx +1 -1
- package/src/views/settings/LanguageRegionCard.tsx +5 -7
- package/src/views/settings/MonthlyUsageCard.tsx +6 -10
- package/src/views/settings/ProviderConnectionCard.tsx +1 -1
- package/src/views/settings/SecurityStatusCard.tsx +8 -11
- package/src/views/settings/SecurityTab.tsx +1 -1
- package/src/views/settings/SeoRobotsDefaultsCard.tsx +7 -14
- package/src/views/settings/SeoSettingsTab.tsx +17 -26
- package/src/views/settings/SiteIndexStatusCard.tsx +10 -22
- package/src/views/settings/SiteInformationCard.tsx +3 -9
- package/src/views/settings/TypographyMotionCard.tsx +1 -1
- package/src/views/settings/UpdatesTab.tsx +545 -0
- package/src/views/settings/components.tsx +123 -1
- package/src/views/settings/securityPrimitives.tsx +14 -9
- package/src/views/settings/useUpdates.ts +183 -0
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { AlertTriangle, Check, CircleHelp, X } from 'lucide-react'
|
|
4
4
|
import type { ReactNode } from 'react'
|
|
5
5
|
import type { CheckStatus } from '@actuate-media/cms-core/security-center'
|
|
6
|
+
import { SettingsCard } from './components.js'
|
|
6
7
|
|
|
7
8
|
/** Small status icon used in check/summary rows. Text label always accompanies it. */
|
|
8
9
|
export function CheckIcon({ status }: { status: CheckStatus }) {
|
|
@@ -51,25 +52,29 @@ export function StatusRow({
|
|
|
51
52
|
)
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* Section wrapper for the Security tab. Delegates to the shared
|
|
57
|
+
* {@link SettingsCard} so Security matches the canonical Settings design
|
|
58
|
+
* language (p-6, text-lg title, muted description). `headingId` is accepted
|
|
59
|
+
* for backward compatibility but ignored — {@link SettingsCard} generates and
|
|
60
|
+
* wires its own accessible heading id.
|
|
61
|
+
*/
|
|
55
62
|
export function SecurityCard({
|
|
56
|
-
headingId,
|
|
57
63
|
title,
|
|
58
64
|
description,
|
|
65
|
+
actions,
|
|
59
66
|
children,
|
|
60
67
|
}: {
|
|
61
|
-
|
|
68
|
+
/** @deprecated No longer used; SettingsCard manages the heading id. */
|
|
69
|
+
headingId?: string
|
|
62
70
|
title: string
|
|
63
71
|
description: string
|
|
72
|
+
actions?: ReactNode
|
|
64
73
|
children: ReactNode
|
|
65
74
|
}) {
|
|
66
75
|
return (
|
|
67
|
-
<
|
|
68
|
-
<h3 id={headingId} className="text-foreground mb-1 text-base font-medium">
|
|
69
|
-
{title}
|
|
70
|
-
</h3>
|
|
71
|
-
<p className="text-muted-foreground mb-4 text-sm">{description}</p>
|
|
76
|
+
<SettingsCard title={title} description={description} actions={actions}>
|
|
72
77
|
{children}
|
|
73
|
-
</
|
|
78
|
+
</SettingsCard>
|
|
74
79
|
)
|
|
75
80
|
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useState } from 'react'
|
|
4
|
+
import { toast } from 'sonner'
|
|
5
|
+
|
|
6
|
+
import { cmsApi } from '../../lib/api.js'
|
|
7
|
+
|
|
8
|
+
export type UpdateChangeType =
|
|
9
|
+
| 'feature'
|
|
10
|
+
| 'fix'
|
|
11
|
+
| 'improvement'
|
|
12
|
+
| 'breaking'
|
|
13
|
+
| 'security'
|
|
14
|
+
| 'deprecation'
|
|
15
|
+
|
|
16
|
+
export interface UpdateChangelogItem {
|
|
17
|
+
version: string
|
|
18
|
+
date: string
|
|
19
|
+
summary: string
|
|
20
|
+
type?: UpdateChangeType
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface UpdatePluginCompatibility {
|
|
24
|
+
name: string
|
|
25
|
+
compatible: boolean
|
|
26
|
+
minVersion?: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface UpdateInfo {
|
|
30
|
+
current: string
|
|
31
|
+
latest: string
|
|
32
|
+
updateAvailable: boolean
|
|
33
|
+
severity?: 'patch' | 'minor' | 'major' | 'security'
|
|
34
|
+
releaseDate?: string
|
|
35
|
+
changelog?: UpdateChangelogItem[]
|
|
36
|
+
updateCommand?: string
|
|
37
|
+
breakingChanges?: string[]
|
|
38
|
+
hasMigrations?: boolean
|
|
39
|
+
pluginCompatibility?: UpdatePluginCompatibility[]
|
|
40
|
+
source?: 'update-server' | 'npm'
|
|
41
|
+
/** Whether the current session may manage updates (admin/owner). */
|
|
42
|
+
canManage?: boolean
|
|
43
|
+
hasGithubToken?: boolean
|
|
44
|
+
githubRepo?: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface GithubForm {
|
|
48
|
+
githubRepo: string
|
|
49
|
+
githubToken: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const REPO_PATTERN = /^[a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+$/
|
|
53
|
+
|
|
54
|
+
/** Admin/Owner may manage updates (matches the server's `requireAdminScope`). */
|
|
55
|
+
export function canManageUpdates(role: string | null | undefined): boolean {
|
|
56
|
+
const r = (role ?? '').toUpperCase()
|
|
57
|
+
return r === 'ADMIN' || r === 'OWNER'
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* State + actions for the Settings → Updates tab. Owns the update-status check,
|
|
62
|
+
* the GitHub-integration config form (with dirty tracking for the shared save
|
|
63
|
+
* bar), and the "Create Update PR" action. All writes are gated on `canManage`,
|
|
64
|
+
* which the server confirms in the check response.
|
|
65
|
+
*/
|
|
66
|
+
export function useUpdates({ role }: { role?: string }) {
|
|
67
|
+
const [info, setInfo] = useState<UpdateInfo | null>(null)
|
|
68
|
+
const [checking, setChecking] = useState(false)
|
|
69
|
+
const [hasChecked, setHasChecked] = useState(false)
|
|
70
|
+
const [error, setError] = useState('')
|
|
71
|
+
const [applying, setApplying] = useState(false)
|
|
72
|
+
const [prResult, setPrResult] = useState<{ prUrl: string; prNumber: number } | null>(null)
|
|
73
|
+
|
|
74
|
+
const [form, setForm] = useState<GithubForm>({ githubRepo: '', githubToken: '' })
|
|
75
|
+
const [baseline, setBaseline] = useState<GithubForm>({ githubRepo: '', githubToken: '' })
|
|
76
|
+
const [saveState, setSaveState] = useState<'idle' | 'saving' | 'success' | 'error'>('idle')
|
|
77
|
+
|
|
78
|
+
// Optimistic until the check response confirms the server-side permission.
|
|
79
|
+
const canManage = info?.canManage ?? canManageUpdates(role)
|
|
80
|
+
|
|
81
|
+
const check = useCallback(async () => {
|
|
82
|
+
setChecking(true)
|
|
83
|
+
setError('')
|
|
84
|
+
setPrResult(null)
|
|
85
|
+
const res = await cmsApi<UpdateInfo>('/updates/check')
|
|
86
|
+
if (res.error) {
|
|
87
|
+
setError(res.error)
|
|
88
|
+
} else if (res.data) {
|
|
89
|
+
setInfo(res.data)
|
|
90
|
+
const repo = res.data.githubRepo ?? ''
|
|
91
|
+
setForm({ githubRepo: repo, githubToken: '' })
|
|
92
|
+
setBaseline({ githubRepo: repo, githubToken: '' })
|
|
93
|
+
}
|
|
94
|
+
setChecking(false)
|
|
95
|
+
setHasChecked(true)
|
|
96
|
+
}, [])
|
|
97
|
+
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
check()
|
|
100
|
+
}, [check])
|
|
101
|
+
|
|
102
|
+
const repoError =
|
|
103
|
+
form.githubRepo.trim() && !REPO_PATTERN.test(form.githubRepo.trim())
|
|
104
|
+
? 'Use the owner/repo format (e.g. actuate-media/my-site).'
|
|
105
|
+
: ''
|
|
106
|
+
const hasErrors = Boolean(repoError)
|
|
107
|
+
const dirty = form.githubRepo !== baseline.githubRepo || form.githubToken.trim() !== ''
|
|
108
|
+
|
|
109
|
+
const setField = useCallback((key: keyof GithubForm, value: string) => {
|
|
110
|
+
setForm((f) => ({ ...f, [key]: value }))
|
|
111
|
+
setSaveState('idle')
|
|
112
|
+
}, [])
|
|
113
|
+
|
|
114
|
+
const save = useCallback(async () => {
|
|
115
|
+
if (hasErrors || !dirty) return
|
|
116
|
+
setSaveState('saving')
|
|
117
|
+
const body: Record<string, string> = {}
|
|
118
|
+
if (form.githubToken.trim()) body.githubToken = form.githubToken.trim()
|
|
119
|
+
if (form.githubRepo.trim()) body.githubRepo = form.githubRepo.trim()
|
|
120
|
+
|
|
121
|
+
const res = await cmsApi('/updates/config', { method: 'PUT', body: JSON.stringify(body) })
|
|
122
|
+
if (res.error) {
|
|
123
|
+
setSaveState('error')
|
|
124
|
+
toast.error(res.error)
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
setSaveState('success')
|
|
128
|
+
toast.success('GitHub configuration saved and encrypted.')
|
|
129
|
+
const savedRepo = form.githubRepo.trim()
|
|
130
|
+
setBaseline({ githubRepo: savedRepo, githubToken: '' })
|
|
131
|
+
setForm({ githubRepo: savedRepo, githubToken: '' })
|
|
132
|
+
setInfo((prev) =>
|
|
133
|
+
prev
|
|
134
|
+
? {
|
|
135
|
+
...prev,
|
|
136
|
+
githubRepo: savedRepo,
|
|
137
|
+
hasGithubToken: prev.hasGithubToken || Boolean(body.githubToken),
|
|
138
|
+
}
|
|
139
|
+
: prev,
|
|
140
|
+
)
|
|
141
|
+
}, [dirty, form.githubRepo, form.githubToken, hasErrors])
|
|
142
|
+
|
|
143
|
+
const reset = useCallback(() => {
|
|
144
|
+
setForm({ ...baseline })
|
|
145
|
+
setSaveState('idle')
|
|
146
|
+
}, [baseline])
|
|
147
|
+
|
|
148
|
+
const apply = useCallback(async () => {
|
|
149
|
+
if (!info?.latest || !canManage) return
|
|
150
|
+
setApplying(true)
|
|
151
|
+
const res = await cmsApi<{ prUrl: string; prNumber: number }>('/updates/apply', {
|
|
152
|
+
method: 'POST',
|
|
153
|
+
body: JSON.stringify({ targetVersion: info.latest }),
|
|
154
|
+
})
|
|
155
|
+
if (res.error) {
|
|
156
|
+
toast.error(res.error)
|
|
157
|
+
} else if (res.data) {
|
|
158
|
+
setPrResult(res.data)
|
|
159
|
+
toast.success('Update PR created.')
|
|
160
|
+
}
|
|
161
|
+
setApplying(false)
|
|
162
|
+
}, [info, canManage])
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
info,
|
|
166
|
+
checking,
|
|
167
|
+
hasChecked,
|
|
168
|
+
error,
|
|
169
|
+
applying,
|
|
170
|
+
prResult,
|
|
171
|
+
canManage,
|
|
172
|
+
form,
|
|
173
|
+
setField,
|
|
174
|
+
dirty,
|
|
175
|
+
repoError,
|
|
176
|
+
hasErrors,
|
|
177
|
+
saveState,
|
|
178
|
+
save,
|
|
179
|
+
reset,
|
|
180
|
+
check,
|
|
181
|
+
apply,
|
|
182
|
+
}
|
|
183
|
+
}
|