@prenta/admin 1.13.0 → 1.14.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__/lib/seo-service-proposals.test.d.ts +2 -0
- package/dist/__tests__/lib/seo-service-proposals.test.d.ts.map +1 -0
- package/dist/__tests__/lib/seo-service-proposals.test.js +71 -0
- package/dist/__tests__/lib/seo-service-proposals.test.js.map +1 -0
- package/dist/__tests__/router/seo-tab-for-path.test.js +1 -0
- package/dist/__tests__/router/seo-tab-for-path.test.js.map +1 -1
- package/dist/__tests__/views/seo-proposals-tab.render.test.d.ts +2 -0
- package/dist/__tests__/views/seo-proposals-tab.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/seo-proposals-tab.render.test.js +292 -0
- package/dist/__tests__/views/seo-proposals-tab.render.test.js.map +1 -0
- package/dist/components/seo/ChangePreview.d.ts +6 -0
- package/dist/components/seo/ChangePreview.d.ts.map +1 -0
- package/dist/components/seo/ChangePreview.js +18 -0
- package/dist/components/seo/ChangePreview.js.map +1 -0
- package/dist/components/seo/SeoIssueFixPanel.d.ts.map +1 -1
- package/dist/components/seo/SeoIssueFixPanel.js +1 -14
- package/dist/components/seo/SeoIssueFixPanel.js.map +1 -1
- package/dist/lib/seo-service.d.ts +87 -0
- package/dist/lib/seo-service.d.ts.map +1 -1
- package/dist/lib/seo-service.js +36 -0
- package/dist/lib/seo-service.js.map +1 -1
- package/dist/prenta-admin.css +1 -1
- package/dist/views/SEO.d.ts +5 -1
- package/dist/views/SEO.d.ts.map +1 -1
- package/dist/views/SEO.js +28 -9
- package/dist/views/SEO.js.map +1 -1
- package/dist/views/seo/ProposalsTab.d.ts +7 -0
- package/dist/views/seo/ProposalsTab.d.ts.map +1 -0
- package/dist/views/seo/ProposalsTab.js +298 -0
- package/dist/views/seo/ProposalsTab.js.map +1 -0
- package/package.json +3 -3
- package/src/__tests__/lib/seo-service-proposals.test.ts +79 -0
- package/src/__tests__/router/seo-tab-for-path.test.ts +1 -0
- package/src/__tests__/views/seo-proposals-tab.render.test.tsx +370 -0
- package/src/components/seo/ChangePreview.tsx +47 -0
- package/src/components/seo/SeoIssueFixPanel.tsx +1 -44
- package/src/lib/seo-service.ts +125 -0
- package/src/views/SEO.tsx +40 -7
- package/src/views/seo/ProposalsTab.tsx +681 -0
package/src/views/SEO.tsx
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* SEO Operations Center —
|
|
5
|
-
* Technical / Redirects / Audit). Tab + deep-link state is
|
|
6
|
-
* `?tab=` query string so audits, issues, redirects,
|
|
7
|
-
* all linkable. All data flows through
|
|
4
|
+
* SEO Operations Center — seven-tab workflow surface (Overview / Content /
|
|
5
|
+
* Links / Technical / Redirects / Audit / Proposals). Tab + deep-link state is
|
|
6
|
+
* driven by the `?tab=` query string so audits, issues, redirects, proposals,
|
|
7
|
+
* and content editors are all linkable. All data flows through
|
|
8
|
+
* `lib/seo-service.ts`.
|
|
8
9
|
*/
|
|
9
10
|
import * as Tabs from '@radix-ui/react-tabs'
|
|
10
11
|
import {
|
|
@@ -16,6 +17,7 @@ import {
|
|
|
16
17
|
RefreshCw,
|
|
17
18
|
Bot,
|
|
18
19
|
Link2,
|
|
20
|
+
Inbox,
|
|
19
21
|
} from 'lucide-react'
|
|
20
22
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
21
23
|
import { toast } from 'sonner'
|
|
@@ -32,6 +34,7 @@ import { TechnicalTab } from './seo/TechnicalTab.js'
|
|
|
32
34
|
import { RedirectsTab } from './seo/RedirectsTab.js'
|
|
33
35
|
import { AuditTab } from './seo/AuditTab.js'
|
|
34
36
|
import { LinksTab } from './seo/LinksTab.js'
|
|
37
|
+
import { ProposalsTab } from './seo/ProposalsTab.js'
|
|
35
38
|
|
|
36
39
|
export interface SEOProps {
|
|
37
40
|
onNavigate?: (path: string) => void
|
|
@@ -45,6 +48,7 @@ const TABS = [
|
|
|
45
48
|
{ id: 'technical', label: 'Technical', icon: Settings2 },
|
|
46
49
|
{ id: 'redirects', label: 'Redirects', icon: ArrowRightLeft },
|
|
47
50
|
{ id: 'audit', label: 'Audit', icon: ClipboardCheck },
|
|
51
|
+
{ id: 'proposals', label: 'Proposals', icon: Inbox },
|
|
48
52
|
] as const
|
|
49
53
|
|
|
50
54
|
type TabId = (typeof TABS)[number]['id']
|
|
@@ -69,6 +73,7 @@ export const SEO_DEEP_PATHS = [
|
|
|
69
73
|
'/seo/technical',
|
|
70
74
|
'/seo/links',
|
|
71
75
|
'/seo/audit',
|
|
76
|
+
'/seo/proposals',
|
|
72
77
|
'/seo',
|
|
73
78
|
] as const
|
|
74
79
|
|
|
@@ -89,6 +94,8 @@ export function seoTabForPath(pathname: string): TabId | null {
|
|
|
89
94
|
return 'technical'
|
|
90
95
|
case '/seo/audit':
|
|
91
96
|
return 'audit'
|
|
97
|
+
case '/seo/proposals':
|
|
98
|
+
return 'proposals'
|
|
92
99
|
case '/seo':
|
|
93
100
|
return 'overview'
|
|
94
101
|
default:
|
|
@@ -177,7 +184,11 @@ export function SEO({ onNavigate, initialTab = 'overview' }: SEOProps) {
|
|
|
177
184
|
|
|
178
185
|
const badgeCounts = useMemo(() => {
|
|
179
186
|
const summary = overviewResource.data?.issuesSummary
|
|
180
|
-
|
|
187
|
+
const pending = overviewResource.data?.pendingProposals
|
|
188
|
+
return {
|
|
189
|
+
audit: summary ? summary.critical + summary.warning : 0,
|
|
190
|
+
proposals: pending ? pending.issueFixes + pending.redirects : 0,
|
|
191
|
+
}
|
|
181
192
|
}, [overviewResource.data])
|
|
182
193
|
|
|
183
194
|
return (
|
|
@@ -215,7 +226,18 @@ export function SEO({ onNavigate, initialTab = 'overview' }: SEOProps) {
|
|
|
215
226
|
>
|
|
216
227
|
{TABS.map((t) => {
|
|
217
228
|
const Icon = t.icon
|
|
218
|
-
const badge =
|
|
229
|
+
const badge =
|
|
230
|
+
t.id === 'audit'
|
|
231
|
+
? badgeCounts.audit
|
|
232
|
+
: t.id === 'proposals'
|
|
233
|
+
? badgeCounts.proposals
|
|
234
|
+
: 0
|
|
235
|
+
// The proposals count is informational (items awaiting review),
|
|
236
|
+
// not a warning like the audit issue count.
|
|
237
|
+
const badgeClass =
|
|
238
|
+
t.id === 'proposals'
|
|
239
|
+
? 'bg-muted text-muted-foreground'
|
|
240
|
+
: 'bg-destructive/10 text-destructive'
|
|
219
241
|
return (
|
|
220
242
|
<Tabs.Trigger
|
|
221
243
|
key={t.id}
|
|
@@ -225,7 +247,9 @@ export function SEO({ onNavigate, initialTab = 'overview' }: SEOProps) {
|
|
|
225
247
|
<Icon className="h-4 w-4" aria-hidden />
|
|
226
248
|
{t.label}
|
|
227
249
|
{badge > 0 && (
|
|
228
|
-
<span
|
|
250
|
+
<span
|
|
251
|
+
className={`${badgeClass} ml-1 rounded-full px-1.5 py-0.5 text-xs font-medium tabular-nums`}
|
|
252
|
+
>
|
|
229
253
|
{badge}
|
|
230
254
|
</span>
|
|
231
255
|
)}
|
|
@@ -272,6 +296,15 @@ export function SEO({ onNavigate, initialTab = 'overview' }: SEOProps) {
|
|
|
272
296
|
/>
|
|
273
297
|
)}
|
|
274
298
|
</Tabs.Content>
|
|
299
|
+
<Tabs.Content value="proposals" tabIndex={-1}>
|
|
300
|
+
{activeTab === 'proposals' && (
|
|
301
|
+
<ProposalsTab
|
|
302
|
+
planInfo={planInfo}
|
|
303
|
+
onChanged={() => setRefetchKey((k) => k + 1)}
|
|
304
|
+
onNavigate={onNavigate}
|
|
305
|
+
/>
|
|
306
|
+
)}
|
|
307
|
+
</Tabs.Content>
|
|
275
308
|
</ErrorBoundary>
|
|
276
309
|
</div>
|
|
277
310
|
</Tabs.Root>
|