@prenta/admin 1.6.2 → 1.8.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 +12 -0
- package/dist/__tests__/components/seo-editor-pane.render.test.js +2 -1
- package/dist/__tests__/components/seo-editor-pane.render.test.js.map +1 -1
- package/dist/__tests__/components/seo-issue-fix-panel.render.test.d.ts +2 -0
- package/dist/__tests__/components/seo-issue-fix-panel.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/seo-issue-fix-panel.render.test.js +60 -0
- package/dist/__tests__/components/seo-issue-fix-panel.render.test.js.map +1 -0
- package/dist/__tests__/components/seo-linking-section.render.test.d.ts +2 -0
- package/dist/__tests__/components/seo-linking-section.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/seo-linking-section.render.test.js +94 -0
- package/dist/__tests__/components/seo-linking-section.render.test.js.map +1 -0
- package/dist/__tests__/lib/collection-routes.test.js +8 -1
- package/dist/__tests__/lib/collection-routes.test.js.map +1 -1
- package/dist/__tests__/lib/seo-service-errors.test.js +35 -3
- package/dist/__tests__/lib/seo-service-errors.test.js.map +1 -1
- package/dist/__tests__/views/seo-content-pane.render.test.js +8 -1
- package/dist/__tests__/views/seo-content-pane.render.test.js.map +1 -1
- package/dist/components/seo/SeoEditorPane.d.ts +2 -1
- package/dist/components/seo/SeoEditorPane.d.ts.map +1 -1
- package/dist/components/seo/SeoEditorPane.js +2 -2
- package/dist/components/seo/SeoEditorPane.js.map +1 -1
- package/dist/components/seo/SeoIssueFixPanel.d.ts.map +1 -1
- package/dist/components/seo/SeoIssueFixPanel.js +32 -8
- package/dist/components/seo/SeoIssueFixPanel.js.map +1 -1
- package/dist/components/seo/SeoLinkingSection.d.ts +5 -1
- package/dist/components/seo/SeoLinkingSection.d.ts.map +1 -1
- package/dist/components/seo/SeoLinkingSection.js +50 -3
- package/dist/components/seo/SeoLinkingSection.js.map +1 -1
- package/dist/lib/collection-routes.d.ts +2 -0
- package/dist/lib/collection-routes.d.ts.map +1 -1
- package/dist/lib/collection-routes.js +5 -0
- package/dist/lib/collection-routes.js.map +1 -1
- package/dist/lib/seo-service.d.ts +25 -2
- package/dist/lib/seo-service.d.ts.map +1 -1
- package/dist/lib/seo-service.js +23 -3
- package/dist/lib/seo-service.js.map +1 -1
- package/dist/views/SEO.d.ts.map +1 -1
- package/dist/views/SEO.js +1 -1
- package/dist/views/SEO.js.map +1 -1
- package/dist/views/page-editor/PageSectionEditor.d.ts.map +1 -1
- package/dist/views/page-editor/PageSectionEditor.js +1 -1
- package/dist/views/page-editor/PageSectionEditor.js.map +1 -1
- package/dist/views/post-editor/PostSectionEditor.d.ts.map +1 -1
- package/dist/views/post-editor/PostSectionEditor.js +1 -1
- package/dist/views/post-editor/PostSectionEditor.js.map +1 -1
- package/dist/views/seo/ContentTab.d.ts +2 -1
- package/dist/views/seo/ContentTab.d.ts.map +1 -1
- package/dist/views/seo/ContentTab.js +18 -12
- package/dist/views/seo/ContentTab.js.map +1 -1
- package/dist/views/seo/LinksTab.d.ts +3 -1
- package/dist/views/seo/LinksTab.d.ts.map +1 -1
- package/dist/views/seo/LinksTab.js +36 -6
- package/dist/views/seo/LinksTab.js.map +1 -1
- package/package.json +5 -5
- package/src/__tests__/components/seo-editor-pane.render.test.tsx +4 -1
- package/src/__tests__/components/seo-issue-fix-panel.render.test.tsx +65 -0
- package/src/__tests__/components/seo-linking-section.render.test.tsx +146 -0
- package/src/__tests__/lib/collection-routes.test.ts +13 -1
- package/src/__tests__/lib/seo-service-errors.test.ts +40 -2
- package/src/__tests__/views/seo-content-pane.render.test.tsx +8 -1
- package/src/components/seo/SeoEditorPane.tsx +3 -0
- package/src/components/seo/SeoIssueFixPanel.tsx +34 -10
- package/src/components/seo/SeoLinkingSection.tsx +116 -18
- package/src/lib/collection-routes.ts +13 -0
- package/src/lib/seo-service.ts +44 -4
- package/src/views/SEO.tsx +2 -1
- package/src/views/page-editor/PageSectionEditor.tsx +1 -0
- package/src/views/post-editor/PostSectionEditor.tsx +1 -0
- package/src/views/seo/ContentTab.tsx +40 -2
- package/src/views/seo/LinksTab.tsx +108 -19
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { type PlanInfo } from '../../lib/plan.js';
|
|
2
|
+
export declare function LinksTab({ onNavigate, planInfo, }: {
|
|
2
3
|
onNavigate?: (path: string) => void;
|
|
4
|
+
planInfo?: PlanInfo | null;
|
|
3
5
|
}): import("react/jsx-runtime").JSX.Element;
|
|
4
6
|
//# sourceMappingURL=LinksTab.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LinksTab.d.ts","sourceRoot":"","sources":["../../../src/views/seo/LinksTab.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"LinksTab.d.ts","sourceRoot":"","sources":["../../../src/views/seo/LinksTab.tsx"],"names":[],"mappings":"AA4BA,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAqKjE,wBAAgB,QAAQ,CAAC,EACvB,UAAU,EACV,QAAQ,GACT,EAAE;IACD,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAA;CAC3B,2CAqWA"}
|
|
@@ -7,8 +7,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
7
7
|
import { useCallback, useMemo, useState } from 'react';
|
|
8
8
|
import { Link2, Sparkles, AlertTriangle, CheckCircle2, Loader2, RefreshCw, ExternalLink, Bot, } from 'lucide-react';
|
|
9
9
|
import { toast } from 'sonner';
|
|
10
|
-
import { fetchLinkGraphSnapshot, fetchLinkHealthIssues, fetchLinkSuggestions, runLinkAiIndex, } from '../../lib/seo-service.js';
|
|
11
|
-
import { editPathForDoc } from '../../lib/collection-routes.js';
|
|
10
|
+
import { applySeoInternalLink, fetchLinkGraphSnapshot, fetchLinkHealthIssues, fetchLinkSuggestions, runLinkAiIndex, } from '../../lib/seo-service.js';
|
|
11
|
+
import { editPathForDoc, sourceEditPath } from '../../lib/collection-routes.js';
|
|
12
|
+
import { hasPlanFeature } from '../../lib/plan.js';
|
|
13
|
+
import { PlanUpgradeCallout } from '../../components/PlanUpgradeCallout.js';
|
|
12
14
|
import { SectionCard, SeoLoading, SeoErrorState, SeoEmptyState, btnPrimary, btnSecondary, gradeMeta, } from '../../components/seo/primitives.js';
|
|
13
15
|
import { useSeoResource } from './useSeoResource.js';
|
|
14
16
|
function scoreFromCoverage(coverage, orphanCount, broken) {
|
|
@@ -21,16 +23,39 @@ function ActionCard({ icon: Icon, title, description, impact, actionLabel, onAct
|
|
|
21
23
|
const impactTone = impact === 'High' ? 'text-destructive' : 'text-warning';
|
|
22
24
|
return (_jsxs("div", { className: "border-border bg-card flex flex-col gap-4 rounded-xl border p-5 shadow-sm sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { className: "flex min-w-0 gap-4", children: [_jsx("div", { className: "bg-primary/10 text-primary flex h-11 w-11 shrink-0 items-center justify-center rounded-lg", children: _jsx(Icon, { className: "h-5 w-5", "aria-hidden": true }) }), _jsxs("div", { className: "min-w-0", children: [_jsx("h3", { className: "text-foreground text-sm font-medium", children: title }), _jsx("p", { className: "text-muted-foreground mt-1 text-sm", children: description }), _jsx("div", { className: "mt-2 flex flex-wrap items-center gap-2", children: _jsxs("span", { className: `text-xs font-medium ${impactTone}`, children: ["Impact: ", impact] }) })] })] }), _jsxs("button", { type: "button", className: btnPrimary, onClick: onAction, disabled: disabled || busy, children: [busy ? _jsx(Loader2, { className: "h-4 w-4 animate-spin", "aria-hidden": true }) : null, actionLabel] })] }));
|
|
23
25
|
}
|
|
24
|
-
function SuggestionList({ suggestions, loading, }) {
|
|
26
|
+
function SuggestionList({ suggestions, loading, targetEntityId, onNavigate, onApplied, planInfo, }) {
|
|
27
|
+
const [applyingKey, setApplyingKey] = useState(null);
|
|
28
|
+
const inlineApplyEnabled = hasPlanFeature(planInfo, 'seo.inlineApply');
|
|
29
|
+
async function handleApprove(suggestion) {
|
|
30
|
+
const key = `${suggestion.sourceEntityId}-${suggestion.fingerprint}`;
|
|
31
|
+
setApplyingKey(key);
|
|
32
|
+
const res = await applySeoInternalLink({
|
|
33
|
+
sourceEntityId: suggestion.sourceEntityId,
|
|
34
|
+
targetEntityId,
|
|
35
|
+
anchorText: suggestion.anchorText,
|
|
36
|
+
fingerprint: suggestion.fingerprint,
|
|
37
|
+
});
|
|
38
|
+
setApplyingKey(null);
|
|
39
|
+
if (res.error) {
|
|
40
|
+
toast.error(res.error);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
toast.success('Link added.');
|
|
44
|
+
onApplied?.(suggestion);
|
|
45
|
+
}
|
|
25
46
|
if (loading) {
|
|
26
47
|
return (_jsxs("p", { className: "text-muted-foreground flex items-center gap-2 text-sm", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin", "aria-hidden": true }), "Finding AI link opportunities\u2026"] }));
|
|
27
48
|
}
|
|
28
49
|
if (suggestions.length === 0) {
|
|
29
50
|
return (_jsx("p", { className: "text-muted-foreground text-sm", children: "No suggestions yet. Run AI indexing first, or add more published content." }));
|
|
30
51
|
}
|
|
31
|
-
return (
|
|
52
|
+
return (_jsxs("div", { className: "space-y-3", children: [!inlineApplyEnabled && suggestions.some((s) => s.wrappable) ? (_jsx(PlanUpgradeCallout, { feature: "seo.inlineApply", upgradeUrl: planInfo?.upgradeUrl })) : null, _jsx("ul", { className: "divide-border divide-y rounded-lg border", children: suggestions.map((s) => {
|
|
53
|
+
const key = `${s.sourceEntityId}-${s.fingerprint || s.anchorText}`;
|
|
54
|
+
const busy = applyingKey === key;
|
|
55
|
+
return (_jsxs("li", { className: "px-4 py-3", children: [_jsx("p", { className: "text-foreground text-sm font-medium", children: s.sourceTitle }), _jsxs("p", { className: "text-muted-foreground mt-0.5 text-xs", children: ["Link \u201C", s.anchorText, "\u201D \u2192 ", s.sourceUrl || 'target page', ' ', _jsxs("span", { className: "text-muted-foreground/80 tabular-nums", children: ["(", Math.round(s.confidence * 100), "% match)"] })] }), s.excerpt && (_jsx("p", { className: "text-muted-foreground mt-1 line-clamp-2 text-xs", children: s.excerpt })), s.wrappable && inlineApplyEnabled ? (_jsx("div", { className: "mt-2", children: _jsxs("button", { type: "button", className: btnPrimary, disabled: busy, onClick: () => void handleApprove(s), children: [busy ? _jsx(Loader2, { className: "h-4 w-4 animate-spin", "aria-hidden": true }) : null, "Approve link"] }) })) : onNavigate ? (_jsx("div", { className: "mt-2", children: _jsx("button", { type: "button", className: btnSecondary, onClick: () => onNavigate(sourceEditPath(s.sourceCollection, s.sourceEntityId)), children: "Open in editor" }) })) : null] }, key));
|
|
56
|
+
}) })] }));
|
|
32
57
|
}
|
|
33
|
-
export function LinksTab({ onNavigate }) {
|
|
58
|
+
export function LinksTab({ onNavigate, planInfo, }) {
|
|
34
59
|
const { data, loading, error, refetch } = useSeoResource(fetchLinkGraphSnapshot, []);
|
|
35
60
|
const [indexing, setIndexing] = useState(false);
|
|
36
61
|
const [healthLoading, setHealthLoading] = useState(false);
|
|
@@ -104,7 +129,12 @@ export function LinksTab({ onNavigate }) {
|
|
|
104
129
|
document.getElementById('orphan-list')?.scrollIntoView({ behavior: 'smooth' });
|
|
105
130
|
} }), _jsx(ActionCard, { icon: Sparkles, title: "Link coverage", description: "Pages with fewer than two outbound internal links. Strengthen topical clusters by linking to related content.", impact: "High", actionLabel: "Review under-linked", onAction: () => {
|
|
106
131
|
document.getElementById('under-linked-list')?.scrollIntoView({ behavior: 'smooth' });
|
|
107
|
-
} })] }), _jsx(SectionCard, { title: "Orphaned content", description: `${data.orphanCount} published item${data.orphanCount === 1 ? '' : 's'} with no inbound internal links`, children: _jsxs("div", { id: "orphan-list", className: "space-y-4", children: [data.orphaned.length === 0 ? (_jsxs("p", { className: "text-muted-foreground flex items-center gap-2 text-sm", children: [_jsx(CheckCircle2, { className: "text-success h-4 w-4", "aria-hidden": true }), "No orphaned pages detected."] })) : (_jsx("ul", { className: "divide-border divide-y rounded-lg border", children: data.orphaned.map((row) => (_jsxs("li", { className: "flex flex-wrap items-center justify-between gap-3 px-4 py-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-foreground truncate text-sm font-medium", children: row.title }), _jsx("p", { className: "text-muted-foreground truncate text-xs", children: row.url || row.path })] }), _jsxs("div", { className: "flex shrink-0 flex-wrap gap-2", children: [_jsx("button", { type: "button", className: btnSecondary, onClick: () => openEditor(row), children: "Edit" }), _jsxs("button", { type: "button", className: btnPrimary, onClick: () => void loadSuggestions(row), children: [_jsx(Sparkles, { className: "h-3.5 w-3.5", "aria-hidden": true }), "AI suggest"] })] })] }, row.id))) })), selectedOrphan && (_jsxs("div", { className: "border-border bg-muted/30 rounded-lg border p-4", children: [_jsxs("p", { className: "text-foreground mb-2 text-sm font-medium", children: ["AI suggestions for \u201C", selectedOrphan.title, "\u201D"] }), _jsx(SuggestionList, { suggestions: suggestions, loading: suggestLoading
|
|
132
|
+
} })] }), _jsx(SectionCard, { title: "Orphaned content", description: `${data.orphanCount} published item${data.orphanCount === 1 ? '' : 's'} with no inbound internal links`, children: _jsxs("div", { id: "orphan-list", className: "space-y-4", children: [data.orphaned.length === 0 ? (_jsxs("p", { className: "text-muted-foreground flex items-center gap-2 text-sm", children: [_jsx(CheckCircle2, { className: "text-success h-4 w-4", "aria-hidden": true }), "No orphaned pages detected."] })) : (_jsx("ul", { className: "divide-border divide-y rounded-lg border", children: data.orphaned.map((row) => (_jsxs("li", { className: "flex flex-wrap items-center justify-between gap-3 px-4 py-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-foreground truncate text-sm font-medium", children: row.title }), _jsx("p", { className: "text-muted-foreground truncate text-xs", children: row.url || row.path })] }), _jsxs("div", { className: "flex shrink-0 flex-wrap gap-2", children: [_jsx("button", { type: "button", className: btnSecondary, onClick: () => openEditor(row), children: "Edit" }), _jsxs("button", { type: "button", className: btnPrimary, onClick: () => void loadSuggestions(row), children: [_jsx(Sparkles, { className: "h-3.5 w-3.5", "aria-hidden": true }), "AI suggest"] })] })] }, row.id))) })), selectedOrphan && (_jsxs("div", { className: "border-border bg-muted/30 rounded-lg border p-4", children: [_jsxs("p", { className: "text-foreground mb-2 text-sm font-medium", children: ["AI suggestions for \u201C", selectedOrphan.title, "\u201D"] }), _jsx(SuggestionList, { suggestions: suggestions, loading: suggestLoading, targetEntityId: selectedOrphan.id, onNavigate: onNavigate, planInfo: planInfo, onApplied: (applied) => {
|
|
133
|
+
setSuggestions((prev) => prev.filter((row) => !(row.fingerprint === applied.fingerprint &&
|
|
134
|
+
row.sourceEntityId === applied.sourceEntityId)));
|
|
135
|
+
refetch();
|
|
136
|
+
void loadSuggestions(selectedOrphan);
|
|
137
|
+
} })] }))] }) }), _jsx(SectionCard, { title: "Under-linked pages", description: "Fewer than 2 outbound internal links", children: _jsx("div", { id: "under-linked-list", children: data.underLinked.length === 0 ? (_jsx("p", { className: "text-muted-foreground text-sm", children: "All crawled pages have adequate outbound links." })) : (_jsx("ul", { className: "divide-border max-h-80 divide-y overflow-y-auto rounded-lg border", children: data.underLinked.map((row) => (_jsxs("li", { className: "flex items-center justify-between gap-3 px-4 py-3 text-sm", children: [_jsx("span", { className: "text-foreground min-w-0 truncate font-medium", children: row.title }), _jsxs("span", { className: "text-muted-foreground shrink-0 tabular-nums", children: [row.outboundInternal, " outbound"] })] }, row.id))) })) }) }), _jsx(SectionCard, { title: "Live link health scan", description: "Probe outbound URLs (rate-limited). Reports broken links and internal redirects (3xx) so trailing-slash hrefs can be rewritten to the canonical URL.", action: _jsxs("button", { type: "button", className: btnSecondary, onClick: () => void handleLinkHealthScan(), disabled: healthLoading, children: [healthLoading ? (_jsx(Loader2, { className: "h-4 w-4 animate-spin", "aria-hidden": true })) : (_jsx(RefreshCw, { className: "h-4 w-4", "aria-hidden": true })), "Scan now"] }), children: healthIssues && healthIssues.issues.length > 0 ? (_jsx("ul", { className: "divide-border max-h-64 divide-y overflow-y-auto rounded-lg border", children: healthIssues.issues.map((issue) => (_jsxs("li", { className: "px-4 py-3 text-sm", children: [_jsx("p", { className: "text-foreground font-medium", children: issue.page }), _jsxs("p", { className: "text-muted-foreground mt-0.5 flex items-center gap-1 truncate text-xs", children: [_jsx(ExternalLink, { className: "h-3 w-3 shrink-0", "aria-hidden": true }), issue.url, issue.kind === 'redirected' ? (_jsxs("span", { className: "text-muted-foreground ml-1", children: ["HTTP ", issue.status, " \u2192 ", issue.redirectTarget ?? 'canonical URL'] })) : (_jsxs("span", { className: "text-destructive ml-1", children: ["HTTP ", issue.status || 'error'] })), _jsxs("span", { className: "text-muted-foreground", children: ["\u00B7 ", issue.type] })] })] }, issue.id))) })) : (_jsx("p", { className: "text-muted-foreground text-sm", children: healthIssues
|
|
108
138
|
? 'No broken or redirecting links in the last scan.'
|
|
109
139
|
: 'Run a scan to check live URL responses across published content.' })) }), _jsxs("p", { className: "text-muted-foreground text-xs", children: ["Crawled ", data.postsCrawled.toLocaleString(), " published documents \u00B7 Updated", ' ', new Date(data.scannedAt).toLocaleString()] })] }));
|
|
110
140
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LinksTab.js","sourceRoot":"","sources":["../../../src/views/seo/LinksTab.tsx"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"LinksTab.js","sourceRoot":"","sources":["../../../src/views/seo/LinksTab.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prenta/admin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -98,16 +98,16 @@
|
|
|
98
98
|
"tailwindcss": "^4.0.0",
|
|
99
99
|
"typescript": "^5.7.0",
|
|
100
100
|
"vitest": "^4.1.0",
|
|
101
|
-
"@prenta/component-blocks": "0.3.
|
|
102
|
-
"@prenta/core": "4.
|
|
101
|
+
"@prenta/component-blocks": "0.3.1",
|
|
102
|
+
"@prenta/core": "4.3.0",
|
|
103
103
|
"@prenta/plugin-ai": "1.0.0",
|
|
104
104
|
"@prenta/plugin-blocks": "0.2.0",
|
|
105
105
|
"@prenta/plugin-commerce": "0.2.1",
|
|
106
106
|
"@prenta/plugin-forms": "0.1.0",
|
|
107
107
|
"@prenta/plugin-media": "0.2.0",
|
|
108
108
|
"@prenta/plugin-navigation": "0.3.0",
|
|
109
|
-
"@prenta/plugin-seo": "0.1.
|
|
110
|
-
"@prenta/sections-react": "0.17.
|
|
109
|
+
"@prenta/plugin-seo": "0.1.2",
|
|
110
|
+
"@prenta/sections-react": "0.17.21"
|
|
111
111
|
},
|
|
112
112
|
"scripts": {
|
|
113
113
|
"build": "tsc --project tsconfig.json && npx @tailwindcss/cli -i src/styles/build-input.css -o dist/prenta-admin.css --minify",
|
|
@@ -61,6 +61,7 @@ vi.mock('../../lib/seo-service.js', () => ({
|
|
|
61
61
|
updateSeoFields: vi.fn(async () => ({})),
|
|
62
62
|
generateSeoField: (field: string, ctx: unknown) => generateSeoField(field, ctx),
|
|
63
63
|
fetchLinkSuggestions: vi.fn(async () => ({ data: { suggestions: [] } })),
|
|
64
|
+
applySeoInternalLink: vi.fn(async () => ({})),
|
|
64
65
|
SEO_SCHEMA_TYPES: ['Article', 'WebPage'],
|
|
65
66
|
}))
|
|
66
67
|
|
|
@@ -81,7 +82,9 @@ beforeEach(() => {
|
|
|
81
82
|
|
|
82
83
|
describe('SeoEditorPane header', () => {
|
|
83
84
|
it('labels the analysis number as "Content analysis N/100", not "Live analysis"', async () => {
|
|
84
|
-
render(
|
|
85
|
+
render(
|
|
86
|
+
<SeoEditorPane entityType="page" entityId="p1" onClose={() => {}} onNavigate={() => {}} />,
|
|
87
|
+
)
|
|
85
88
|
// Score header renders once the record loads.
|
|
86
89
|
expect(await screen.findByText(/SEO score:/)).toBeTruthy()
|
|
87
90
|
await waitFor(() => expect(screen.getByText(/Content analysis 72\/100/)).toBeTruthy())
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
3
|
+
import { render, screen } from '@testing-library/react'
|
|
4
|
+
import type { SeoIssue } from '../../lib/seo-service.js'
|
|
5
|
+
|
|
6
|
+
const orphanIssue: SeoIssue = {
|
|
7
|
+
id: 'iss-orphan',
|
|
8
|
+
auditRunId: 'run-1',
|
|
9
|
+
entityType: 'page',
|
|
10
|
+
entityId: 'p1',
|
|
11
|
+
entityTitle: 'Orphan',
|
|
12
|
+
url: '/orphan',
|
|
13
|
+
severity: 'warning',
|
|
14
|
+
category: 'content',
|
|
15
|
+
type: 'orphan-page',
|
|
16
|
+
title: 'Orphan page',
|
|
17
|
+
description: null,
|
|
18
|
+
impact: null,
|
|
19
|
+
recommendation: null,
|
|
20
|
+
status: 'open',
|
|
21
|
+
fixable: true,
|
|
22
|
+
fixActionType: 'insert-link',
|
|
23
|
+
relatedEntityIds: [],
|
|
24
|
+
source: 'audit',
|
|
25
|
+
firstDetectedAt: null,
|
|
26
|
+
lastDetectedAt: null,
|
|
27
|
+
resolvedAt: null,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
vi.mock('../../lib/seo-service.js', async (importOriginal) => {
|
|
31
|
+
const actual = await importOriginal<typeof import('../../lib/seo-service.js')>()
|
|
32
|
+
return {
|
|
33
|
+
...actual,
|
|
34
|
+
fetchSeoIssueFixSuggestion: vi.fn(async () => ({
|
|
35
|
+
suggestion: {
|
|
36
|
+
autoFixable: true,
|
|
37
|
+
source: 'deterministic',
|
|
38
|
+
fixStrategy: 'insert-link',
|
|
39
|
+
fingerprint: 'fp-insert',
|
|
40
|
+
patch: {},
|
|
41
|
+
changes: [],
|
|
42
|
+
justification: 'Add an inbound link from a related page.',
|
|
43
|
+
},
|
|
44
|
+
})),
|
|
45
|
+
applySeoIssueFix: vi.fn(async () => ({})),
|
|
46
|
+
fetchSeoContentBrief: vi.fn(async () => ({})),
|
|
47
|
+
fetchSeoContentResearch: vi.fn(async () => ({})),
|
|
48
|
+
generateSeoContentBrief: vi.fn(async () => ({})),
|
|
49
|
+
ignoreSeoIssue: vi.fn(async () => ({})),
|
|
50
|
+
researchAndBriefSeoIssue: vi.fn(async () => ({})),
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn(), loading: vi.fn() } }))
|
|
55
|
+
|
|
56
|
+
const { SeoIssueFixPanel } = await import('../../components/seo/SeoIssueFixPanel.js')
|
|
57
|
+
|
|
58
|
+
describe('SeoIssueFixPanel insert-link copy', () => {
|
|
59
|
+
it('labels orphan-page insert-link Approve as Approve link, not Approve redirect', async () => {
|
|
60
|
+
render(<SeoIssueFixPanel issue={orphanIssue} expanded />)
|
|
61
|
+
|
|
62
|
+
expect(await screen.findByRole('button', { name: 'Approve link' })).toBeTruthy()
|
|
63
|
+
expect(screen.queryByRole('button', { name: 'Approve redirect' })).toBeNull()
|
|
64
|
+
})
|
|
65
|
+
})
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
3
|
+
import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'
|
|
4
|
+
import type { LinkSuggestion } from '../../lib/seo-service.js'
|
|
5
|
+
|
|
6
|
+
const wrappable: LinkSuggestion = {
|
|
7
|
+
sourceEntityId: 'src-1',
|
|
8
|
+
sourceTitle: 'Related guide',
|
|
9
|
+
sourceUrl: '/guides/related',
|
|
10
|
+
anchorText: 'RankMath',
|
|
11
|
+
confidence: 0.9,
|
|
12
|
+
excerpt: 'We recommend RankMath for on-page SEO.',
|
|
13
|
+
fingerprint: 'fp-1',
|
|
14
|
+
wrappable: true,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const notWrappable: LinkSuggestion = {
|
|
18
|
+
sourceEntityId: 'src-2',
|
|
19
|
+
sourceTitle: 'Other post',
|
|
20
|
+
sourceUrl: '/blog/other',
|
|
21
|
+
anchorText: 'missing phrase',
|
|
22
|
+
confidence: 0.35,
|
|
23
|
+
excerpt: 'This page never mentions the target title.',
|
|
24
|
+
fingerprint: 'fp-2',
|
|
25
|
+
wrappable: false,
|
|
26
|
+
sourceCollection: 'blog',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const fetchLinkSuggestions = vi.fn(async () => ({
|
|
30
|
+
data: {
|
|
31
|
+
target: { id: 'tgt-1', title: 'Orphan', url: '/orphan' },
|
|
32
|
+
suggestions: [wrappable, notWrappable],
|
|
33
|
+
},
|
|
34
|
+
}))
|
|
35
|
+
|
|
36
|
+
const applySeoInternalLink = vi.fn(async () => ({}))
|
|
37
|
+
const fetchPlanInfo = vi.fn(async () => null)
|
|
38
|
+
|
|
39
|
+
vi.mock('../../lib/seo-service.js', () => ({
|
|
40
|
+
fetchLinkSuggestions: () => fetchLinkSuggestions(),
|
|
41
|
+
applySeoInternalLink: () => applySeoInternalLink(),
|
|
42
|
+
}))
|
|
43
|
+
|
|
44
|
+
vi.mock('../../lib/plan.js', async (importOriginal) => {
|
|
45
|
+
const actual = await importOriginal<typeof import('../../lib/plan.js')>()
|
|
46
|
+
return {
|
|
47
|
+
...actual,
|
|
48
|
+
fetchPlanInfo: () => fetchPlanInfo(),
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn(), loading: vi.fn() } }))
|
|
53
|
+
|
|
54
|
+
const { SeoLinkingSection } = await import('../../components/seo/SeoLinkingSection.js')
|
|
55
|
+
|
|
56
|
+
beforeEach(() => {
|
|
57
|
+
fetchLinkSuggestions.mockClear()
|
|
58
|
+
applySeoInternalLink.mockClear()
|
|
59
|
+
fetchPlanInfo.mockClear()
|
|
60
|
+
fetchPlanInfo.mockResolvedValue(null)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
describe('SeoLinkingSection approve UX', () => {
|
|
64
|
+
it('shows Approve link on wrappable rows and Open in editor on the rest', async () => {
|
|
65
|
+
render(
|
|
66
|
+
<SeoLinkingSection
|
|
67
|
+
entityType="page"
|
|
68
|
+
entityId="tgt-1"
|
|
69
|
+
internalLinks={0}
|
|
70
|
+
externalLinks={0}
|
|
71
|
+
internalLinkOk={false}
|
|
72
|
+
externalLinkOk={false}
|
|
73
|
+
onNavigate={() => {}}
|
|
74
|
+
/>,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
expect(await screen.findByRole('button', { name: 'Approve link' })).toBeTruthy()
|
|
78
|
+
|
|
79
|
+
const wrappableRow = screen.getByText('Related guide').closest('li') as HTMLElement
|
|
80
|
+
expect(within(wrappableRow).getByRole('button', { name: 'Approve link' })).toBeTruthy()
|
|
81
|
+
expect(within(wrappableRow).queryByRole('button', { name: 'Open in editor' })).toBeNull()
|
|
82
|
+
|
|
83
|
+
const otherRow = screen.getByText('Other post').closest('li') as HTMLElement
|
|
84
|
+
expect(within(otherRow).getByRole('button', { name: 'Open in editor' })).toBeTruthy()
|
|
85
|
+
expect(within(otherRow).queryByRole('button', { name: 'Approve link' })).toBeNull()
|
|
86
|
+
|
|
87
|
+
expect(screen.queryByText(/apply links in the content editor/i)).toBeNull()
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('hides Approve link and shows upgrade callout when seo.inlineApply is off', async () => {
|
|
91
|
+
render(
|
|
92
|
+
<SeoLinkingSection
|
|
93
|
+
entityType="page"
|
|
94
|
+
entityId="tgt-1"
|
|
95
|
+
internalLinks={0}
|
|
96
|
+
externalLinks={0}
|
|
97
|
+
internalLinkOk={false}
|
|
98
|
+
externalLinkOk={false}
|
|
99
|
+
onNavigate={() => {}}
|
|
100
|
+
planInfo={{
|
|
101
|
+
tier: 'starter',
|
|
102
|
+
features: ['seo.contentBrief'],
|
|
103
|
+
upgradeUrl: 'https://example.com/upgrade',
|
|
104
|
+
}}
|
|
105
|
+
/>,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
expect(await screen.findByText('Related guide')).toBeTruthy()
|
|
109
|
+
expect(screen.queryByRole('button', { name: 'Approve link' })).toBeNull()
|
|
110
|
+
expect(screen.getByText(/Upgrade to unlock SEO inline apply/i)).toBeTruthy()
|
|
111
|
+
|
|
112
|
+
const wrappableRow = screen.getByText('Related guide').closest('li') as HTMLElement
|
|
113
|
+
expect(within(wrappableRow).getByRole('button', { name: 'Open in editor' })).toBeTruthy()
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('opens a blog source in the post editor and drops the row after Approve', async () => {
|
|
117
|
+
const onNavigate = vi.fn()
|
|
118
|
+
render(
|
|
119
|
+
<SeoLinkingSection
|
|
120
|
+
entityType="page"
|
|
121
|
+
entityId="tgt-1"
|
|
122
|
+
internalLinks={0}
|
|
123
|
+
externalLinks={0}
|
|
124
|
+
internalLinkOk={false}
|
|
125
|
+
externalLinkOk={false}
|
|
126
|
+
onNavigate={onNavigate}
|
|
127
|
+
planInfo={{
|
|
128
|
+
tier: 'pro',
|
|
129
|
+
features: ['seo.inlineApply'],
|
|
130
|
+
upgradeUrl: null,
|
|
131
|
+
}}
|
|
132
|
+
/>,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
expect(await screen.findByRole('button', { name: 'Approve link' })).toBeTruthy()
|
|
136
|
+
|
|
137
|
+
fireEvent.click(screen.getByRole('button', { name: 'Open in editor' }))
|
|
138
|
+
expect(onNavigate).toHaveBeenCalledWith('/posts/blog/src-2/edit')
|
|
139
|
+
|
|
140
|
+
fireEvent.click(screen.getByRole('button', { name: 'Approve link' }))
|
|
141
|
+
await waitFor(() => {
|
|
142
|
+
expect(applySeoInternalLink).toHaveBeenCalled()
|
|
143
|
+
expect(screen.queryByRole('button', { name: 'Approve link' })).toBeNull()
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
})
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
editPathForDoc,
|
|
5
|
+
newPathForCollection,
|
|
6
|
+
sourceEditPath,
|
|
7
|
+
} from '../../lib/collection-routes.js'
|
|
4
8
|
|
|
5
9
|
describe('editPathForDoc', () => {
|
|
6
10
|
it('routes post-type documents to the section editor (not the generic route)', () => {
|
|
@@ -22,6 +26,14 @@ describe('editPathForDoc', () => {
|
|
|
22
26
|
})
|
|
23
27
|
})
|
|
24
28
|
|
|
29
|
+
describe('sourceEditPath', () => {
|
|
30
|
+
it('routes a blog source through CollectionRouteMeta so posts open the post editor', () => {
|
|
31
|
+
expect(sourceEditPath('blog', 'src-2')).toBe('/posts/blog/src-2/edit')
|
|
32
|
+
expect(sourceEditPath('pages', 'abc')).toBe('/pages/abc/edit')
|
|
33
|
+
expect(sourceEditPath(undefined, 'abc')).toBe('/pages/abc/edit')
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
25
37
|
describe('newPathForCollection', () => {
|
|
26
38
|
it('routes new post documents to the post editor', () => {
|
|
27
39
|
expect(newPathForCollection({ type: 'post' }, 'case-studies')).toBe('/posts/case-studies/new')
|
|
@@ -8,10 +8,12 @@ vi.mock('../../lib/api.js', () => ({
|
|
|
8
8
|
}))
|
|
9
9
|
|
|
10
10
|
const {
|
|
11
|
+
applySeoInternalLink,
|
|
11
12
|
fetchSeoOverview,
|
|
12
13
|
fetchSeoContentRecords,
|
|
13
14
|
fetchSeoIssues,
|
|
14
15
|
fetchRedirectRules,
|
|
16
|
+
isInlineSeoFixIssue,
|
|
15
17
|
runSeoAudit,
|
|
16
18
|
updateSeoFields,
|
|
17
19
|
} = await import('../../lib/seo-service.js')
|
|
@@ -61,8 +63,17 @@ describe('SEO service read fetchers surface errors', () => {
|
|
|
61
63
|
})
|
|
62
64
|
|
|
63
65
|
it('returns data normally on success', async () => {
|
|
64
|
-
cmsApi.mockResolvedValue({
|
|
65
|
-
|
|
66
|
+
cmsApi.mockResolvedValue({
|
|
67
|
+
data: { records: [{ id: 'r1' }], total: 1, page: 1, pageSize: 50, truncated: false },
|
|
68
|
+
status: 200,
|
|
69
|
+
})
|
|
70
|
+
await expect(fetchSeoContentRecords()).resolves.toEqual({
|
|
71
|
+
records: [{ id: 'r1' }],
|
|
72
|
+
total: 1,
|
|
73
|
+
page: 1,
|
|
74
|
+
pageSize: 50,
|
|
75
|
+
truncated: false,
|
|
76
|
+
})
|
|
66
77
|
})
|
|
67
78
|
|
|
68
79
|
it('mutations still return { error } rather than throwing', async () => {
|
|
@@ -71,6 +82,33 @@ describe('SEO service read fetchers surface errors', () => {
|
|
|
71
82
|
error: 'forbidden',
|
|
72
83
|
})
|
|
73
84
|
})
|
|
85
|
+
|
|
86
|
+
it('applySeoInternalLink POSTs the apply body and returns { error } on failure', async () => {
|
|
87
|
+
cmsApi.mockResolvedValue({ error: 'Not wrappable', status: 422 })
|
|
88
|
+
await expect(
|
|
89
|
+
applySeoInternalLink({
|
|
90
|
+
sourceEntityId: 'src-1',
|
|
91
|
+
targetEntityId: 'tgt-1',
|
|
92
|
+
anchorText: 'RankMath',
|
|
93
|
+
fingerprint: 'fp-1',
|
|
94
|
+
}),
|
|
95
|
+
).resolves.toEqual({ error: 'Not wrappable' })
|
|
96
|
+
expect(cmsApi).toHaveBeenCalledWith('/seo/links/apply', {
|
|
97
|
+
method: 'POST',
|
|
98
|
+
body: JSON.stringify({
|
|
99
|
+
sourceEntityId: 'src-1',
|
|
100
|
+
targetEntityId: 'tgt-1',
|
|
101
|
+
anchorText: 'RankMath',
|
|
102
|
+
fingerprint: 'fp-1',
|
|
103
|
+
}),
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
describe('inline SEO fix issue types', () => {
|
|
109
|
+
it('treats orphan-page as an inline Approve issue', () => {
|
|
110
|
+
expect(isInlineSeoFixIssue('orphan-page')).toBe(true)
|
|
111
|
+
})
|
|
74
112
|
})
|
|
75
113
|
|
|
76
114
|
describe('SEO audit mutation contract', () => {
|
|
@@ -51,7 +51,13 @@ const RECORDS = [
|
|
|
51
51
|
makeRecord({ id: 'seo2', entityType: 'post', entityId: 'p2', title: 'First Post' }),
|
|
52
52
|
]
|
|
53
53
|
|
|
54
|
-
const fetchSeoContentRecords = vi.fn(async () =>
|
|
54
|
+
const fetchSeoContentRecords = vi.fn(async () => ({
|
|
55
|
+
records: RECORDS,
|
|
56
|
+
total: RECORDS.length,
|
|
57
|
+
page: 1,
|
|
58
|
+
pageSize: 50,
|
|
59
|
+
truncated: false,
|
|
60
|
+
}))
|
|
55
61
|
const fetchSeoContentRecord = vi.fn(async (entityType: string, entityId: string) => {
|
|
56
62
|
return RECORDS.find((r) => r.entityType === entityType && r.entityId === entityId) ?? null
|
|
57
63
|
})
|
|
@@ -69,6 +75,7 @@ vi.mock('../../lib/seo-service.js', () => ({
|
|
|
69
75
|
updateSeoFields: (t: string, i: string, p: unknown) => updateSeoFields(t, i, p),
|
|
70
76
|
generateSeoField: vi.fn(async () => ({ unavailable: true })),
|
|
71
77
|
fetchLinkSuggestions: vi.fn(async () => ({ data: { suggestions: [] } })),
|
|
78
|
+
applySeoInternalLink: vi.fn(async () => ({})),
|
|
72
79
|
SEO_SCHEMA_TYPES: ['Article', 'WebPage'],
|
|
73
80
|
applyContentSeoQuery: (
|
|
74
81
|
records: Array<{ entityType: string; title: string }>,
|
|
@@ -162,6 +162,7 @@ export interface SeoEditorPaneProps {
|
|
|
162
162
|
title?: string
|
|
163
163
|
bodyHtml?: string
|
|
164
164
|
featuredImage?: string
|
|
165
|
+
onNavigate?: (path: string) => void
|
|
165
166
|
}
|
|
166
167
|
|
|
167
168
|
export function SeoEditorPane({
|
|
@@ -178,6 +179,7 @@ export function SeoEditorPane({
|
|
|
178
179
|
title,
|
|
179
180
|
bodyHtml,
|
|
180
181
|
featuredImage,
|
|
182
|
+
onNavigate,
|
|
181
183
|
}: SeoEditorPaneProps) {
|
|
182
184
|
const isDraft = !entityId
|
|
183
185
|
const [record, setRecord] = useState<SeoContentRecord | null>(null)
|
|
@@ -994,6 +996,7 @@ export function SeoEditorPane({
|
|
|
994
996
|
externalLinks={Number(externalLinks)}
|
|
995
997
|
internalLinkOk={internalOk}
|
|
996
998
|
externalLinkOk={externalOk}
|
|
999
|
+
onNavigate={onNavigate}
|
|
997
1000
|
/>
|
|
998
1001
|
</SeoAccordionItem>
|
|
999
1002
|
</SeoAccordion>
|
|
@@ -24,6 +24,38 @@ import { hasPlanFeature, type PlanInfo } from '../../lib/plan.js'
|
|
|
24
24
|
import { PlanUpgradeCallout } from '../PlanUpgradeCallout.js'
|
|
25
25
|
import { SeoErrorState, btnPrimary, btnSecondary } from './primitives.js'
|
|
26
26
|
|
|
27
|
+
function approveButtonLabel(strategy: SeoIssueFixSuggestion['fixStrategy']): string {
|
|
28
|
+
switch (strategy) {
|
|
29
|
+
case 'fix-link':
|
|
30
|
+
return 'Fix link in content'
|
|
31
|
+
case 'insert-link':
|
|
32
|
+
return 'Approve link'
|
|
33
|
+
case 'redirect':
|
|
34
|
+
case undefined:
|
|
35
|
+
return 'Approve redirect'
|
|
36
|
+
default: {
|
|
37
|
+
const _exhaustive: never = strategy
|
|
38
|
+
return _exhaustive
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function approveSuccessToast(strategy: SeoIssueFixSuggestion['fixStrategy']): string {
|
|
44
|
+
switch (strategy) {
|
|
45
|
+
case 'fix-link':
|
|
46
|
+
return 'Link updated in content and issue resolved.'
|
|
47
|
+
case 'insert-link':
|
|
48
|
+
return 'Link added and issue resolved.'
|
|
49
|
+
case 'redirect':
|
|
50
|
+
case undefined:
|
|
51
|
+
return 'Redirect created and issue resolved.'
|
|
52
|
+
default: {
|
|
53
|
+
const _exhaustive: never = strategy
|
|
54
|
+
return _exhaustive
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
27
59
|
function formatValue(value: string | boolean | null): string {
|
|
28
60
|
if (value === null || value === '') return '—'
|
|
29
61
|
if (typeof value === 'boolean') return value ? 'Yes' : 'No'
|
|
@@ -321,11 +353,7 @@ export function SeoIssueFixPanel({
|
|
|
321
353
|
toast.error(res.error)
|
|
322
354
|
return
|
|
323
355
|
}
|
|
324
|
-
toast.success(
|
|
325
|
-
suggestion?.fixStrategy === 'fix-link'
|
|
326
|
-
? 'Link updated in content and issue resolved.'
|
|
327
|
-
: 'Redirect created and issue resolved.',
|
|
328
|
-
)
|
|
356
|
+
toast.success(approveSuccessToast(suggestion?.fixStrategy))
|
|
329
357
|
onChanged?.()
|
|
330
358
|
}
|
|
331
359
|
|
|
@@ -491,11 +519,7 @@ export function SeoIssueFixPanel({
|
|
|
491
519
|
disabled={busy}
|
|
492
520
|
>
|
|
493
521
|
<Check className="h-4 w-4" aria-hidden />
|
|
494
|
-
{busy
|
|
495
|
-
? 'Applying…'
|
|
496
|
-
: suggestion.fixStrategy === 'fix-link'
|
|
497
|
-
? 'Fix link in content'
|
|
498
|
-
: 'Approve redirect'}
|
|
522
|
+
{busy ? 'Applying…' : approveButtonLabel(suggestion.fixStrategy)}
|
|
499
523
|
</button>
|
|
500
524
|
) : inlineFix && suggestion?.autoFixable && !inlineApplyEnabled ? (
|
|
501
525
|
<PlanUpgradeCallout feature="seo.inlineApply" upgradeUrl={planInfo?.upgradeUrl} />
|