Sửa chữa giao diện, lỗi privacy

This commit is contained in:
2026-06-11 09:02:54 +07:00
parent edd91d4d64
commit be149f26ca
9 changed files with 236 additions and 64 deletions
+185 -42
View File
@@ -711,7 +711,11 @@ function openCreateTourModal(lat, lng) {
* Đóng Modal tạo Tour
*/
function closeTourModal() {
document.getElementById('create-tour-modal').style.display = 'none';
const modal = document.getElementById('create-tour-modal');
if (modal) {
modal.style.display = 'none';
modal.removeAttribute('data-original-display');
}
if (tempMarker) {
map.removeLayer(tempMarker);
tempMarker = null;
@@ -733,9 +737,14 @@ function openEditTourModal(tour) {
const token = localStorage.getItem('jwt');
if (!token) return;
dashboardReturnTab = 'my-scenes';
returnToDashboardAfterEdit = true;
closeDashboard();
const isDashboardOpen = document.getElementById('dashboard-overlay').style.display === 'flex';
if (isDashboardOpen) {
dashboardReturnTab = 'my-scenes';
returnToDashboardAfterEdit = true;
closeDashboard();
} else {
returnToDashboardAfterEdit = false;
}
const tourIdInput = document.getElementById('tour-id');
if (tourIdInput) tourIdInput.value = tour._id;
@@ -833,8 +842,16 @@ function openCreateSceneModal(lat, lng, tourId = null) {
* Closes the Create Scene Modal and removes temporary marker
*/
function closeModal() {
document.getElementById('create-tour-modal').style.display = 'none'; // Đảm bảo đóng cả modal Tour
document.getElementById('create-scene-modal').style.display = 'none';
const tourModal = document.getElementById('create-tour-modal');
if (tourModal) {
tourModal.style.display = 'none';
tourModal.removeAttribute('data-original-display');
}
const sceneModal = document.getElementById('create-scene-modal');
if (sceneModal) {
sceneModal.style.display = 'none';
sceneModal.removeAttribute('data-original-display');
}
if (tempMarker) {
map.removeLayer(tempMarker);
tempMarker = null;
@@ -1144,12 +1161,12 @@ async function handleEditDeleteScene(scene) {
// Cập nhật nhãn và sự kiện cho nút Xóa
deleteBtn.innerHTML = tour ? '<span class="icon">🗑️</span> Xóa vĩnh viễn Tour' : '<span class="icon">🗑️</span> Xóa vĩnh viễn';
deleteBtn.onclick = () => {
deleteBtn.onclick = async () => {
returnToDashboardAfterEdit = false; // Đảm bảo không mở dashboard nếu xóa từ map
closeActionModal();
if (tour) {
// Tái sử dụng logic xóa tour từ dashboard
if (confirm(`Bạn có chắc muốn xóa Tour "${tour.name}" và toàn bộ cảnh bên trong?`)) {
if (await window.showConfirmModal(`Bạn có chắc muốn xóa Tour "${tour.name}" và toàn bộ cảnh bên trong?`)) {
confirmDeleteTourFromMap(tour._id);
}
} else {
@@ -1187,10 +1204,15 @@ async function confirmDeleteTourFromMap(tourId) {
*/
window.deleteScene = async function(sceneId, sceneData = null) { // Thêm sceneData để tránh fetch lại
sceneIdToDelete = sceneId;
// Tạm thời đóng dashboard và lưu trạng thái để mở lại sau
dashboardReturnTab = 'my-scenes';
returnToDashboardAfterEdit = true;
closeDashboard();
// Tạm thời đóng dashboard nếu nó đang mở
const isDashboardOpen = document.getElementById('dashboard-overlay').style.display === 'flex';
if (isDashboardOpen) {
dashboardReturnTab = 'my-scenes';
returnToDashboardAfterEdit = true;
closeDashboard();
} else {
returnToDashboardAfterEdit = false;
}
const confirmModal = document.getElementById('delete-scene-confirm-modal');
const confirmMessageElem = document.getElementById('delete-scene-confirm-message'); // Giả định có element này trong HTML
@@ -1546,7 +1568,7 @@ window.handleHotspotCreation = async function(pitch, yaw, existingHotspot = null
return;
}
await saveHotspotToDB(pitch, yaw, formData.get('title'), formData.get('description'), finalTargetId, existingHotspot?._id);
modal.style.display = 'none';
closeHotspotModal();
};
};
@@ -1554,7 +1576,11 @@ window.handleHotspotCreation = async function(pitch, yaw, existingHotspot = null
* Đóng Modal biên tập Hotspot
*/
function closeHotspotModal() {
document.getElementById('hotspot-modal').style.display = 'none';
const modal = document.getElementById('hotspot-modal');
if (modal) {
modal.style.display = 'none';
modal.removeAttribute('data-original-display');
}
}
/**
@@ -1680,7 +1706,7 @@ async function saveHotspotToDB(pitch, yaw, title, description, targetSceneId, ho
* Gọi lệnh: systemReset() từ trình duyệt
*/
window.systemReset = async function() {
if (!confirm("CẢNH BÁO: Thao tác này sẽ xóa sạch TOÀN BỘ scene và ảnh trên server. Bạn có chắc chắn?")) return;
if (!await window.showConfirmModal("CẢNH BÁO: Thao tác này sẽ xóa sạch TOÀN BỘ scene và ảnh trên server. Bạn có chắc chắn?")) return;
const token = localStorage.getItem('jwt');
try {
@@ -1720,7 +1746,7 @@ window.openHotspotMenu = function(hotspot) {
// Hành động Xóa: Xác nhận và gọi API xóa
deleteBtn.onclick = async () => {
if (confirm('Bạn có chắc chắn muốn xóa điểm điều hướng này?')) {
if (await window.showConfirmModal('Bạn có chắc chắn muốn xóa điểm điều hướng này?')) {
closeHotspotActionModal();
await deleteHotspot(hotspot._id);
}
@@ -1831,6 +1857,10 @@ async function loadMyTours() {
card.style.borderLeft = `5px solid ${tour.privacy === 'public' ? '#28a745' : '#ffc107'}`;
const currentUserId = localStorage.getItem('userId');
const userRole = localStorage.getItem('role');
const isTourOwner = (tour.createdBy?._id === currentUserId || tour.createdBy === currentUserId || userRole === 'admin' || userRole === 'Chủ sở hữu');
card.innerHTML = `
<div class="scene-card-overlay">
<div class="scene-card-info">
@@ -1844,8 +1874,10 @@ async function loadMyTours() {
</div>
</div>
<div class="media-actions" style="border: none; padding: 0;">
<button class="edit-btn-small" id="edit-tour-${tour._id}" style="background:#007bff">Sửa</button>
<button class="delete-btn-small" id="delete-tour-${tour._id}">Xóa</button>
${isTourOwner ? `
<button class="edit-btn-small" id="edit-tour-${tour._id}" style="background:#007bff">Sửa</button>
<button class="delete-btn-small" id="delete-tour-${tour._id}">Xóa</button>
` : ''}
<button class="edit-btn-small" id="view-tour-${tour._id}" style="background:#28a745">Xem</button>
</div>
</div>
@@ -1853,26 +1885,32 @@ async function loadMyTours() {
listContainer.appendChild(card);
// Nút Sửa Tour
document.getElementById(`edit-tour-${tour._id}`).onclick = () => {
openEditTourModal(tour);
};
const editBtn = document.getElementById(`edit-tour-${tour._id}`);
if (editBtn) {
editBtn.onclick = () => {
openEditTourModal(tour);
};
}
// Nút Xóa Tour: Gọi API xóa Tour (bao gồm xóa cascade các scene bên trong)
document.getElementById(`delete-tour-${tour._id}`).onclick = async () => {
if (confirm(`Bạn có chắc muốn xóa Tour "${tour.name}" và toàn bộ ${tour.scenes?.length || 0} cảnh bên trong?`)) {
try {
const res = await fetch(`${API_BASE_URL}/tours/${tour._id}`, {
method: 'DELETE',
headers: { 'Authorization': `Bearer ${token}` }
});
if (res.ok) {
showNotification("Đã xóa Tour thành công", "success");
loadMyTours();
loadScenes();
}
} catch (e) { showNotification("Lỗi xóa tour", "error"); }
}
};
const deleteBtn = document.getElementById(`delete-tour-${tour._id}`);
if (deleteBtn) {
deleteBtn.onclick = async () => {
if (await window.showConfirmModal(`Bạn có chắc muốn xóa Tour "${tour.name}" và toàn bộ ${tour.scenes?.length || 0} cảnh bên trong?`)) {
try {
const res = await fetch(`${API_BASE_URL}/tours/${tour._id}`, {
method: 'DELETE',
headers: { 'Authorization': `Bearer ${token}` }
});
if (res.ok) {
showNotification("Đã xóa Tour thành công", "success");
loadMyTours();
loadScenes();
}
} catch (e) { showNotification("Lỗi xóa tour", "error"); }
}
};
}
// Nút Xem Tour: Bay tới vị trí và mở cảnh khởi đầu
document.getElementById(`view-tour-${tour._id}`).onclick = () => {
@@ -1990,7 +2028,7 @@ window.updateUserByAdmin = async function(userId) {
};
window.deleteUserByAdmin = async function(userId) {
if (!confirm('Xóa vĩnh viễn người dùng này?')) return;
if (!await window.showConfirmModal('Xóa vĩnh viễn người dùng này?')) return;
const token = localStorage.getItem('jwt');
try {
const res = await fetch(`${API_BASE_URL}/admin/users/${userId}`, {
@@ -2170,12 +2208,26 @@ async function loadMyAssets() {
*/
window.deleteAsset = function(assetId) {
assetIdToDelete = assetId;
const isDashboardOpen = document.getElementById('dashboard-overlay').style.display === 'flex';
if (isDashboardOpen) {
dashboardReturnTab = 'media-library';
returnToDashboardAfterEdit = true;
closeDashboard();
} else {
returnToDashboardAfterEdit = false;
}
document.getElementById('delete-asset-confirm-modal').style.display = 'flex';
};
window.closeDeleteAssetModal = function() {
document.getElementById('delete-asset-confirm-modal').style.display = 'none';
assetIdToDelete = null;
if (returnToDashboardAfterEdit) {
const targetTab = dashboardReturnTab;
returnToDashboardAfterEdit = false;
openDashboard();
openDashboardTab(targetTab);
}
};
window.confirmDeleteAsset = async function() {
@@ -2207,6 +2259,40 @@ window.confirmDeleteAsset = async function() {
}
};
window.hiddenModalsStack = window.hiddenModalsStack || [];
window.tempHideActiveModals = function(exceptModalIds = []) {
const modals = document.querySelectorAll('.modal, .modal-overlay');
const hiddenInThisTurn = [];
modals.forEach(modal => {
if (exceptModalIds.includes(modal.id)) return;
const style = window.getComputedStyle(modal);
if (style.display !== 'none' && modal.style.display !== 'none') {
const originalDisplay = modal.style.display || style.display;
modal.setAttribute('data-original-display', originalDisplay);
modal.style.display = 'none';
hiddenInThisTurn.push(modal);
}
});
if (hiddenInThisTurn.length > 0) {
window.hiddenModalsStack.push(hiddenInThisTurn);
}
};
window.restorePreviousModals = function() {
if (window.hiddenModalsStack && window.hiddenModalsStack.length > 0) {
const lastHiddenGroup = window.hiddenModalsStack.pop();
lastHiddenGroup.forEach(modal => {
if (modal.hasAttribute('data-original-display')) {
const originalDisplay = modal.getAttribute('data-original-display') || 'flex';
modal.style.display = originalDisplay;
modal.removeAttribute('data-original-display');
}
});
}
};
/**
* Hiển thị Modal thông báo thành công
*/
@@ -2215,6 +2301,7 @@ window.showSuccessModal = function(message, icon = '✓') {
const msgElem = document.getElementById('success-modal-message');
const iconElem = document.getElementById('success-modal-icon');
if (modal && msgElem && iconElem) {
window.tempHideActiveModals(['success-modal']);
msgElem.innerText = message;
iconElem.innerText = icon;
modal.style.display = 'flex';
@@ -2231,7 +2318,10 @@ window.closeSuccessModal = function(e) {
if (!modal) return;
// Nếu nhấn từ code (không có e) hoặc click trúng overlay thì đóng
if (e && e.target !== modal) return;
modal.style.display = 'none';
if (modal.style.display !== 'none') {
modal.style.display = 'none';
window.restorePreviousModals();
}
};
/**
@@ -2243,6 +2333,7 @@ window.showErrorModal = function(message, title = "Thông báo", icon = '⚠️'
const titleElem = document.getElementById('error-modal-title');
const iconElem = document.getElementById('error-modal-icon');
if (modal && msgElem && iconElem) {
window.tempHideActiveModals(['error-modal']);
msgElem.innerText = message;
iconElem.innerText = icon;
if (titleElem) titleElem.innerText = title;
@@ -2258,7 +2349,10 @@ window.closeErrorModal = function(e) {
if (!modal) return;
// Nếu nhấn từ code (không có e) hoặc click trúng overlay thì đóng
if (e && e.target !== modal) return;
modal.style.display = 'none';
if (modal.style.display !== 'none') {
modal.style.display = 'none';
window.restorePreviousModals();
}
};
/**
@@ -2274,6 +2368,51 @@ window.showNotification = function(message, type = 'success') {
}
};
// Đè hàm alert mặc định của trình duyệt để gọi modal tương ứng
window.alert = function(message) {
window.showErrorModal(message, 'Thông báo', '⚠️');
};
// Hàm hiển thị confirm dạng modal bất đồng bộ
window.showConfirmModal = function(message) {
return new Promise((resolve) => {
// Tạm ẩn các modal đang mở
window.tempHideActiveModals(['generic-confirm-modal']);
let modal = document.getElementById('generic-confirm-modal');
if (!modal) {
modal = document.createElement('div');
modal.id = 'generic-confirm-modal';
modal.className = 'modal-overlay';
modal.style.zIndex = '9999';
modal.innerHTML = `
<div class="modal-content action-modal-content logout-modal-dark" style="border-top: 4px solid #ffc107; max-width: 400px; text-align: center;">
<div style="font-size: 40px; color: #ffc107; margin-bottom: 10px;">⚠️</div>
<h2 style="color: #fff; margin-bottom: 10px;">Xác nhận</h2>
<p id="generic-confirm-message" style="color: #ccc; margin-bottom: 25px; line-height: 1.5; font-size: 14px; text-align: center;"></p>
<div class="action-buttons" style="display: flex; gap: 10px; justify-content: center;">
<button id="generic-confirm-ok-btn" class="delete-btn-large" style="background: #dc3545; flex: 1; padding: 10px 20px; font-size: 14px; cursor: pointer; border: none; border-radius: 4px; color: white;">Xác nhận</button>
<button id="generic-confirm-cancel-btn" class="edit-btn-large" style="background: #6c757d; flex: 1; padding: 10px 20px; font-size: 14px; cursor: pointer; border: none; border-radius: 4px; color: white;">Hủy bỏ</button>
</div>
</div>
`;
document.body.appendChild(modal);
}
document.getElementById('generic-confirm-message').innerText = message;
modal.style.display = 'flex';
const handleResolve = (value) => {
modal.style.display = 'none';
window.restorePreviousModals();
resolve(value);
};
document.getElementById('generic-confirm-ok-btn').onclick = () => handleResolve(true);
document.getElementById('generic-confirm-cancel-btn').onclick = () => handleResolve(false);
});
};
window.openEditFromMedia = function(scene, isChild = false) {
if (!scene || !scene._id) {
showNotification("Không thể chỉnh sửa: Ảnh này không được gắn với một Scene hợp lệ.", 'error');
@@ -2347,7 +2486,11 @@ window.openEditMetadataModal = function(scene, isChildArg = null) {
};
function closeEditMetadataModal() {
document.getElementById('edit-scene-metadata-modal').style.display = 'none';
const modal = document.getElementById('edit-scene-metadata-modal');
if (modal) {
modal.style.display = 'none';
modal.removeAttribute('data-original-display');
}
if (returnToDashboardAfterEdit) {
const targetTab = dashboardReturnTab;
returnToDashboardAfterEdit = false;
@@ -2586,7 +2729,7 @@ async function handleBackup() {
*/
async function handleRestore(input) {
if (!input.files || !input.files[0]) return;
if (!confirm("CẢNH BÁO: Khôi phục dữ liệu sẽ xóa sạch dữ liệu hiện tại và thay thế bằng dữ liệu từ bản sao lưu. Bạn có chắc chắn?")) {
if (!await window.showConfirmModal("CẢNH BÁO: Khôi phục dữ liệu sẽ xóa sạch dữ liệu hiện tại và thay thế bằng dữ liệu từ bản sao lưu. Bạn có chắc chắn?")) {
input.value = ''; return;
}
const token = localStorage.getItem('jwt');
@@ -2643,7 +2786,7 @@ async function updateSystemSettings(e) {
*/
window.recalculateAllTourCenters = async function() {
const token = localStorage.getItem('jwt');
if (!confirm("Bạn có chắc chắn muốn tính toán lại tọa độ trung tâm cho TOÀN BỘ Tour trong hệ thống? Việc này có thể mất một chút thời gian nếu dữ liệu lớn.")) return;
if (!await window.showConfirmModal("Bạn có chắc chắn muốn tính toán lại tọa độ trung tâm cho TOÀN BỘ Tour trong hệ thống? Việc này có thể mất một chút thời gian nếu dữ liệu lớn.")) return;
try {
showNotification("Đang xử lý tính toán lại...", "success");