tuhbooh commited on
Commit
e72fc95
·
verified ·
1 Parent(s): 662a165

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +246 -230
index.html CHANGED
@@ -3,283 +3,299 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Professional Temp Mail</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
  <style>
10
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
11
- body {
12
- font-family: 'Inter', sans-serif;
13
- background-color: #0f172a;
14
- color: #f1f5f9;
15
- }
16
- .dark-card {
17
- background: #1e293b;
18
- border: 1px solid #334155;
19
- }
20
- .mail-content-wrapper {
21
- background: white;
22
- border-radius: 8px;
23
- overflow: hidden;
24
- min-height: 400px;
25
- }
26
- .mail-content-wrapper iframe {
27
- width: 100%;
28
- border: none;
29
- height: 100%;
30
- min-height: 400px;
31
- }
32
  .loader { border-top-color: #6366f1; animation: spinner 0.6s linear infinite; }
33
  @keyframes spinner { to { transform: rotate(360deg); } }
34
- .progress-bar { transition: width 1s linear; }
 
35
 
36
- /* Custom scrollbar */
37
- ::-webkit-scrollbar { width: 6px; }
38
- ::-webkit-scrollbar-track { background: #1e293b; }
39
- ::-webkit-scrollbar-thumb { background: #475569; border-radius: 10px; }
40
-
41
- /* Text content formatting */
42
- .raw-text-content {
43
- color: #1e293b;
44
- line-height: 1.6;
45
- font-family: 'Inter', sans-serif;
46
- white-space: pre-wrap;
47
- word-break: break-word;
48
  }
49
- .raw-text-content a {
 
50
  color: #4f46e5;
51
- text-decoration: underline;
52
  }
53
  </style>
54
  </head>
55
  <body class="min-h-screen py-10 px-4">
56
- <div class="max-w-4xl mx-auto">
57
- <!-- Header -->
58
- <div class="mb-8">
59
- <h1 class="text-3xl font-bold text-white flex items-center gap-3">
60
- <i class="fa-solid fa-shield-halved text-indigo-500"></i> Temp Mail Service
61
- </h1>
62
- <p class="text-slate-400 mt-1">Dịch vụ email tạm thời bảo mật và ẩn danh</p>
63
- </div>
64
 
65
- <!-- Mail Creation Area -->
66
- <div class="dark-card rounded-2xl p-6 shadow-2xl mb-6">
67
- <label class="block text-sm font-medium text-slate-400 mb-2 uppercase tracking-wider">Địa chỉ email hiện tại</label>
68
- <div class="flex flex-col sm:flex-row gap-3">
69
- <div class="relative flex-grow">
70
- <input type="text" id="emailAddr" readonly placeholder="Đang khởi tạo..."
71
- class="w-full bg-slate-900 border border-slate-700 rounded-xl py-3 px-4 pr-12 font-mono text-indigo-400 focus:outline-none focus:border-indigo-500">
72
- <button onclick="copyEmail()" class="absolute right-3 top-3 text-slate-500 hover:text-indigo-400 transition-colors">
73
- <i class="fa-regular fa-copy text-xl"></i>
74
- </button>
75
- </div>
76
- <button onclick="createNewAccount()" class="bg-slate-700 hover:bg-slate-600 text-white font-semibold py-3 px-6 rounded-xl flex items-center justify-center gap-2 transition-all">
77
- <i class="fa-solid fa-rotate"></i> Đổi địa chỉ
 
 
 
 
78
  </button>
79
  </div>
80
- <div id="copyToast" class="hidden text-xs text-emerald-400 mt-2 font-medium text-center">Đã sao chép địa chỉ vào clipboard</div>
 
 
81
  </div>
 
 
82
 
83
- <!-- Controls & Status -->
84
- <div class="flex items-center justify-between mb-4 px-2">
85
- <div class="flex items-center gap-3">
86
- <h2 class="text-xl font-bold text-slate-200">Hộp thư đến</h2>
87
- <div id="refreshLoader" class="hidden w-5 h-5 border-2 border-slate-600 loader rounded-full"></div>
88
- </div>
89
- <div class="flex items-center gap-2 text-sm text-slate-500">
90
- <span id="timerText">Làm mới sau 5s</span>
91
- <div class="w-20 h-1.5 bg-slate-800 rounded-full overflow-hidden">
92
- <div id="timerBar" class="progress-bar h-full bg-indigo-500" style="width: 100%"></div>
93
- </div>
94
- </div>
95
  </div>
 
 
 
 
 
96
 
97
- <!-- Inbox List -->
98
- <div id="inboxContainer" class="space-y-3">
99
- <!-- Empty State -->
100
- <div id="emptyState" class="text-center py-20 dark-card rounded-2xl border-dashed border-2 border-slate-700">
101
- <i class="fa-solid fa-inbox text-5xl text-slate-700 mb-4"></i>
102
- <p class="text-slate-500">Đang chờ thư mới...</p>
103
- </div>
104
  </div>
105
  </div>
 
106
 
107
- <!-- Modal Xem Thư (Reader) -->
108
- <div id="mailModal" class="fixed inset-0 bg-slate-950/80 hidden z-50 flex items-center justify-center p-4">
109
- <div class="dark-card w-full max-w-4xl rounded-2xl shadow-2xl flex flex-col max-h-[90vh]">
110
- <div class="p-4 border-b border-slate-700 flex justify-between items-center">
111
- <h3 class="font-bold text-slate-200 truncate pr-4" id="modalSubject">Tiêu đề thư</h3>
112
- <button onclick="closeMail()" class="text-slate-500 hover:text-white text-2xl">&times;</button>
113
- </div>
114
- <div class="p-4 bg-slate-900/50 border-b border-slate-700 text-sm">
115
- <div id="modalFrom" class="text-indigo-400 font-medium"></div>
116
- <div id="modalTime" class="text-slate-500 text-xs mt-1"></div>
117
- </div>
118
- <div class="p-4 overflow-auto flex-grow bg-slate-100 rounded-b-2xl">
119
- <div id="modalBody" class="mail-content-wrapper"></div>
 
 
 
 
120
  </div>
121
  </div>
 
 
 
 
122
  </div>
 
123
 
124
- <script>
125
- const API = "https://api.mail.tm";
126
- let currentToken = "";
127
- let currentEmail = "";
128
- let refreshSec = 5;
129
 
130
- async function init() {
131
- await createNewAccount();
132
- startTimer();
133
- }
134
 
135
- async function createNewAccount() {
136
- try {
137
- const domains = await fetch(`${API}/domains`).then(r => r.json());
138
- const domain = domains['hydra:member'][0].domain;
139
- const user = Math.random().toString(36).substring(2, 12);
140
- const address = `${user}@${domain}`;
141
- const password = "password123";
 
 
 
 
 
 
142
 
143
- await fetch(`${API}/accounts`, {
144
- method: 'POST',
145
- headers: {'Content-Type': 'application/json'},
146
- body: JSON.stringify({address, password})
147
- });
148
 
149
- const tokenData = await fetch(`${API}/token`, {
150
- method: 'POST',
151
- headers: {'Content-Type': 'application/json'},
152
- body: JSON.stringify({address, password})
153
- }).then(r => r.json());
 
 
 
 
 
 
 
154
 
155
- currentToken = tokenData.token;
156
- currentEmail = address;
157
- document.getElementById('emailAddr').value = address;
158
-
159
- document.getElementById('inboxContainer').innerHTML = `
160
- <div id="emptyState" class="text-center py-20 dark-card rounded-2xl border-dashed border-2 border-slate-700">
161
- <i class="fa-solid fa-inbox text-5xl text-slate-700 mb-4"></i>
162
- <p class="text-slate-500">Đang chờ thư mới...</p>
163
- </div>
164
- `;
165
- checkInbox();
166
- } catch (err) { console.error(err); }
167
- }
168
 
169
- async function checkInbox() {
170
- if (!currentToken) return;
171
- document.getElementById('refreshLoader').classList.remove('hidden');
172
- try {
173
- const res = await fetch(`${API}/messages`, {
174
- headers: {'Authorization': `Bearer ${currentToken}`}
175
- }).then(r => r.json());
176
- renderInbox(res['hydra:member']);
177
- } catch (err) { console.error(err); }
178
- document.getElementById('refreshLoader').classList.add('hidden');
179
  }
 
180
 
181
- function renderInbox(msgs) {
182
- const container = document.getElementById('inboxContainer');
183
- const empty = document.getElementById('emptyState');
184
- if (!msgs || msgs.length === 0) return;
 
185
 
186
- if (empty) empty.remove();
187
- const currentIds = Array.from(container.querySelectorAll('.mail-item')).map(m => m.dataset.id);
188
-
189
- msgs.forEach(m => {
190
- if (!currentIds.includes(m.id)) {
191
- const div = document.createElement('div');
192
- div.className = "mail-item bg-slate-800 p-4 rounded-xl border border-slate-700 hover:border-indigo-500 transition-all cursor-pointer flex justify-between items-center group shadow-sm animate-fade-in";
193
- div.dataset.id = m.id;
194
- div.onclick = () => openMail(m.id);
195
- div.innerHTML = `
196
- <div class="flex-grow mr-4 overflow-hidden">
197
- <div class="flex items-center gap-2 mb-1">
198
- <span class="text-xs font-bold text-indigo-400 uppercase tracking-tighter truncate">${m.from.address}</span>
199
- <span class="text-[10px] text-slate-500 whitespace-nowrap">${new Date(m.createdAt).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</span>
200
- </div>
201
- <div class="font-semibold text-slate-200 truncate">${m.subject || '(Không có tiêu đề)'}</div>
 
202
  </div>
203
- <i class="fa-solid fa-arrow-right-long text-slate-600 group-hover:text-indigo-400 group-hover:translate-x-1 transition-all"></i>
204
- `;
205
- container.prepend(div);
206
- }
207
- });
208
- }
 
 
209
 
210
- async function openMail(id) {
211
- const modal = document.getElementById('mailModal');
212
- const bodyWrapper = document.getElementById('modalBody');
213
- bodyWrapper.innerHTML = '<div class="flex justify-center p-20"><div class="loader w-10 h-10 border-4 rounded-full"></div></div>';
214
- modal.classList.remove('hidden');
 
 
 
 
 
215
 
216
- try {
217
- const m = await fetch(`${API}/messages/${id}`, {
218
- headers: {'Authorization': `Bearer ${currentToken}`}
219
- }).then(r => r.json());
220
 
221
- document.getElementById('modalSubject').innerText = m.subject || 'No Subject';
222
- document.getElementById('modalFrom').innerText = `Từ: ${m.from.address}`;
223
- document.getElementById('modalTime').innerText = new Date(m.createdAt).toLocaleString();
224
 
225
- if (m.html && m.html.length > 0) {
226
- bodyWrapper.innerHTML = `<iframe id="mailIframe" title="Email Content"></iframe>`;
227
- const iframe = document.getElementById('mailIframe');
228
- const doc = iframe.contentWindow.document;
229
- doc.open();
230
- doc.write(`
231
- <style>
232
- body { font-family: 'Inter', -apple-system, sans-serif; line-height: 1.6; color: #333; }
233
- img { max-width: 100%; height: auto; }
234
- a { color: #4f46e5; }
235
- </style>
236
- ${m.html[0]}
237
- `);
238
- doc.close();
239
 
240
- // Auto-height adjustment
241
- iframe.onload = () => {
242
- iframe.style.height = doc.body.scrollHeight + 'px';
243
- };
244
- } else {
245
- // Fallback for Text/Markdown-like content
246
- const textWithLinks = m.text.replace(/(https?:\/\/[^\s]+)/g, '<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>');
247
- bodyWrapper.innerHTML = `
248
- <div class="p-6 bg-white h-full overflow-auto">
249
- <div class="raw-text-content">${textWithLinks}</div>
250
- </div>
251
  `;
252
- }
253
- } catch (err) {
254
- bodyWrapper.innerHTML = '<p class="text-red-500 text-center p-10">Lỗi tải nội dung thư. Vui lòng thử lại.</p>';
255
  }
256
- }
257
 
258
- function closeMail() { document.getElementById('mailModal').classList.add('hidden'); }
259
-
260
- function copyEmail() {
261
- const el = document.getElementById('emailAddr');
262
- el.select();
263
- document.execCommand('copy');
264
- const toast = document.getElementById('copyToast');
265
- toast.classList.remove('hidden');
266
- setTimeout(() => toast.classList.add('hidden'), 2000);
 
 
 
 
 
267
  }
 
268
 
269
- function startTimer() {
270
- let count = refreshSec;
271
- const bar = document.getElementById('timerBar');
272
- setInterval(() => {
273
- count--;
274
- if (bar) bar.style.width = (count / refreshSec * 100) + "%";
275
- if (count <= 0) {
276
- checkInbox();
277
- count = refreshSec;
278
- }
279
- }, 1000);
 
 
 
 
 
280
  }
 
281
 
282
- window.onload = init;
283
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
284
  </body>
285
- </html>
 
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Professional Temp Mail - Full Version</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
  <style>
10
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
11
+ body { font-family: 'Inter', sans-serif; background-color: #0f172a; color: #f1f5f9; }
12
+ .dark-card { background: #1e293b; border: 1px solid #334155; }
13
+ .mail-content-wrapper { background: white; border-radius: 8px; overflow: hidden; min-height: 400px; }
14
+ .mail-content-wrapper iframe { width: 100%; border: none; height: 100%; min-height: 400px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  .loader { border-top-color: #6366f1; animation: spinner 0.6s linear infinite; }
16
  @keyframes spinner { to { transform: rotate(360deg); } }
17
+ .animate-fade-in { animation: fadeIn 0.3s ease-out forwards; }
18
+ @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
19
 
20
+ /* Attachment styling */
21
+ .attachment-chip {
22
+ background: #f1f5f9;
23
+ color: #334155;
24
+ padding: 6px 12px;
25
+ border-radius: 6px;
26
+ font-size: 12px;
27
+ display: flex;
28
+ align-items: center;
29
+ gap: 8px;
30
+ border: 1px solid #e2e8f0;
31
+ transition: all 0.2s;
32
  }
33
+ .attachment-chip:hover {
34
+ background: #e2e8f0;
35
  color: #4f46e5;
 
36
  }
37
  </style>
38
  </head>
39
  <body class="min-h-screen py-10 px-4">
 
 
 
 
 
 
 
 
40
 
41
+ <div class="max-w-4xl mx-auto">
42
+ <!-- Header -->
43
+ <div class="mb-8">
44
+ <h1 class="text-3xl font-bold text-white flex items-center gap-3">
45
+ <i class="fa-solid fa-shield-halved text-indigo-500"></i> Temp Mail Pro
46
+ </h1>
47
+ <p class="text-slate-400 mt-1">Dịch vụ email tạm thời - Hỗ trợ tệp đính kèm</p>
48
+ </div>
49
+
50
+ <!-- Mail Creation Area -->
51
+ <div class="dark-card rounded-2xl p-6 shadow-2xl mb-6">
52
+ <label class="block text-sm font-medium text-slate-400 mb-2 uppercase tracking-wider">Địa chỉ email hiện tại</label>
53
+ <div class="flex flex-col sm:flex-row gap-3">
54
+ <div class="relative flex-grow">
55
+ <input type="text" id="emailAddr" readonly placeholder="Đang khởi tạo..." class="w-full bg-slate-900 border border-slate-700 rounded-xl py-3 px-4 pr-12 font-mono text-indigo-400 focus:outline-none focus:border-indigo-500">
56
+ <button onclick="copyEmail()" class="absolute right-3 top-3 text-slate-500 hover:text-indigo-400 transition-colors">
57
+ <i class="fa-regular fa-copy text-xl"></i>
58
  </button>
59
  </div>
60
+ <button onclick="createNewAccount()" class="bg-slate-700 hover:bg-slate-600 text-white font-semibold py-3 px-6 rounded-xl flex items-center justify-center gap-2 transition-all active:scale-95">
61
+ <i class="fa-solid fa-rotate"></i> Đổi địa chỉ
62
+ </button>
63
  </div>
64
+ <div id="copyToast" class="hidden text-xs text-emerald-400 mt-2 font-medium text-center">Đã sao chép địa chỉ vào clipboard</div>
65
+ </div>
66
 
67
+ <!-- Controls -->
68
+ <div class="flex items-center justify-between mb-4 px-2">
69
+ <div class="flex items-center gap-3">
70
+ <h2 class="text-xl font-bold text-slate-200">Hộp thư đến</h2>
71
+ <div id="refreshLoader" class="hidden w-5 h-5 border-2 border-slate-600 loader rounded-full"></div>
 
 
 
 
 
 
 
72
  </div>
73
+
74
+ <button onclick="checkInbox()" class="bg-indigo-600/10 hover:bg-indigo-600/20 text-indigo-400 text-sm font-semibold py-2 px-4 rounded-lg flex items-center gap-2 transition-all border border-indigo-500/20 active:scale-95">
75
+ <i class="fa-solid fa-arrows-rotate" id="refreshIcon"></i> Làm mới hộp thư
76
+ </button>
77
+ </div>
78
 
79
+ <!-- Inbox List -->
80
+ <div id="inboxContainer" class="space-y-3">
81
+ <div id="emptyState" class="text-center py-20 dark-card rounded-2xl border-dashed border-2 border-slate-700">
82
+ <i class="fa-solid fa-inbox text-5xl text-slate-700 mb-4"></i>
83
+ <p class="text-slate-500">Chưa có thư. Nhấn "Làm mới" để kiểm tra.</p>
 
 
84
  </div>
85
  </div>
86
+ </div>
87
 
88
+ <!-- Modal Xem Thư -->
89
+ <div id="mailModal" class="fixed inset-0 bg-slate-950/80 hidden z-50 flex items-center justify-center p-4">
90
+ <div class="dark-card w-full max-w-4xl rounded-2xl shadow-2xl flex flex-col max-h-[90vh]">
91
+ <div class="p-4 border-b border-slate-700 flex justify-between items-center bg-slate-800 rounded-t-2xl">
92
+ <h3 class="font-bold text-slate-200 truncate pr-4" id="modalSubject">Tiêu đề thư</h3>
93
+ <button onclick="closeMail()" class="text-slate-500 hover:text-white text-2xl px-2">&times;</button>
94
+ </div>
95
+
96
+ <!-- Mail Header Info -->
97
+ <div class="p-4 bg-slate-900 border-b border-slate-700 text-sm">
98
+ <div id="modalFrom" class="text-indigo-400 font-medium"></div>
99
+ <div id="modalTime" class="text-slate-500 text-xs mt-1"></div>
100
+
101
+ <!-- Attachments Area -->
102
+ <div id="attachmentArea" class="hidden mt-4 pt-3 border-t border-slate-800">
103
+ <p class="text-[10px] text-slate-500 uppercase tracking-widest mb-2">Tệp đính kèm:</p>
104
+ <div id="attachmentList" class="flex flex-wrap gap-2"></div>
105
  </div>
106
  </div>
107
+
108
+ <div class="p-4 overflow-auto flex-grow bg-slate-100 rounded-b-2xl">
109
+ <div id="modalBody" class="mail-content-wrapper"></div>
110
+ </div>
111
  </div>
112
+ </div>
113
 
114
+ <script>
115
+ const API = "https://api.mail.tm";
116
+ let currentToken = "";
117
+ let currentEmail = "";
 
118
 
119
+ async function init() {
120
+ await createNewAccount();
121
+ }
 
122
 
123
+ async function createNewAccount() {
124
+ try {
125
+ const domains = await fetch(`${API}/domains`).then(r => r.json());
126
+ const domain = domains['hydra:member'][0].domain;
127
+ const user = Math.random().toString(36).substring(2, 12);
128
+ const address = `${user}@${domain}`;
129
+ const password = "password123";
130
+
131
+ await fetch(`${API}/accounts`, {
132
+ method: 'POST',
133
+ headers: {'Content-Type': 'application/json'},
134
+ body: JSON.stringify({address, password})
135
+ });
136
 
137
+ const tokenData = await fetch(`${API}/token`, {
138
+ method: 'POST',
139
+ headers: {'Content-Type': 'application/json'},
140
+ body: JSON.stringify({address, password})
141
+ }).then(r => r.json());
142
 
143
+ currentToken = tokenData.token;
144
+ currentEmail = address;
145
+ document.getElementById('emailAddr').value = address;
146
+
147
+ document.getElementById('inboxContainer').innerHTML = `
148
+ <div id="emptyState" class="text-center py-20 dark-card rounded-2xl border-dashed border-2 border-slate-700">
149
+ <i class="fa-solid fa-inbox text-5xl text-slate-700 mb-4"></i>
150
+ <p class="text-slate-500">Đang chờ thư mới...</p>
151
+ </div>`;
152
+ checkInbox();
153
+ } catch (err) { console.error(err); }
154
+ }
155
 
156
+ async function checkInbox() {
157
+ if (!currentToken) return;
158
+ const loader = document.getElementById('refreshLoader');
159
+ const icon = document.getElementById('refreshIcon');
160
+ loader.classList.remove('hidden');
161
+ if (icon) icon.classList.add('fa-spin');
 
 
 
 
 
 
 
162
 
163
+ try {
164
+ const res = await fetch(`${API}/messages`, {
165
+ headers: {'Authorization': `Bearer ${currentToken}`}
166
+ }).then(r => r.json());
167
+ renderInbox(res['hydra:member']);
168
+ } catch (err) { console.error(err); } finally {
169
+ setTimeout(() => {
170
+ loader.classList.add('hidden');
171
+ if (icon) icon.classList.remove('fa-spin');
172
+ }, 500);
173
  }
174
+ }
175
 
176
+ function renderInbox(msgs) {
177
+ const container = document.getElementById('inboxContainer');
178
+ const empty = document.getElementById('emptyState');
179
+ if (!msgs || msgs.length === 0) return;
180
+ if (empty) empty.remove();
181
 
182
+ const currentIds = Array.from(container.querySelectorAll('.mail-item')).map(m => m.dataset.id);
183
+
184
+ msgs.forEach(m => {
185
+ if (!currentIds.includes(m.id)) {
186
+ const div = document.createElement('div');
187
+ div.className = "mail-item bg-slate-800 p-4 rounded-xl border border-slate-700 hover:border-indigo-500 transition-all cursor-pointer flex justify-between items-center group shadow-sm animate-fade-in";
188
+ div.dataset.id = m.id;
189
+ div.onclick = () => openMail(m.id);
190
+
191
+ // Hiển thị icon nếu có file đính kèm
192
+ const hasAttach = m.hasAttachments ? '<i class="fa-solid fa-paperclip text-slate-500 mr-2"></i>' : '';
193
+
194
+ div.innerHTML = `
195
+ <div class="flex-grow mr-4 overflow-hidden">
196
+ <div class="flex items-center gap-2 mb-1">
197
+ <span class="text-xs font-bold text-indigo-400 uppercase tracking-tighter truncate">${m.from.address}</span>
198
+ <span class="text-[10px] text-slate-500 whitespace-nowrap">${new Date(m.createdAt).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</span>
199
  </div>
200
+ <div class="font-semibold text-slate-200 truncate">${hasAttach}${m.subject || '(Không có tiêu đề)'}</div>
201
+ </div>
202
+ <i class="fa-solid fa-arrow-right-long text-slate-600 group-hover:text-indigo-400 group-hover:translate-x-1 transition-all"></i>
203
+ `;
204
+ container.prepend(div);
205
+ }
206
+ });
207
+ }
208
 
209
+ async function openMail(id) {
210
+ const modal = document.getElementById('mailModal');
211
+ const bodyWrapper = document.getElementById('modalBody');
212
+ const attachmentArea = document.getElementById('attachmentArea');
213
+ const attachmentList = document.getElementById('attachmentList');
214
+
215
+ bodyWrapper.innerHTML = '<div class="flex justify-center p-20"><div class="loader w-10 h-10 border-4 rounded-full"></div></div>';
216
+ attachmentArea.classList.add('hidden');
217
+ attachmentList.innerHTML = '';
218
+ modal.classList.remove('hidden');
219
 
220
+ try {
221
+ const m = await fetch(`${API}/messages/${id}`, {
222
+ headers: {'Authorization': `Bearer ${currentToken}`}
223
+ }).then(r => r.json());
224
 
225
+ document.getElementById('modalSubject').innerText = m.subject || 'No Subject';
226
+ document.getElementById('modalFrom').innerText = `Từ: ${m.from.address}`;
227
+ document.getElementById('modalTime').innerText = new Date(m.createdAt).toLocaleString();
228
 
229
+ // Xử tệp đính kèm
230
+ if (m.attachments && m.attachments.length > 0) {
231
+ attachmentArea.classList.remove('hidden');
232
+ m.attachments.forEach(file => {
233
+ const downloadUrl = `${API}${file.downloadUrl}`;
234
+ const fileSize = (file.size / 1024).toFixed(1) + ' KB';
 
 
 
 
 
 
 
 
235
 
236
+ const chip = document.createElement('a');
237
+ chip.href = "javascript:void(0)";
238
+ chip.onclick = (e) => downloadFile(downloadUrl, file.filename);
239
+ chip.className = "attachment-chip";
240
+ chip.innerHTML = `
241
+ <i class="fa-solid fa-file-arrow-down"></i>
242
+ <span>${file.filename} <span class="text-slate-400">(${fileSize})</span></span>
 
 
 
 
243
  `;
244
+ attachmentList.appendChild(chip);
245
+ });
 
246
  }
 
247
 
248
+ if (m.html && m.html.length > 0) {
249
+ bodyWrapper.innerHTML = `<iframe id="mailIframe" title="Email Content"></iframe>`;
250
+ const iframe = document.getElementById('mailIframe');
251
+ const doc = iframe.contentWindow.document;
252
+ doc.open();
253
+ doc.write(`<style>body{font-family:'Inter',sans-serif;line-height:1.6;color:#333;}img{max-width:100%;height:auto;}a{color:#4f46e5;}</style>${m.html[0]}`);
254
+ doc.close();
255
+ iframe.onload = () => { iframe.style.height = doc.body.scrollHeight + 'px'; };
256
+ } else {
257
+ const textWithLinks = m.text.replace(/(https?:\/\/[^\s]+)/g, '<a href="$1" target="_blank" rel="noopener noreferrer" style="color:#4f46e5;text-decoration:underline;">$1</a>');
258
+ bodyWrapper.innerHTML = `<div class="p-6 bg-white h-full overflow-auto" style="color:#1e293b; line-height:1.6; white-space:pre-wrap; word-break:break-word;">${textWithLinks}</div>`;
259
+ }
260
+ } catch (err) {
261
+ bodyWrapper.innerHTML = '<p class="text-red-500 text-center p-10">Lỗi tải nội dung thư. Vui lòng thử lại.</p>';
262
  }
263
+ }
264
 
265
+ // Hàm tải tệp thực tế sử dụng Bearer Token
266
+ async function downloadFile(url, filename) {
267
+ try {
268
+ const response = await fetch(url, {
269
+ headers: { 'Authorization': `Bearer ${currentToken}` }
270
+ });
271
+ const blob = await response.blob();
272
+ const link = document.createElement('a');
273
+ link.href = window.URL.createObjectURL(blob);
274
+ link.download = filename;
275
+ document.body.appendChild(link);
276
+ link.click();
277
+ document.body.removeChild(link);
278
+ } catch (err) {
279
+ console.error("Lỗi tải tệp:", err);
280
+ alert("Không thể tải tệp. Vui lòng kiểm tra kết nối.");
281
  }
282
+ }
283
 
284
+ function closeMail() {
285
+ document.getElementById('mailModal').classList.add('hidden');
286
+ }
287
+
288
+ function copyEmail() {
289
+ const el = document.getElementById('emailAddr');
290
+ el.select();
291
+ document.execCommand('copy');
292
+ const toast = document.getElementById('copyToast');
293
+ toast.classList.remove('hidden');
294
+ setTimeout(() => toast.classList.add('hidden'), 2000);
295
+ }
296
+
297
+ window.onload = init;
298
+ </script>
299
  </body>
300
+ </html>
301
+