import React from 'react'; import { CardSkeleton } from '../agent-analytics/CardSkeleton'; import InfoTooltip from '../agent-analytics/InfoTooltip'; import { formatDelta, toneToBadgeClass } from './helpers'; const EXPLANATIONS = { visibility: 'Blended 0–100 score combining share of voice, citation rate from your own domain, and your average position in AI answers across the 16 monitored prompts.', sov: 'Share of Voice - the percentage of brand mentions your domain captured among all brands surfaced by AI answers for the tracked prompts this week.', rank: "Your brand's overall rank among all entities that appeared in this week's AI responses - lower is better. '#3 of 18' means three brands scored above you.", sentiment: 'How AI described your brand this week. Positive / negative percentages reflect the tone of every response that mentioned you; anything not clearly positive or negative is counted as neutral.', }; interface KPIRowProps { visibilityScore: number; shareOfVoice: number; brandRank: number | null; totalCompetingEntities: number | null; deltaScore: number | null; deltaSov: number | null; sentimentPositivePct: number | null; sentimentNegativePct: number | null; deltaSentiment: number | null; loading: boolean; } /** * 4-card KPI row: Visibility score, Share of voice, Rank #X of Y, Sentiment. * Each card's "vs last week" row is hidden when its delta is ``null`` (first * week); ``0`` is treated as "no change". */ const KPIRow = ({ visibilityScore, shareOfVoice, brandRank, totalCompetingEntities, deltaScore, deltaSov, sentimentPositivePct, sentimentNegativePct, deltaSentiment, loading, }: KPIRowProps): JSX.Element => { if (loading) { return (
); } const visibilityDelta = formatDelta(deltaScore, ' pts'); const sovDelta = formatDelta(deltaSov, '%'); const sentimentDelta = formatDelta(deltaSentiment, '%'); const rankLabel = brandRank !== null && totalCompetingEntities !== null ? `#${brandRank} of ${totalCompetingEntities}` : brandRank !== null ? `#${brandRank}` : '-'; const clampPct = (v: number | null): number => { if (v === null || !Number.isFinite(v)) return 0; if (v < 0) return 0; if (v > 100) return 100; return v; }; const positivePct = clampPct(sentimentPositivePct); const negativePct = clampPct(sentimentNegativePct); const hasSentiment = sentimentPositivePct !== null || sentimentNegativePct !== null; return (
{/* Visibility score */}
Visibility score

{visibilityScore} / 100

{!visibilityDelta.hidden && (
{visibilityDelta.label} vs last week
)}
{/* Share of voice */}
Share of voice

{shareOfVoice} %

{!sovDelta.hidden && (
{sovDelta.label} vs last week
)}
{/* Rank */}
Rank

{rankLabel}

{/* Sentiment */}
Sentiment
{hasSentiment ? (
Positive {`${positivePct}%`}
Negative {`${negativePct}%`}
{!sentimentDelta.hidden && (
{sentimentDelta.label} vs last week
)}
) : (

No sentiment data for this period.

)}
); }; export default KPIRow;