| 1 |
import { __ } from "@wordpress/i18n"; |
| 2 |
import React from "react"; |
| 3 |
|
| 4 |
import { I } from "../../analytics/components/common/icons"; |
| 5 |
import { Badge, Card } from "../../analytics/components/common/ui"; |
| 6 |
import { FauxBtn } from "./primitives"; |
| 7 |
|
| 8 |
/** |
| 9 |
* Inert mock of Pro's Content Gaps screen |
| 10 |
* (betterdocs-pro/react-src/admin/content-intelligence/views/GapsView.js): |
| 11 |
* the explainer banner, then one card per detected topic carrying its |
| 12 |
* provenance + confidence badges, the demand meter, the reader queries that |
| 13 |
* evidence it, and the AI-drafted article suggestion. |
| 14 |
*/ |
| 15 |
|
| 16 |
// 5-segment demand meter, same construction as Pro's. |
| 17 |
function DemandMeter( { value } ) { |
| 18 |
const filled = Math.round( ( Math.max( 0, Math.min( 100, value ) ) / 100 ) * 5 ); |
| 19 |
return ( |
| 20 |
<div className="bd-cit-demand"> |
| 21 |
{ [ 0, 1, 2, 3, 4 ].map( ( i ) => ( |
| 22 |
<span key={ i } className={ i < filled ? "is-on" : "" } /> |
| 23 |
) ) } |
| 24 |
</div> |
| 25 |
); |
| 26 |
} |
| 27 |
|
| 28 |
/* Invented gaps of the kind this module actually surfaces: a question readers |
| 29 |
ask in several different wordings that no article answers. */ |
| 30 |
const GAPS = [ |
| 31 |
{ |
| 32 |
semantic: true, |
| 33 |
confidence: __( "High confidence", "betterdocs" ), |
| 34 |
color: "green", |
| 35 |
topic: "SSO login errors", |
| 36 |
volume: 384, |
| 37 |
phrasings: 6, |
| 38 |
demand: 94, |
| 39 |
queries: [ "sso login error", "saml authentication failed", "okta sign in loop", "single sign-on not working" ], |
| 40 |
suggested: "Troubleshooting SSO and SAML Sign-In Errors", |
| 41 |
outline: "1. Check the identity provider metadata\n2. Match the ACS URL and entity ID\n3. Read the SAML response\n4. Common Okta and Azure AD failures" |
| 42 |
}, |
| 43 |
{ |
| 44 |
semantic: true, |
| 45 |
confidence: __( "Medium confidence", "betterdocs" ), |
| 46 |
color: "amber", |
| 47 |
topic: "Exporting a knowledge base to PDF", |
| 48 |
volume: 211, |
| 49 |
phrasings: 4, |
| 50 |
demand: 68, |
| 51 |
queries: [ "export docs to pdf", "print whole knowledge base", "offline documentation download" ], |
| 52 |
suggested: "Exporting Your Knowledge Base as a PDF Handbook", |
| 53 |
outline: "" |
| 54 |
}, |
| 55 |
{ |
| 56 |
semantic: false, |
| 57 |
confidence: __( "Medium confidence", "betterdocs" ), |
| 58 |
color: "amber", |
| 59 |
topic: "Webhook retry behaviour", |
| 60 |
volume: 96, |
| 61 |
phrasings: 3, |
| 62 |
demand: 52, |
| 63 |
queries: [ "webhook retry policy", "failed webhook delivery", "webhook timeout" ], |
| 64 |
suggested: "", |
| 65 |
outline: "" |
| 66 |
} |
| 67 |
]; |
| 68 |
|
| 69 |
function GapCard( { gap } ) { |
| 70 |
return ( |
| 71 |
<Card padded> |
| 72 |
<div className="bd-cit-gap-head"> |
| 73 |
<div className="bd-cit-gap-main"> |
| 74 |
<div className="bd-cit-gap-badges"> |
| 75 |
{ gap.semantic |
| 76 |
? <Badge color="purple">{ __( "AI-detected", "betterdocs" ) }</Badge> |
| 77 |
: <Badge color="slate">{ __( "Search analytics", "betterdocs" ) }</Badge> } |
| 78 |
<Badge color={ gap.color }>{ gap.confidence }</Badge> |
| 79 |
</div> |
| 80 |
<div className="bd-cit-gap-title">{ gap.topic }</div> |
| 81 |
<div className="bd-cit-gap-meta"> |
| 82 |
{ gap.volume } { __( "unanswered searches", "betterdocs" ) } |
| 83 |
{ " · " }{ gap.phrasings } { __( "phrasings", "betterdocs" ) } |
| 84 |
</div> |
| 85 |
</div> |
| 86 |
<div className="bd-cit-gap-side"> |
| 87 |
<DemandMeter value={ gap.demand } /> |
| 88 |
<FauxBtn kind="ghost" size="sm" icon={ <I.X size={ 13 } /> } /> |
| 89 |
</div> |
| 90 |
</div> |
| 91 |
|
| 92 |
<div className="bd-cit-gap-queries"> |
| 93 |
<div className="bd-cit-label">{ __( "Sample reader queries", "betterdocs" ) }</div> |
| 94 |
<div className="bd-cit-chips"> |
| 95 |
{ gap.queries.map( ( q, i ) => <code className="bd-cit-chip" key={ i }>{ q }</code> ) } |
| 96 |
</div> |
| 97 |
</div> |
| 98 |
|
| 99 |
<div className="bd-cit-gap-foot"> |
| 100 |
{ gap.suggested ? ( |
| 101 |
<> |
| 102 |
<div className="bd-cit-label">{ __( "Suggested doc", "betterdocs" ) }</div> |
| 103 |
<div className="bd-cit-gap-suggested">{ gap.suggested }</div> |
| 104 |
{ gap.outline && <pre className="bd-cit-gap-outline">{ gap.outline }</pre> } |
| 105 |
</> |
| 106 |
) : ( |
| 107 |
<div className="bd-cit-gap-draft"> |
| 108 |
<FauxBtn kind="soft" size="sm" icon={ <I.Sparkle size={ 13 } /> }> |
| 109 |
{ __( "Draft title with AI", "betterdocs" ) } |
| 110 |
</FauxBtn> |
| 111 |
<span className="bd-cit-hint">{ __( "Uses your API key", "betterdocs" ) }</span> |
| 112 |
</div> |
| 113 |
) } |
| 114 |
</div> |
| 115 |
</Card> |
| 116 |
); |
| 117 |
} |
| 118 |
|
| 119 |
export default function GapsMock() { |
| 120 |
return ( |
| 121 |
<div className="bda-stack-sm"> |
| 122 |
<div className="bda-banner"> |
| 123 |
<div className="bda-banner-ico"><I.Sparkle size={ 18 } /></div> |
| 124 |
<div className="bd-cit-banner-copy"> |
| 125 |
<div className="bd-cit-banner-title">{ __( "Topics readers want that your docs don't cover", "betterdocs" ) }</div> |
| 126 |
<div className="bd-cit-banner-sub"> |
| 127 |
{ __( "Clustered from searches that came back empty, visits that bounced, and what readers said in feedback. Every topic is re-checked against your existing docs before it is listed.", "betterdocs" ) } |
| 128 |
</div> |
| 129 |
</div> |
| 130 |
<FauxBtn kind="soft" size="sm" icon={ <I.RefreshCw size={ 13 } /> }>{ __( "Analyze now", "betterdocs" ) }</FauxBtn> |
| 131 |
</div> |
| 132 |
|
| 133 |
<div className="bda-stack-sm"> |
| 134 |
{ GAPS.map( ( g, i ) => <GapCard gap={ g } key={ i } /> ) } |
| 135 |
</div> |
| 136 |
</div> |
| 137 |
); |
| 138 |
} |
| 139 |
|