ML-LT / src /components /SummaryBox.jsx
wokogaming's picture
Upload 57 files
05abd64 verified
import { useState } from 'react';
function SummaryBox({
summary,
totalArticles,
loading,
onResummarize,
resummarizeDisabled = false,
onGenerateTts,
ttsStatus = 'idle',
ttsAudioUrl = '',
ttsError = '',
}) {
const [copied, setCopied] = useState(false);
const handleCopy = async () => {
if (!summary) return;
try {
await navigator.clipboard.writeText(summary);
setCopied(true);
window.setTimeout(() => setCopied(false), 2000);
} catch {
setCopied(false);
}
};
const renderedSummary = summary
?.split('\n')
.map((line) => line.trim())
.filter(Boolean);
const hasSummary = renderedSummary?.length > 0;
const isGeneratingAudio = ttsStatus === 'queued' || ttsStatus === 'processing';
return (
<div className="summary-panel overflow-hidden border-2 border-black bg-white p-6 shadow-[10px_10px_0px_0px_rgba(0,0,0,0.04)] md:p-7">
<div className="mb-6 flex items-center gap-3">
<span className="h-[2px] w-8 bg-black" />
<h2 className="text-xs font-black uppercase tracking-[0.22em] text-black">Bản tóm tắt AI</h2>
{loading ? (
<span className="ml-auto rounded-full border border-black/20 bg-white px-3 py-1 text-[10px] font-black uppercase tracking-[0.12em] text-black animate-pulse">
Đang tóm tắt...
</span>
) : totalArticles ? (
<span className="ml-auto rounded-full border border-black/20 bg-white px-3 py-1 text-[10px] font-black uppercase tracking-[0.12em] text-black">
{totalArticles} bài
</span>
) : null}
</div>
{onResummarize ? (
<div className="mb-6 flex justify-end">
<button
type="button"
onClick={onResummarize}
disabled={resummarizeDisabled}
className="flex items-center gap-2 rounded-full border border-black bg-white px-4 py-2 text-[10px] font-black uppercase tracking-[0.14em] text-black transition-all hover:bg-black hover:text-white disabled:cursor-not-allowed disabled:opacity-60"
>
<span className={`material-symbols-outlined text-sm ${loading ? 'animate-spin' : ''}`}>
{loading ? 'progress_activity' : 'autorenew'}
</span>
Re-summary hôm nay
</button>
</div>
) : null}
{loading ? (
<div className="space-y-4 rounded-lg border border-black/10 bg-slate-50 p-5">
<div className="flex items-center gap-3 text-slate-600">
<span className="material-symbols-outlined summary-spin text-2xl">progress_activity</span>
<p className="text-sm font-semibold">Đang tạo bản tóm tắt thông minh...</p>
</div>
<div className="summary-shimmer h-3 rounded" />
<div className="summary-shimmer h-3 w-[88%] rounded" />
<div className="summary-shimmer h-3 w-[76%] rounded" />
<div className="summary-shimmer h-3 w-[82%] rounded" />
</div>
) : !hasSummary ? (
<div className="rounded-lg border border-dashed border-black/20 bg-slate-50 p-5 text-center">
<span className="material-symbols-outlined text-4xl text-black/25">auto_awesome</span>
<p className="mt-3 text-sm font-medium text-slate-500">
Chọn hoặc tìm kiếm tin tức để tạo bản tóm tắt.
</p>
</div>
) : (
<div>
<div className="max-h-[48vh] space-y-3 overflow-y-auto pr-1 text-[15px] font-medium leading-relaxed text-slate-600 md:text-base">
{renderedSummary?.map((line, index) => (
<p key={`${index}-${line}`} className={/^\d+\./.test(line) ? 'font-semibold text-black' : ''}>
{line}
</p>
))}
</div>
<div className="mt-8 flex flex-wrap items-center gap-4">
<button
type="button"
onClick={onGenerateTts}
disabled={!hasSummary || isGeneratingAudio}
className="flex items-center gap-3 bg-black px-8 py-4 text-xs font-black uppercase tracking-[0.15em] text-white transition-all hover:bg-slate-800 disabled:cursor-not-allowed disabled:opacity-60"
>
<span className={`material-symbols-outlined text-lg ${isGeneratingAudio ? 'animate-spin' : ''}`}>
{isGeneratingAudio ? 'progress_activity' : 'play_arrow'}
</span>
{isGeneratingAudio ? 'Đang tạo audio...' : ttsAudioUrl ? 'Tạo lại bản audio' : 'Nghe bản tin'}
</button>
<button
type="button"
onClick={handleCopy}
className="flex items-center gap-3 border-2 border-black bg-white px-8 py-4 text-xs font-black uppercase tracking-[0.15em] text-black transition-all hover:bg-slate-50"
>
<span className="material-symbols-outlined text-lg">content_copy</span>
{copied ? 'Đã sao chép' : 'Sao chép tóm tắt'}
</button>
</div>
{ttsError ? (
<p className="mt-4 text-xs font-semibold text-red-600">{ttsError}</p>
) : null}
{ttsAudioUrl ? (
<div className="mt-4">
<audio controls className="w-full" src={ttsAudioUrl} preload="none" />
</div>
) : null}
</div>
)}
</div>
);
}
export default SummaryBox;