cawncade-ai / frontend /src /components /OutputPanel.jsx
Nawaz-khan-droid
build: finalize stratos refactor, textarea hardening, and payload limit alignment
acf82bb
Raw
History Blame Contribute Delete
4.02 kB
import React, { useState } from 'react';
import { ChevronDown, ChevronUp, Video } from 'lucide-react';
export default function OutputPanel({ result }) {
const [showTranscript, setShowTranscript] = useState(false);
if (!result) return null;
const confidence = (result.confidence || 0) * 100;
const transcriptPreview = result.video_transcript || '';
const getConfidenceColor = (c) => {
if (c >= 80) return 'text-emerald-400';
if (c >= 60) return 'text-sky-400';
if (c >= 40) return 'text-amber-400';
return 'text-rose-400';
};
return (
<div className="glass-card animate-fade-in space-y-5 p-6">
<div className="flex items-center justify-between gap-4">
<h3 className="text-lg font-semibold text-white">Analysis Result</h3>
<span className={`text-sm font-medium ${getConfidenceColor(confidence)}`}>{confidence.toFixed(1)}% confidence</span>
</div>
{result.answer && (
<div className="space-y-2">
<h4 className="text-xs uppercase tracking-wider text-slate-500">Summary</h4>
<p className="text-sm leading-relaxed text-slate-200" style={{ overflowWrap: 'break-word', wordBreak: 'break-word' }}>{result.answer}</p>
</div>
)}
{result.context_summary && (
<div className="space-y-2">
<h4 className="text-xs uppercase tracking-wider text-slate-500">Verification Context</h4>
<p className="text-sm leading-relaxed text-slate-300" style={{ overflowWrap: 'break-word', wordBreak: 'break-word' }}>{result.context_summary}</p>
</div>
)}
{transcriptPreview && (
<div className="mt-6 border-t border-white/10 pt-6">
<button
type="button"
onClick={() => setShowTranscript((prev) => !prev)}
className="flex w-full items-center justify-between gap-3 text-left transition-colors hover:text-sky-200"
>
<div className="flex items-center gap-2">
<Video className="h-4 w-4 text-sky-400" />
<div>
<h4 className="text-xs uppercase tracking-wider text-sky-300">Video Transcript</h4>
<p className="text-xs text-slate-500">{showTranscript ? 'Hide transcript details' : 'Expand transcript details'}</p>
</div>
</div>
{showTranscript ? <ChevronUp className="h-4 w-4 text-sky-300" /> : <ChevronDown className="h-4 w-4 text-sky-300" />}
</button>
{showTranscript && (
<div
className="custom-scrollbar max-h-[350px] overflow-y-auto rounded-lg border border-sky-500/20 bg-slate-950/40 p-4 text-sm leading-relaxed text-slate-300 whitespace-pre-wrap"
style={{ overflowWrap: 'break-word', wordBreak: 'break-word' }}
>
{transcriptPreview}
</div>
)}
</div>
)}
{result.metadata?.input_type === 'url' && result.metadata?.extraction?.safety_warning && (
<div className="rounded-lg border border-red-500/20 bg-red-500/10 p-3">
<p className="text-xs text-red-400">Safety Warning: {Array.isArray(result.metadata.extraction.safety_warning) ? result.metadata.extraction.safety_warning.join(', ') : result.metadata.extraction.safety_warning}</p>
</div>
)}
{result.agreements && result.agreements.length > 0 && (
<div className="space-y-2">
<h4 className="text-xs uppercase tracking-wider text-slate-500">Corroborating Sources</h4>
<div className="flex flex-wrap gap-2">
{result.agreements.map((source, i) => (
<span key={i} className="rounded-full border border-emerald-400/20 bg-emerald-500/10 px-2 py-1 text-xs text-emerald-300">{source}</span>
))}
</div>
</div>
)}
{result.compute_time_ms && (
<p className="text-xs text-slate-600">Processed in {result.compute_time_ms}ms across {result.metadata?.sources_retrieved || 0} sources</p>
)}
</div>
);
}