@actuate-media/cms-admin 0.36.1 → 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.
Files changed (38) hide show
  1. package/dist/__tests__/views/appearance-settings.render.test.js +4 -4
  2. package/dist/__tests__/views/updates-tab.render.test.d.ts +2 -0
  3. package/dist/__tests__/views/updates-tab.render.test.d.ts.map +1 -0
  4. package/dist/__tests__/views/updates-tab.render.test.js +132 -0
  5. package/dist/__tests__/views/updates-tab.render.test.js.map +1 -0
  6. package/dist/actuate-admin.css +1 -1
  7. package/dist/views/Settings.d.ts.map +1 -1
  8. package/dist/views/Settings.js +4 -112
  9. package/dist/views/Settings.js.map +1 -1
  10. package/dist/views/settings/AdminThemeCard.js +1 -1
  11. package/dist/views/settings/BrandPreviewCard.js +1 -1
  12. package/dist/views/settings/BrandingCard.d.ts.map +1 -1
  13. package/dist/views/settings/BrandingCard.js +34 -19
  14. package/dist/views/settings/BrandingCard.js.map +1 -1
  15. package/dist/views/settings/TypographyMotionCard.js +1 -1
  16. package/dist/views/settings/UpdatesTab.d.ts +6 -0
  17. package/dist/views/settings/UpdatesTab.d.ts.map +1 -0
  18. package/dist/views/settings/UpdatesTab.js +80 -0
  19. package/dist/views/settings/UpdatesTab.js.map +1 -0
  20. package/dist/views/settings/components.d.ts +20 -0
  21. package/dist/views/settings/components.d.ts.map +1 -1
  22. package/dist/views/settings/components.js +11 -0
  23. package/dist/views/settings/components.js.map +1 -1
  24. package/dist/views/settings/useUpdates.d.ts +67 -0
  25. package/dist/views/settings/useUpdates.d.ts.map +1 -0
  26. package/dist/views/settings/useUpdates.js +127 -0
  27. package/dist/views/settings/useUpdates.js.map +1 -0
  28. package/package.json +2 -2
  29. package/src/__tests__/views/appearance-settings.render.test.tsx +4 -4
  30. package/src/__tests__/views/updates-tab.render.test.tsx +161 -0
  31. package/src/views/Settings.tsx +2 -438
  32. package/src/views/settings/AdminThemeCard.tsx +1 -1
  33. package/src/views/settings/BrandPreviewCard.tsx +1 -1
  34. package/src/views/settings/BrandingCard.tsx +179 -180
  35. package/src/views/settings/TypographyMotionCard.tsx +1 -1
  36. package/src/views/settings/UpdatesTab.tsx +545 -0
  37. package/src/views/settings/components.tsx +51 -0
  38. package/src/views/settings/useUpdates.ts +183 -0
