import React, { useState } from 'react'; import EmptyState from '../agent-analytics/EmptyState'; import type { CompetitorSnapshot } from '../../service/visibility/visibility.interface'; import { faviconUrlFor, formatDelta, toneToBadgeClass } from './helpers'; import ConfirmExcludeCompetitorModal from './ConfirmExcludeCompetitorModal'; /** Rows shown before "Load more"; the long tail is revealed on demand. * Manually tracked rivals are always kept regardless of rank. */ const INITIAL_LEADERBOARD_ROWS = 6; /** How many extra rows each "Load more" click reveals. */ const LEADERBOARD_ROWS_STEP = 6; /** Props for {@link CompetitorsTable}. */ interface CompetitorsTableProps { /** Competitor snapshots for the selected week (ordered by SOV desc). */ competitors: CompetitorSnapshot[]; /** Merchant's own brand snapshot, pinned at the top of the * leaderboard. Shown even when share-of-voice is 0% so the merchant * always sees their own row alongside the rivals. */ ownBrand?: { name: string; domain: string; shareOfVoice: number; deltaSov: number | null; mentionsCount: number | null; } | null; /** Recomaze client id, passed to the per-row delete action. */ clientId: string; /** Recomaze JWT, passed to the per-row delete action. */ token: string; /** Brand the leaderboard belongs to. */ brandId: string; /** * Fired after a competitor is excluded so the parent can refetch the * leaderboard. The backend already filters excluded domains, so a plain * refetch removes the row from the table. */ onCompetitorExcluded: () => void; } /** Props for {@link SampleQueriesPopover}. */ interface SampleQueriesPopoverProps { /** Localized sample queries for one competitor; render verbatim. */ queries: string[]; } /** * Click-to-open list of the sample queries that surfaced one competitor. * * @param {SampleQueriesPopoverProps} props - Popover props. * @returns {JSX.Element} Trigger + absolute-positioned panel. */ const SampleQueriesPopover = ({ queries, }: SampleQueriesPopoverProps): JSX.Element => { const [active, setActive] = useState(false); if (queries.length === 0) { return -; } return (
| Domain | SOV | Δ vs last week | Mentions | Sample queries | |
|---|---|---|---|---|---|
|
|
{ownBrand.shareOfVoice}% | {ownDelta.hidden ? ( - ) : ( {ownDelta.label} )} | {ownBrand.mentionsCount ?? '-'} | See “Tracked prompts” below | |
|
|
{competitor.share_of_voice}% | {delta.hidden ? ( - ) : ( {delta.label} )} | {competitor.mentions_count} |
|