Spaces:
Paused
Paused
File size: 5,597 Bytes
05abd64 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | 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;
|