diff --git a/hotel-data-status.js b/hotel-data-status.js
index 2fda602..cff2b21 100644
--- a/hotel-data-status.js
+++ b/hotel-data-status.js
@@ -18,23 +18,16 @@
function applyPendingState(data) {
const box = ensureStatusBox();
- if (box) {
- box.innerHTML = '⚠️ Chưa có snapshot live cho flow mới. Preview không dùng danh sách fallback giả. Sau khi merge, workflow Hotel sẽ tự discovery Google Hotels và ghi dữ liệu thật.';
- }
+ if (box) box.innerHTML = 'Dữ liệu khách sạn đang được cập nhật. Vui lòng quay lại sau ít phút.';
- if ($('discoveryMeta')) {
- const maxPages = Number(data?.search?.max_pages || 0);
- $('discoveryMeta').textContent = `0/${maxPages || '—'} trang API · chưa chạy live refresh`;
- }
- if ($('hotelResults')) {
- $('hotelResults').innerHTML = '
Chưa có dữ liệu Google Hotels live.
Danh sách khách sạn sẽ xuất hiện sau lần workflow cập nhật đầu tiên.
';
- }
- if ($('resultCount')) $('resultCount').textContent = '0 khách sạn · chờ refresh';
+ if ($('discoveryMeta')) $('discoveryMeta').textContent = 'Đang cập nhật danh sách khách sạn';
+ if ($('hotelResults')) $('hotelResults').innerHTML = 'Danh sách khách sạn đang được cập nhật.
';
+ if ($('resultCount')) $('resultCount').textContent = 'Đang cập nhật';
if ($('cheapestPrice')) $('cheapestPrice').textContent = '—';
- if ($('cheapestName')) $('cheapestName').textContent = 'Chưa có snapshot live';
+ if ($('cheapestName')) $('cheapestName').textContent = 'Chưa có dữ liệu giá';
if ($('groupEstimate')) $('groupEstimate').textContent = '—';
- if ($('updatedAgo')) $('updatedAgo').textContent = 'Chưa chạy';
- if ($('updatedAt')) $('updatedAt').textContent = 'Workflow sẽ chạy sau khi merge';
+ if ($('updatedAgo')) $('updatedAgo').textContent = 'Đang cập nhật';
+ if ($('updatedAt')) $('updatedAt').textContent = '';
}
function applyLegacyWarning(data) {
@@ -42,9 +35,7 @@
const legacy = properties.some(item => item?.catalogue_fallback || String(item?.id || '').startsWith('fallback:'));
if (!legacy && data?.search?.query === 'Shanghai hotels') return false;
const box = ensureStatusBox();
- if (box) {
- box.innerHTML = '⚠️ Snapshot cũ. Dữ liệu này chưa được tạo từ discovery “Shanghai hotels”; không nên dùng để đánh giá giá hoặc khoảng cách.';
- }
+ if (box) box.innerHTML = 'Dữ liệu đang được làm mới. Một số giá hoặc thông tin có thể chưa cập nhật.';
return true;
}
@@ -90,4 +81,8 @@
}
init();
+
+ const copyScript = document.createElement('script');
+ copyScript.src = './hotel-ui-copy.js';
+ document.head.appendChild(copyScript);
})();
diff --git a/hotel-ui-copy.js b/hotel-ui-copy.js
new file mode 100644
index 0000000..db6ad11
--- /dev/null
+++ b/hotel-ui-copy.js
@@ -0,0 +1,132 @@
+(() => {
+ const DATA_URL = './data/hotels.json';
+ const $ = id => document.getElementById(id);
+ let data = null;
+
+ function setText(node, value) {
+ if (node && node.textContent !== value) node.textContent = value;
+ }
+
+ function replaceText(root, from, to) {
+ if (!root) return;
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
+ const nodes = [];
+ while (walker.nextNode()) nodes.push(walker.currentNode);
+ nodes.forEach(node => {
+ if (node.nodeValue.includes(from)) node.nodeValue = node.nodeValue.replaceAll(from, to);
+ });
+ }
+
+ function formatCount(value) {
+ const n = Number(value || 0);
+ return Number.isFinite(n) ? new Intl.NumberFormat('vi-VN').format(n) : '0';
+ }
+
+ function applyStaticCopy() {
+ setText(document.querySelector('.hotel-heading .hotel-kicker'), 'Khách sạn');
+ setText(document.querySelector('.hotel-heading h1'), 'Khách sạn tại Shanghai');
+ setText(document.querySelector('.hotel-heading p'), 'So sánh giá, vị trí và đánh giá cho đêm 19–20/10/2026. Khoảng cách được tính từ 531 Jinling East Road để bạn dễ chọn nơi lưu trú phù hợp.');
+ setText(document.querySelector('.hotel-results-head .hotel-kicker'), 'Danh sách');
+ setText(document.querySelector('.hotel-results-head h2'), 'Khách sạn phù hợp');
+
+ const note = document.querySelector('.hotel-note:not(#hotelDataStatus)');
+ setText(note, 'Giá hiển thị dành cho 2 người lớn/phòng và có thể thay đổi theo hạng phòng, thuế, phí hoặc tình trạng phòng tại thời điểm đặt.');
+
+ setText(document.querySelector('.hotel-history .hotel-kicker'), 'Theo dõi giá');
+ setText(document.querySelector('.hotel-history h2'), 'Lịch sử giá');
+
+ const historyMeta = $('historyMeta');
+ if (historyMeta && /workflow|lần ghi nhận|Mỗi điểm/i.test(historyMeta.textContent || '')) {
+ setText(historyMeta, 'Theo dõi biến động giá của khách sạn đã chọn.');
+ }
+
+ const targetCard = document.querySelector('.hotel-target-card > p');
+ setText(targetCard, 'Đặt mức giá mong muốn để dễ theo dõi.');
+
+ const groupStat = [...document.querySelectorAll('.hotel-stat')].find(card => /Ước tính 3 phòng|Tổng 3 phòng/i.test(card.textContent || ''));
+ if (groupStat) {
+ setText(groupStat.querySelector('span'), 'Tổng 3 phòng');
+ setText(groupStat.querySelector('small'), '6 người lớn');
+ }
+ }
+
+ function applyResultCopy() {
+ const results = $('hotelResults');
+ if (!results) return;
+ replaceText(results, 'Google Hotels chưa trả giá', 'Chưa có giá');
+ replaceText(results, 'đang chờ Google Hotels', 'Tạm chưa có giá');
+ replaceText(results, 'Chưa có rating', 'Chưa có đánh giá');
+ replaceText(results, ' review', ' đánh giá');
+ replaceText(results, 'Ước tính 3 phòng cho 6 người', '3 phòng / 6 người');
+ }
+
+ function applyDetailCopy() {
+ const detail = document.getElementById('hotelDetailBackdrop');
+ if (!detail) return;
+ replaceText(detail, 'Google Hotels chưa trả ảnh cho khách sạn này trong snapshot hiện tại.', 'Hiện chưa có ảnh cho khách sạn này.');
+ replaceText(detail, 'Thông tin chi tiết vẫn có thể xem bên dưới.', 'Bạn vẫn có thể xem thông tin khách sạn bên dưới.');
+ replaceText(detail, 'Google Hotels chưa trả giá live', 'Tạm chưa có giá');
+ replaceText(detail, ' review', ' đánh giá');
+ replaceText(detail, 'Gallery và chi tiết lấy từ snapshot Google Hotels hiện tại; mở modal không phát sinh thêm SerpApi request.', 'Thông tin và hình ảnh được cập nhật từ nguồn dữ liệu khách sạn hiện có.');
+ replaceText(detail, 'Nguồn giá đang có', 'Giá từ các nguồn');
+ }
+
+ function applyDataCopy() {
+ if (!data || data.status === 'pending_refresh') return;
+ const properties = Array.isArray(data.properties) ? data.properties : [];
+ const search = data.search || {};
+ const visible = Number(search.displayed_property_count ?? properties.length ?? 0);
+ const priced = Number(search.priced_property_count ?? 0);
+ const value = `${formatCount(visible)} khách sạn · ${formatCount(priced)} có giá`;
+ setText($('discoveryMeta'), value);
+ }
+
+ function professionalStatusCopy() {
+ const box = $('hotelDataStatus');
+ if (!box) return;
+ const raw = box.textContent || '';
+ if (/Chưa có snapshot live|flow mới|workflow Hotel|fallback giả|chưa chạy live refresh/i.test(raw)) {
+ box.innerHTML = 'Dữ liệu khách sạn đang được cập nhật. Vui lòng quay lại sau ít phút.';
+ } else if (/Snapshot cũ|discovery/i.test(raw)) {
+ box.innerHTML = 'Dữ liệu đang được làm mới. Một số giá hoặc thông tin có thể chưa cập nhật.';
+ }
+ }
+
+ async function loadData() {
+ try {
+ const response = await fetch(`${DATA_URL}?copy=${Date.now()}`, { cache: 'no-store' });
+ if (response.ok) data = await response.json();
+ } catch {}
+ applyDataCopy();
+ professionalStatusCopy();
+ }
+
+ applyStaticCopy();
+ applyResultCopy();
+ applyDetailCopy();
+ loadData();
+
+ const results = $('hotelResults');
+ if (results) {
+ const resultsObserver = new MutationObserver(() => {
+ resultsObserver.disconnect();
+ applyResultCopy();
+ resultsObserver.observe(results, { childList: true });
+ });
+ resultsObserver.observe(results, { childList: true });
+ }
+
+ document.addEventListener('click', event => {
+ if (event.target.closest('[data-hotel-detail]')) {
+ setTimeout(applyDetailCopy, 0);
+ }
+ });
+
+ setTimeout(() => {
+ applyStaticCopy();
+ applyResultCopy();
+ applyDetailCopy();
+ applyDataCopy();
+ professionalStatusCopy();
+ }, 300);
+})();