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;