@@ -46,9 +46,9 @@ describe('AppearanceTab', () => {
46
46
  it('renders the branding/theme/typography cards with a clean (disabled) save bar', async () => {
47
47
  render(_jsx(AppearanceTab, { role: "ADMIN" }));
48
48
  await screen.findByText('Branding');
49
- expect(screen.getByText('Admin Theme')).toBeTruthy();
50
- expect(screen.getByText('Typography & Motion')).toBeTruthy();
51
- expect(screen.getByText('Brand Preview')).toBeTruthy();
49
+ expect(screen.getByText('Admin theme')).toBeTruthy();
50
+ expect(screen.getByText('Typography & motion')).toBeTruthy();
51
+ expect(screen.getByText('Brand preview')).toBeTruthy();
52
52
  expect(screen.getByRole('button', { name: 'Save Changes' }).disabled).toBe(true);
53
53
  });
54
54
  it('enables Save when the accent changes and persists appearance to the settings global', async () => {
@@ -79,7 +79,7 @@ describe('AppearanceTab', () => {
79
79
  it('uploads a logo through the Media Center and enables Save', async () => {
80
80
  render(_jsx(AppearanceTab, { role: "ADMIN" }));
81
81
  await screen.findByText('Branding');
82
- const input = screen.getByLabelText('Primary Logo');
82
+ const input = screen.getByLabelText('Primary logo');
83
83
  const file = new File(['<svg viewBox="0 0 400 100" xmlns="http://www.w3.org/2000/svg"></svg>'], 'logo.svg', { type: 'image/svg+xml' });
84
84
  fireEvent.change(input, { target: { files: [file] } });
85
85
  await waitFor(() => expect(cmsApi).toHaveBeenCalledWith('/media/upload', expect.objectContaining({ method: 'POST' })));
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=updates-tab.render.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"updates-tab.render.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/views/updates-tab.render.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,132 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // @vitest-environment happy-dom
3
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
4
+ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
5
+ // The Updates tab reads `/updates/check`, saves GitHub config via
6
+ // PUT `/updates/config`, and opens an upgrade PR via POST `/updates/apply`.
7
+ let checkData;
8
+ const cmsApi = vi.fn(async (endpoint, options) => {
9
+ const method = options?.method ?? 'GET';
10
+ if (endpoint === '/updates/check' && method === 'GET')
11
+ return { data: checkData, status: 200 };
12
+ if (endpoint === '/updates/config' && method === 'PUT')
13
+ return { data: { success: true }, status: 200 };
14
+ if (endpoint === '/updates/apply' && method === 'POST')
15
+ return { data: { prUrl: 'https://github.com/x/y/pull/1', prNumber: 1 }, status: 200 };
16
+ return { data: {}, status: 200 };
17
+ });
18
+ vi.mock('../../lib/api.js', () => ({ cmsApi: (e, o) => cmsApi(e, o) }));
19
+ vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn(), loading: vi.fn() } }));
20
+ const { UpdatesTab } = await import('../../views/settings/UpdatesTab.js');
21
+ function upToDate(overrides = {}) {
22
+ return {
23
+ current: '0.46.0',
24
+ latest: '0.46.0',
25
+ updateAvailable: false,
26
+ canManage: true,
27
+ hasGithubToken: false,
28
+ githubRepo: '',
29
+ source: 'update-server',
30
+ ...overrides,
31
+ };
32
+ }
33
+ function updateAvailable(overrides = {}) {
34
+ return {
35
+ current: '0.45.0',
36
+ latest: '0.46.0',
37
+ updateAvailable: true,
38
+ severity: 'minor',
39
+ releaseDate: '2026-01-02',
40
+ changelog: [
41
+ {
42
+ version: '0.46.0',
43
+ date: '2026-01-02',
44
+ summary: 'Add Brand Voice training',
45
+ type: 'feature',
46
+ },
47
+ ],
48
+ hasMigrations: true,
49
+ breakingChanges: ['Removed the legacy provider API'],
50
+ pluginCompatibility: [
51
+ { name: '@actuate-media/plugin-seo', compatible: true, minVersion: '0.46.0' },
52
+ ],
53
+ updateCommand: 'npm update @actuate-media/cms-core',
54
+ canManage: true,
55
+ hasGithubToken: false,
56
+ githubRepo: '',
57
+ source: 'update-server',
58
+ ...overrides,
59
+ };
60
+ }
61
+ beforeEach(() => {
62
+ cmsApi.mockClear();
63
+ checkData = upToDate();
64
+ });
65
+ describe('UpdatesTab', () => {
66
+ it('shows the current version and an up-to-date banner', async () => {
67
+ render(_jsx(UpdatesTab, { role: "ADMIN" }));
68
+ await screen.findByText("You're up to date!");
69
+ expect(screen.getAllByText('0.46.0').length).toBeGreaterThan(0);
70
+ // Admins see the GitHub integration card.
71
+ expect(screen.getByText('GitHub Integration')).toBeTruthy();
72
+ });
73
+ it('surfaces severity, real changelog, migrations and breaking changes', async () => {
74
+ checkData = updateAvailable();
75
+ render(_jsx(UpdatesTab, { role: "ADMIN" }));
76
+ await screen.findByText('Update available');
77
+ expect(screen.getByText('Minor')).toBeTruthy();
78
+ expect(screen.getByText('Add Brand Voice training')).toBeTruthy();
79
+ expect(screen.getByText('Database migrations required')).toBeTruthy();
80
+ expect(screen.getByText('Breaking changes in this release')).toBeTruthy();
81
+ expect(screen.getByText('Removed the legacy provider API')).toBeTruthy();
82
+ // Plugin compatibility surfaced.
83
+ expect(screen.getByText('plugin-seo')).toBeTruthy();
84
+ expect(screen.getByRole('button', { name: 'Create Update PR' })).toBeTruthy();
85
+ });
86
+ it('flags a security release prominently', async () => {
87
+ checkData = updateAvailable({ severity: 'security' });
88
+ render(_jsx(UpdatesTab, { role: "ADMIN" }));
89
+ await screen.findByText('Security update available');
90
+ expect(screen.getByText('Security')).toBeTruthy();
91
+ });
92
+ it('is permission-aware: non-admins cannot manage updates', async () => {
93
+ checkData = updateAvailable({ canManage: false });
94
+ render(_jsx(UpdatesTab, { role: "EDITOR" }));
95
+ await screen.findByText('Update available');
96
+ expect(screen.queryByText('GitHub Integration')).toBeNull();
97
+ expect(screen.queryByRole('button', { name: 'Create Update PR' })).toBeNull();
98
+ expect(screen.getByText(/Ask an administrator/i)).toBeTruthy();
99
+ });
100
+ it('enables Save when the repo changes and persists via PUT /updates/config', async () => {
101
+ render(_jsx(UpdatesTab, { role: "ADMIN" }));
102
+ await screen.findByText('GitHub Integration');
103
+ const save = screen.getByRole('button', { name: 'Save Changes' });
104
+ expect(save.disabled).toBe(true);
105
+ const repo = screen.getByLabelText('Repository');
106
+ fireEvent.change(repo, { target: { value: 'actuate-media/site' } });
107
+ expect(save.disabled).toBe(false);
108
+ fireEvent.click(save);
109
+ await waitFor(() => expect(cmsApi).toHaveBeenCalledWith('/updates/config', expect.objectContaining({
110
+ method: 'PUT',
111
+ body: expect.stringContaining('actuate-media/site'),
112
+ })));
113
+ });
114
+ it('blocks Save on an invalid repository format', async () => {
115
+ render(_jsx(UpdatesTab, { role: "ADMIN" }));
116
+ await screen.findByText('GitHub Integration');
117
+ const repo = screen.getByLabelText('Repository');
118
+ fireEvent.change(repo, { target: { value: 'not a repo' } });
119
+ expect(screen.getByText(/owner\/repo format/i)).toBeTruthy();
120
+ const save = screen.getByRole('button', { name: 'Save Changes' });
121
+ expect(save.disabled).toBe(true);
122
+ });
123
+ it('creates an update PR via POST /updates/apply', async () => {
124
+ checkData = updateAvailable();
125
+ render(_jsx(UpdatesTab, { role: "ADMIN" }));
126
+ await screen.findByText('Update available');
127
+ fireEvent.click(screen.getByRole('button', { name: 'Create Update PR' }));
128
+ await waitFor(() => expect(cmsApi).toHaveBeenCalledWith('/updates/apply', expect.objectContaining({ method: 'POST', body: expect.stringContaining('0.46.0') })));
129
+ await screen.findByText('Pull Request Created');
130
+ });
131
+ });
132
+ //# sourceMappingURL=updates-tab.render.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"updates-tab.render.test.js","sourceRoot":"","sources":["../../../src/__tests__/views/updates-tab.render.test.tsx"],"names":[],"mappings":"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"}