| 1 |
import { __ } from "@wordpress/i18n"; |
| 2 |
import React, { useState } from "react"; |
| 3 |
|
| 4 |
import { I } from "../analytics/components/common/icons"; |
| 5 |
import { cn, ProBadge } from "../analytics/components/common/ui"; |
| 6 |
import { PageHeader } from "../analytics/components/Chrome"; |
| 7 |
import LockedPreview from "../analytics/components/modules/LockedPreview"; |
| 8 |
|
| 9 |
import CIOverviewMock from "./mocks/CIOverviewMock"; |
| 10 |
import GapsMock from "./mocks/GapsMock"; |
| 11 |
import StaleMock from "./mocks/StaleMock"; |
| 12 |
import HealthMock from "./mocks/HealthMock"; |
| 13 |
import DuplicatesMock from "./mocks/DuplicatesMock"; |
| 14 |
import { CopyIcon } from "./mocks/primitives"; |
| 15 |
|
| 16 |
import "../analytics/scss/dashboard.scss"; |
| 17 |
import "./content-intelligence-teaser.scss"; |
| 18 |
|
| 19 |
/** |
| 20 |
* Free's Content Intelligence screen — the locked teaser for Pro's v2.0 |
| 21 |
* Content Intelligence pillar. Only the WP admin menu item is shortened to |
| 22 |
* "Content IQ" (and the page slug to `betterdocs-content-iq`); every heading, |
| 23 |
* title and description on the screen itself still says Content Intelligence. |
| 24 |
* |
| 25 |
* This is the *full shell* variant, the same shape Free's Analytics teaser |
| 26 |
* uses: the real left-hand sub-nav with all five modules, and a per-module |
| 27 |
* `LockedPreview` over a design-accurate mock of that module. Browsing the nav |
| 28 |
* is the point — someone deciding on an upgrade gets to see each of the five |
| 29 |
* screens they'd be buying, not one generic upsell. |
| 30 |
* |
| 31 |
* Free only registers the `betterdocs-content-iq` route when Pro does |
| 32 |
* not ship Content Intelligence (`betterdocs_admin.content_intelligence_teaser`, |
| 33 |
* decided in PHP by `betterdocs()->show_content_intelligence_teaser()`), so this |
| 34 |
* can never shadow Pro's real screen. The page title comes from the shared admin |
| 35 |
* header (`betterdocs_admin_header_title` in `components/Routes.js`); everything |
| 36 |
* below it is ours. |
| 37 |
*/ |
| 38 |
|
| 39 |
/* Sub-tab ids and labels are identical to Pro's Content Intelligence nav group |
| 40 |
(betterdocs-pro/react-src/admin/analytics/components/Chrome.js), including the |
| 41 |
icons, so the teaser and the real thing are the same screen with the data |
| 42 |
removed. Each entry also owns its upsell copy — one array keeps the nav, the |
| 43 |
lock panel and the mock from drifting apart. */ |
| 44 |
const TABS = [ |
| 45 |
{ |
| 46 |
id: "ci-overview", |
| 47 |
label: __( "Overview", "betterdocs" ), |
| 48 |
icon: I.Sparkle, |
| 49 |
mock: CIOverviewMock, |
| 50 |
heading: { title: __( "Content Intelligence", "betterdocs" ), subtitle: __( "What to fix next — gaps, freshness, and health across your knowledge base.", "betterdocs" ) }, |
| 51 |
title: __( "Content Intelligence is a Pro feature", "betterdocs" ), |
| 52 |
subtitle: __( "One screen that answers \"what should I fix next?\" — a health grade for the whole knowledge base, the single highest-impact action this week, and a live count for every intelligence module. It runs on the analytics BetterDocs already collects.", "betterdocs" ), |
| 53 |
bullets: [ |
| 54 |
__( "Gaps, stale docs, health score and applied suggestions at a glance", "betterdocs" ), |
| 55 |
__( "\"This week's top action\", ranked by real reader demand", "betterdocs" ), |
| 56 |
__( "The evidence behind it: empty searches, bounced visits, feedback", "betterdocs" ), |
| 57 |
__( "Module tiles that jump straight to the fix", "betterdocs" ) |
| 58 |
] |
| 59 |
}, |
| 60 |
{ |
| 61 |
id: "gaps", |
| 62 |
label: __( "Content Gaps", "betterdocs" ), |
| 63 |
icon: I.Search, |
| 64 |
mock: GapsMock, |
| 65 |
heading: { title: __( "Content Gap Detection", "betterdocs" ), subtitle: __( "Topics your readers want that your docs don't cover yet.", "betterdocs" ) }, |
| 66 |
title: __( "Content Gap Detection is a Pro feature", "betterdocs" ), |
| 67 |
subtitle: __( "Finds the topics your readers keep asking for and your docs never answer. BetterDocs clusters searches that came back empty, visits that bounced, and feedback mentions into topics, then re-checks each one against your existing docs before listing it.", "betterdocs" ), |
| 68 |
bullets: [ |
| 69 |
__( "Missing topics ranked by demand, with the queries that evidence them", "betterdocs" ), |
| 70 |
__( "Confidence scoring so you can act on the strongest signals first", "betterdocs" ), |
| 71 |
__( "Accept, dismiss, or draft the doc title with your own AI key", "betterdocs" ), |
| 72 |
__( "Grouped by meaning — not just matching words — with the AI Chatbot add-on", "betterdocs" ) |
| 73 |
] |
| 74 |
}, |
| 75 |
{ |
| 76 |
id: "stale", |
| 77 |
label: __( "Stale Content", "betterdocs" ), |
| 78 |
icon: I.Clock, |
| 79 |
mock: StaleMock, |
| 80 |
heading: { title: __( "Stale Content", "betterdocs" ), subtitle: __( "Docs losing accuracy, freshness, or engagement — ranked by staleness.", "betterdocs" ) }, |
| 81 |
title: __( "Stale Content detection is a Pro feature", "betterdocs" ), |
| 82 |
subtitle: __( "Scores every doc on age, declining views and reactions, outdated version mentions, and broken links — so you refresh the docs that are actually decaying rather than simply the oldest ones.", "betterdocs" ), |
| 83 |
bullets: [ |
| 84 |
__( "A 0–100 staleness score with the reason flags behind it", "betterdocs" ), |
| 85 |
__( "Per-doc view trend, ranked worst first", "betterdocs" ), |
| 86 |
__( "Nightly rule-based scan — no API key required", "betterdocs" ), |
| 87 |
__( "Mark as current, dismiss, or open the doc to fix it", "betterdocs" ) |
| 88 |
] |
| 89 |
}, |
| 90 |
{ |
| 91 |
id: "health", |
| 92 |
label: __( "Health Score", "betterdocs" ), |
| 93 |
icon: I.PieChart, |
| 94 |
mock: HealthMock, |
| 95 |
heading: { title: __( "Content Health Score", "betterdocs" ), subtitle: __( "A single, composite measure of how healthy your knowledge base is.", "betterdocs" ) }, |
| 96 |
title: __( "Content Health Score is a Pro feature", "betterdocs" ), |
| 97 |
subtitle: __( "A single composite grade for your knowledge base, built from five measured components, tracked month over month, and broken down to the individual docs pulling it down.", "betterdocs" ), |
| 98 |
bullets: [ |
| 99 |
__( "Site-wide score and A–F grade, trended over time", "betterdocs" ), |
| 100 |
__( "Engagement, Helpfulness, Freshness, Findability & Accuracy", "betterdocs" ), |
| 101 |
__( "Score distribution across every published doc", "betterdocs" ), |
| 102 |
__( "Lowest-scoring docs with their weakest component named", "betterdocs" ) |
| 103 |
] |
| 104 |
}, |
| 105 |
{ |
| 106 |
id: "duplicates", |
| 107 |
label: __( "Duplicates & Overlaps", "betterdocs" ), |
| 108 |
icon: CopyIcon, |
| 109 |
mock: DuplicatesMock, |
| 110 |
heading: { title: __( "Duplicates & Overlaps", "betterdocs" ), subtitle: __( "Docs covering near-identical ground, detected by content similarity.", "betterdocs" ) }, |
| 111 |
title: __( "Duplicates & Overlaps is a Pro feature", "betterdocs" ), |
| 112 |
subtitle: __( "Two docs can cover the same ground without sharing a single phrase — one written for beginners, one for admins. BetterDocs compares what your docs mean rather than the words they use, and lists the near-identical pairs with a similarity score.", "betterdocs" ), |
| 113 |
bullets: [ |
| 114 |
__( "Near-duplicate doc pairs, scored by content similarity", "betterdocs" ), |
| 115 |
__( "Catches overlaps that share no wording at all", "betterdocs" ), |
| 116 |
__( "Merge, keep both, or dismiss each pair as you review it", "betterdocs" ), |
| 117 |
__( "Semantic detection runs on the BetterDocs AI Chatbot add-on's index", "betterdocs" ) |
| 118 |
] |
| 119 |
} |
| 120 |
]; |
| 121 |
|
| 122 |
/* Per-module heading map for the portalled header, keyed by tab id — the real |
| 123 |
product titles (not the "… is a Pro feature" upsell copy), so the header reads |
| 124 |
exactly like Pro's. Reuses the Analytics PageHeader portal + dark-mode switcher |
| 125 |
via its `titles` prop. */ |
| 126 |
const CI_TITLES = Object.fromEntries( TABS.map( ( t ) => [ t.id, t.heading ] ) ); |
| 127 |
|
| 128 |
export default function ContentIntelligenceTeaser() { |
| 129 |
const [ tab, setTab ] = useState( TABS[ 0 ].id ); |
| 130 |
const active = TABS.find( ( t ) => t.id === tab ) || TABS[ 0 ]; |
| 131 |
const Mock = active.mock; |
| 132 |
|
| 133 |
const nav = ( id ) => { |
| 134 |
setTab( id ); |
| 135 |
window.scrollTo( { top: 0 } ); |
| 136 |
}; |
| 137 |
|
| 138 |
return ( |
| 139 |
<div className="betterdocs-dashboard-body bd-cit-wrapper"> |
| 140 |
{ /* Active-tab title + subtitle, portalled into the shared admin |
| 141 |
header (the default row is skipped for this page in Header.js) — |
| 142 |
same treatment as Analytics, no filters. */ } |
| 143 |
<PageHeader route={ tab } titles={ CI_TITLES } fallbackRoute="ci-overview" showFilters={ false } /> |
| 144 |
<div className="bda-app"> |
| 145 |
<div className="bda-container"> |
| 146 |
<div className="bda-shell"> |
| 147 |
<nav className="bda-nav bd-cit-nav"> |
| 148 |
<div className="bda-nav-title">{ __( "Content Intelligence", "betterdocs" ) }</div> |
| 149 |
<div className="bda-nav-items"> |
| 150 |
{ TABS.map( ( item ) => { |
| 151 |
const Icon = item.icon; |
| 152 |
const isActive = item.id === active.id; |
| 153 |
return ( |
| 154 |
<button |
| 155 |
key={ item.id } |
| 156 |
type="button" |
| 157 |
className={ cn( "bda-nav-item", isActive && "active" ) } |
| 158 |
onClick={ () => nav( item.id ) } |
| 159 |
> |
| 160 |
<Icon size={ 16 } className="bda-nav-ico" strokeWidth={ isActive ? 2.2 : 1.8 } /> |
| 161 |
<span>{ item.label }</span> |
| 162 |
<ProBadge className="bda-nav-pro" /> |
| 163 |
</button> |
| 164 |
); |
| 165 |
} ) } |
| 166 |
</div> |
| 167 |
</nav> |
| 168 |
<main className="bda-main"> |
| 169 |
<LockedPreview |
| 170 |
title={ active.title } |
| 171 |
subtitle={ active.subtitle } |
| 172 |
bullets={ active.bullets } |
| 173 |
> |
| 174 |
<Mock /> |
| 175 |
</LockedPreview> |
| 176 |
</main> |
| 177 |
</div> |
| 178 |
</div> |
| 179 |
</div> |
| 180 |
</div> |
| 181 |
); |
| 182 |
} |
| 183 |
|