import React, { useCallback, useEffect, useMemo, useState } from 'react'; import type { PromptResponse, PromptResponseEntry, PromptResponsesResponse, } from '../../service/visibility/visibility.interface'; import { getPromptResponses } from '../../service/visibility/visibility.service'; import { isOwnDomain, sentimentToTone, toneToBadgeClass } from './helpers'; import Modal from './Modal'; interface PromptResponseModalProps { open: boolean; prompt: PromptResponse | null; onClose: () => void; clientId: string; token: string; brandId: string; brandDomain: string | null; } const SNIPPET_PREVIEW_CHARS = 500; const EntryCard = ({ entry, brandDomain, }: { entry: PromptResponseEntry; brandDomain: string | null; }): JSX.Element => { const [expanded, setExpanded] = useState(false); const snippet = entry.response_snippet ?? ''; const shouldFold = snippet.length > SNIPPET_PREVIEW_CHARS; const visibleSnippet = shouldFold && !expanded ? `${snippet.slice(0, SNIPPET_PREVIEW_CHARS).trimEnd()}…` : snippet; const ownCitation = useMemo( () => entry.sources.some(source => isOwnDomain(source, brandDomain)), [entry.sources, brandDomain] ); return (
{entry.engine.toUpperCase()} {entry.scan_date && ( {`Scan date: ${entry.scan_date.slice(0, 10)}`} )} {entry.brand_mentioned ? ( {entry.brand_position ? `Mentioned · position #${entry.brand_position}` : 'Mentioned'} ) : ( Not mentioned )} {entry.sentiment && ( {entry.sentiment} )} {ownCitation && ( Cited from your domain )}
{snippet && (
{visibleSnippet} {shouldFold && (
)}
)} {entry.sources.length > 0 && (

Sources cited

    {entry.sources.map((source, idx) => { const own = isOwnDomain(source, brandDomain); return (
  • {own && (
  • ); })}
)}
); }; /** * Modal showing every historical LLM response recorded for a tracked * prompt. Used as the "proof layer" behind visibility scores. */ const PromptResponseModal = ({ open, prompt, onClose, clientId, token, brandId, brandDomain, }: PromptResponseModalProps): JSX.Element => { const [data, setData] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const load = useCallback(async () => { if (!prompt) return; setLoading(true); setError(null); try { const response = await getPromptResponses( clientId, token, brandId, prompt.prompt_id ); setData(response); } catch (err) { setError( err instanceof Error ? err.message : 'Failed to load responses.' ); } finally { setLoading(false); } }, [clientId, token, brandId, prompt]); useEffect(() => { if (open && prompt) { load(); } if (!open) { setData(null); setError(null); } }, [open, prompt, load]); return ( Close } >
{prompt && (
{prompt.category} {prompt.language.toUpperCase()}

{prompt.prompt_text}

)} {error && (

{error}

)} {loading ? (
) : data && data.entries.length > 0 ? (
{data.entries.map((entry, idx) => ( ))}
) : (

No scan responses recorded yet for this prompt. Run a weekly scan to populate this history.

)}
); }; export default PromptResponseModal;