@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.
Files changed (70) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/__tests__/components/seo-editor-pane.render.test.js +2 -1
  3. package/dist/__tests__/components/seo-editor-pane.render.test.js.map +1 -1
  4. package/dist/__tests__/components/seo-issue-fix-panel.render.test.d.ts +2 -0
  5. package/dist/__tests__/components/seo-issue-fix-panel.render.test.d.ts.map +1 -0
  6. package/dist/__tests__/components/seo-issue-fix-panel.render.test.js +60 -0
  7. package/dist/__tests__/components/seo-issue-fix-panel.render.test.js.map +1 -0
  8. package/dist/__tests__/components/seo-linking-section.render.test.d.ts +2 -0
  9. package/dist/__tests__/components/seo-linking-section.render.test.d.ts.map +1 -0
  10. package/dist/__tests__/components/seo-linking-section.render.test.js +94 -0
  11. package/dist/__tests__/components/seo-linking-section.render.test.js.map +1 -0
  12. package/dist/__tests__/lib/collection-routes.test.js +8 -1
  13. package/dist/__tests__/lib/collection-routes.test.js.map +1 -1
  14. package/dist/__tests__/lib/seo-service-errors.test.js +35 -3
  15. package/dist/__tests__/lib/seo-service-errors.test.js.map +1 -1
  16. package/dist/__tests__/views/seo-content-pane.render.test.js +8 -1
  17. package/dist/__tests__/views/seo-content-pane.render.test.js.map +1 -1
  18. package/dist/components/seo/SeoEditorPane.d.ts +2 -1
  19. package/dist/components/seo/SeoEditorPane.d.ts.map +1 -1
  20. package/dist/components/seo/SeoEditorPane.js +2 -2
  21. package/dist/components/seo/SeoEditorPane.js.map +1 -1
  22. package/dist/components/seo/SeoIssueFixPanel.d.ts.map +1 -1
  23. package/dist/components/seo/SeoIssueFixPanel.js +32 -8
  24. package/dist/components/seo/SeoIssueFixPanel.js.map +1 -1
  25. package/dist/components/seo/SeoLinkingSection.d.ts +5 -1
  26. package/dist/components/seo/SeoLinkingSection.d.ts.map +1 -1
  27. package/dist/components/seo/SeoLinkingSection.js +50 -3
  28. package/dist/components/seo/SeoLinkingSection.js.map +1 -1
  29. package/dist/lib/collection-routes.d.ts +2 -0
  30. package/dist/lib/collection-routes.d.ts.map +1 -1
  31. package/dist/lib/collection-routes.js +5 -0
  32. package/dist/lib/collection-routes.js.map +1 -1
  33. package/dist/lib/seo-service.d.ts +25 -2
  34. package/dist/lib/seo-service.d.ts.map +1 -1
  35. package/dist/lib/seo-service.js +23 -3
  36. package/dist/lib/seo-service.js.map +1 -1
  37. package/dist/views/SEO.d.ts.map +1 -1
  38. package/dist/views/SEO.js +1 -1
  39. package/dist/views/SEO.js.map +1 -1
  40. package/dist/views/page-editor/PageSectionEditor.d.ts.map +1 -1
  41. package/dist/views/page-editor/PageSectionEditor.js +1 -1
  42. package/dist/views/page-editor/PageSectionEditor.js.map +1 -1
  43. package/dist/views/post-editor/PostSectionEditor.d.ts.map +1 -1
  44. package/dist/views/post-editor/PostSectionEditor.js +1 -1
  45. package/dist/views/post-editor/PostSectionEditor.js.map +1 -1
  46. package/dist/views/seo/ContentTab.d.ts +2 -1
  47. package/dist/views/seo/ContentTab.d.ts.map +1 -1
  48. package/dist/views/seo/ContentTab.js +18 -12
  49. package/dist/views/seo/ContentTab.js.map +1 -1
  50. package/dist/views/seo/LinksTab.d.ts +3 -1
  51. package/dist/views/seo/LinksTab.d.ts.map +1 -1
  52. package/dist/views/seo/LinksTab.js +36 -6
  53. package/dist/views/seo/LinksTab.js.map +1 -1
  54. package/package.json +5 -5
  55. package/src/__tests__/components/seo-editor-pane.render.test.tsx +4 -1
  56. package/src/__tests__/components/seo-issue-fix-panel.render.test.tsx +65 -0
  57. package/src/__tests__/components/seo-linking-section.render.test.tsx +146 -0
  58. package/src/__tests__/lib/collection-routes.test.ts +13 -1
  59. package/src/__tests__/lib/seo-service-errors.test.ts +40 -2
  60. package/src/__tests__/views/seo-content-pane.render.test.tsx +8 -1
  61. package/src/components/seo/SeoEditorPane.tsx +3 -0
  62. package/src/components/seo/SeoIssueFixPanel.tsx +34 -10
  63. package/src/components/seo/SeoLinkingSection.tsx +116 -18
  64. package/src/lib/collection-routes.ts +13 -0
  65. package/src/lib/seo-service.ts +44 -4
  66. package/src/views/SEO.tsx +2 -1
  67. package/src/views/page-editor/PageSectionEditor.tsx +1 -0
  68. package/src/views/post-editor/PostSectionEditor.tsx +1 -0
  69. package/src/views/seo/ContentTab.tsx +40 -2
  70. package/src/views/seo/LinksTab.tsx +108 -19
@@ -1,4 +1,6 @@
1
- export declare function LinksTab({ onNavigate }: {
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":"AAiIA,wBAAgB,QAAQ,CAAC,EAAE,UAAU,EAAE,EAAE;IAAE,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,2CAkV/E"}
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 (_jsx("ul", { className: "divide-border divide-y rounded-lg border", children: suggestions.map((s) => (_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.sourceEntityId}-${s.anchorText}`))) }));
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 })] }))] }) }), _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
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.6.2",
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.0",
102
- "@prenta/core": "4.1.1",
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.1",
110
- "@prenta/sections-react": "0.17.19"
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(<SeoEditorPane entityType="page" entityId="p1" onClose={() => {}} />)
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 { editPathForDoc, newPathForCollection } from '../../lib/collection-routes.js'
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({ data: { records: [{ id: 'r1' }] }, status: 200 })
65
- await expect(fetchSeoContentRecords()).resolves.toEqual([{ id: 'r1' }])
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 () => RECORDS)
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} />