Sửa chế độ chia sẻ privacy trực tiếp khi nhấn chuột phải lên scene

This commit is contained in:
2026-06-08 19:56:46 +07:00
parent a2263b9005
commit d1aa2209a7
5 changed files with 564 additions and 16 deletions
+272 -6
View File
@@ -10,11 +10,22 @@ let miniMapMarker = null;
let systemSettings = { timezone: 'Asia/Ho_Chi_Minh', language: 'vi' };
let returnToDashboardAfterEdit = false;
let assetIdToDelete = null;
let editMiniMap = null;
let editMiniMapMarker = null;
let currentEditingScene = null; // Lưu object scene đang sửa để quản lý chia sẻ
let sharedUsersData = []; // [{id, username, email}]
let sharedEmailsData = []; // [email]
// Initialize when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
try {
console.log("--- Bắt đầu khởi tạo Frontend ---");
// 0. Kiểm tra tham số URL để truy cập trực tiếp
const urlParams = new URLSearchParams(window.location.search);
const urlSceneId = urlParams.get('sceneId');
const urlToken = urlParams.get('token');
// Ưu tiên nạp cấu hình hệ thống trước
fetchSystemSettings();
@@ -26,8 +37,13 @@ document.addEventListener('DOMContentLoaded', () => {
// Chạy tuần tự để tránh xung đột luồng xử lý
checkAuthStatus(); // 2. Kiểm tra đăng nhập
// 3. Khôi phục cảnh đang xem nếu có (sau khi người dùng reload trang)
restoreActiveScene();
// 3. Xử lý logic vào thẳng Scene hoặc khôi phục trang
if (urlSceneId) {
console.log(`[Direct Access] Opening scene ${urlSceneId} from URL`);
openScene(urlSceneId, urlToken ? 'shared' : null, urlToken);
} else {
restoreActiveScene();
}
// Đảm bảo map đã sẵn sàng trước khi nạp data
if (map) {
@@ -662,16 +678,25 @@ async function handleEditDeleteScene(scene) {
const modal = document.getElementById('action-choice-modal');
const title = document.getElementById('action-modal-title');
const editBtn = document.getElementById('btn-edit-action');
const editPrivacyBtn = document.getElementById('btn-edit-privacy-action');
const deleteBtn = document.getElementById('btn-delete-action');
title.innerText = `Scene: ${scene.title}`;
modal.style.display = 'flex';
// Hành động Chỉnh sửa privacy
editPrivacyBtn.onclick = () => {
returnToDashboardAfterEdit = false;
closeActionModal();
// Mở modal metadata, false vì ảnh trên map luôn là ảnh mẹ (không phải child)
openEditMetadataModal(scene, false);
};
// Gán sự kiện cho nút Sửa
editBtn.onclick = () => {
returnToDashboardAfterEdit = false;
closeActionModal();
openEditSceneModal(scene);
openEditMetadataModal(scene, false);
};
// Gán sự kiện cho nút Xóa
@@ -805,9 +830,22 @@ async function openScene(sceneId, privacy, shareToken, force = false, initialPit
initPanoramaViewer(secureImageUrl, hotspots || [], sceneOwnerId, initialPitch, initialYaw);
} catch (error) {
if (typeof closeViewer === 'function') closeViewer();
localStorage.removeItem('activeSceneId');
localStorage.removeItem('activeScenePrivacy');
localStorage.removeItem('activeSceneToken');
// Kiểm tra nếu đang truy cập qua link trực tiếp (URL có sceneId) mà gặp lỗi (do xóa token hoặc token không hợp lệ)
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('sceneId')) {
alert("Bạn không có quyền truy cập hoặc liên kết chia sẻ đã hết hạn. Quay về bản đồ công cộng.");
// Xóa toàn bộ tham số URL và tải lại trang để làm mới trạng thái (về trang chủ dành cho khách)
window.history.replaceState({}, document.title, window.location.pathname);
location.reload();
return;
}
alert(error.message);
}
}
@@ -1275,7 +1313,8 @@ async function loadMyAssets() {
const editButton = document.createElement('button');
editButton.className = 'edit-btn-small';
editButton.innerText = 'Sửa Scene';
editButton.addEventListener('click', () => openEditFromMedia(scene)); // Pass scene object directly
const isChild = asset.parentScenes && asset.parentScenes.length > 0;
editButton.addEventListener('click', () => openEditFromMedia(scene, isChild));
card.querySelector('.media-actions').appendChild(editButton);
}
@@ -1362,16 +1401,243 @@ window.closeSuccessModal = function(e) {
modal.style.display = 'none';
};
window.openEditFromMedia = function(scene) {
window.openEditFromMedia = function(scene, isChild = false) {
if (!scene || !scene._id) {
alert("Không thể chỉnh sửa: Ảnh này không được gắn với một Scene hợp lệ.");
return;
}
returnToDashboardAfterEdit = true;
closeDashboard();
openEditSceneModal(scene);
openEditMetadataModal(scene, isChild);
};
/**
* Mở Modal sửa thông tin Metadata chuyên biệt
*/
window.openEditMetadataModal = function(scene, isChild = false) {
currentEditingScene = scene; // Lưu lại để dùng cho chia sẻ
// Load dữ liệu chia sẻ hiện tại
sharedUsersData = scene.sharedWith || [];
sharedEmailsData = scene.sharedEmails || [];
document.getElementById('edit-modal-scene-id').value = scene._id;
document.getElementById('edit-modal-title').value = scene.name || scene.title || '';
document.getElementById('edit-modal-description').value = scene.description || '';
const lat = scene.gps?.lat || scene.lat;
const lng = scene.gps?.lng || scene.lng;
document.getElementById('edit-modal-lat').value = lat;
document.getElementById('edit-modal-lng').value = lng;
// Xử lý logic Privacy cho Cảnh con
const privacySelect = document.getElementById('edit-modal-privacy');
const childInfo = document.getElementById('edit-child-privacy-info');
if (isChild) {
privacySelect.value = scene.privacy;
privacySelect.disabled = true;
childInfo.style.display = 'block';
} else {
privacySelect.value = scene.privacy;
privacySelect.disabled = false;
childInfo.style.display = 'none';
}
handleEditPrivacyChange(); // Cập nhật hiển thị nút bánh răng
document.getElementById('edit-scene-metadata-modal').style.display = 'flex';
// Khởi tạo Mini Map tại vị trí hiện tại của Scene
setTimeout(() => initEditSceneMiniMap(lat, lng), 100);
};
function closeEditMetadataModal() {
document.getElementById('edit-scene-metadata-modal').style.display = 'none';
if (returnToDashboardAfterEdit) {
returnToDashboardAfterEdit = false;
openDashboard();
openDashboardTab('media-library');
}
}
function initEditSceneMiniMap(lat, lng) {
if (editMiniMap) {
editMiniMap.setView([lat, lng], 16);
if (editMiniMapMarker) editMiniMapMarker.setLatLng([lat, lng]);
editMiniMap.invalidateSize();
return;
}
editMiniMap = L.map('edit-mini-map').setView([lat, lng], 16);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(editMiniMap);
editMiniMapMarker = L.marker([lat, lng], { draggable: true }).addTo(editMiniMap);
editMiniMap.on('click', (e) => {
const { lat, lng } = e.latlng;
editMiniMapMarker.setLatLng([lat, lng]);
document.getElementById('edit-modal-lat').value = lat.toFixed(6);
document.getElementById('edit-modal-lng').value = lng.toFixed(6);
});
}
/**
* Xử lý khi thay đổi Dropdown Privacy trong Modal sửa
*/
window.handleEditPrivacyChange = function() {
const privacy = document.getElementById('edit-modal-privacy').value;
const settingsBtn = document.getElementById('btn-edit-privacy-settings');
const isChild = document.getElementById('edit-modal-privacy').disabled;
if (!isChild && (privacy === 'member' || privacy === 'shared')) {
settingsBtn.style.display = 'block';
} else {
settingsBtn.style.display = 'none';
}
};
/**
* Mở modal cài đặt chi tiết dựa trên loại Privacy
*/
window.openPrivacySettingsModal = function() {
const privacy = document.getElementById('edit-modal-privacy').value;
if (privacy === 'member') {
renderSharedList();
document.getElementById('share-member-modal').style.display = 'flex';
} else if (privacy === 'shared') {
const baseUrl = window.location.origin + window.location.pathname;
const token = currentEditingScene.shareToken || 'đang_tạo_mới...';
document.getElementById('shared-link-input').value = `${baseUrl}?sceneId=${currentEditingScene._id}&token=${token}`;
document.getElementById('share-link-modal').style.display = 'flex';
}
};
/**
* Tìm kiếm người dùng để chia sẻ
*/
window.searchUsersToShare = async function(query) {
const dropdown = document.getElementById('search-results-dropdown');
if (!query || query.length < 2) {
dropdown.style.display = 'none';
return;
}
const token = localStorage.getItem('jwt');
try {
const res = await fetch(`${API_BASE_URL}/users/search?q=${encodeURIComponent(query)}`, {
headers: { 'Authorization': `Bearer ${token}` }
});
const users = await res.json();
dropdown.innerHTML = '';
if (users.length > 0) {
users.forEach(user => {
const item = document.createElement('div');
item.className = 'search-item';
item.innerText = `${user.username} (${user.email})`;
item.onclick = () => addMemberToShare(user);
dropdown.appendChild(item);
});
dropdown.style.display = 'block';
} else {
// Nếu không tìm thấy user, cho phép thêm email thủ công
if (query.includes('@')) {
dropdown.innerHTML = `<div class="search-item" onclick="addEmailToShare('${query}')">Thêm email: ${query}</div>`;
dropdown.style.display = 'block';
} else {
dropdown.style.display = 'none';
}
}
} catch (e) { console.error(e); }
};
function addMemberToShare(user) {
if (!sharedUsersData.some(u => (u._id || u) === user._id)) {
sharedUsersData.push(user);
renderSharedList();
}
document.getElementById('share-user-search').value = '';
document.getElementById('search-results-dropdown').style.display = 'none';
}
window.addEmailToShare = function(email) {
if (!sharedEmailsData.includes(email)) {
sharedEmailsData.push(email);
renderSharedList();
}
document.getElementById('share-user-search').value = '';
document.getElementById('search-results-dropdown').style.display = 'none';
};
function renderSharedList() {
const list = document.getElementById('current-shared-list');
list.innerHTML = '';
sharedUsersData.forEach(user => {
const name = user.username || 'User';
list.innerHTML += `<div class="share-list-item">👤 ${name} <span class="remove-share-btn" onclick="removeShared('user', '${user._id || user}')">&times;</span></div>`;
});
sharedEmailsData.forEach(email => {
list.innerHTML += `<div class="share-list-item">📧 ${email} <span class="remove-share-btn" onclick="removeShared('email', '${email}')">&times;</span></div>`;
});
}
window.removeShared = function(type, id) {
if (type === 'user') sharedUsersData = sharedUsersData.filter(u => (u._id || u) !== id);
else sharedEmailsData = sharedEmailsData.filter(e => e !== id);
renderSharedList();
};
window.closeShareMemberModal = () => document.getElementById('share-member-modal').style.display = 'none';
window.closeShareLinkModal = () => document.getElementById('share-link-modal').style.display = 'none';
/**
* Copy link chia sẻ và đóng modal
*/
window.copySharedLink = function() {
const linkInput = document.getElementById('shared-link-input');
linkInput.select();
navigator.clipboard.writeText(linkInput.value).then(() => {
showSuccessModal("Đã sao chép liên kết vào bộ nhớ!");
closeShareLinkModal();
});
};
async function submitEditScene(e) {
e.preventDefault();
const id = document.getElementById('edit-modal-scene-id').value;
const token = localStorage.getItem('jwt');
// Sử dụng FormData vì API Backend hiện tại đang dùng Multer
const formData = new FormData();
formData.append('title', document.getElementById('edit-modal-title').value);
formData.append('description', document.getElementById('edit-modal-description').value);
formData.append('lat', document.getElementById('edit-modal-lat').value);
formData.append('lng', document.getElementById('edit-modal-lng').value);
formData.append('privacy', document.getElementById('edit-modal-privacy').value);
formData.append('shareExpireDays', document.getElementById('share-link-expire').value);
// Đính kèm dữ liệu chia sẻ nâng cao
formData.append('sharedWithUsers', JSON.stringify(sharedUsersData.map(u => u._id || u)));
formData.append('sharedEmails', JSON.stringify(sharedEmailsData));
try {
const res = await fetch(`${API_BASE_URL}/scenes/${id}`, {
method: 'PUT',
headers: { 'Authorization': `Bearer ${token}` },
body: formData
});
const data = await res.json();
if (!res.ok) throw new Error(data.message);
showSuccessModal("Đã cập nhật thông tin cảnh thành công!");
closeEditMetadataModal();
loadScenes();
} catch (err) {
alert("Lỗi cập nhật: " + err.message);
}
}
/**
* Opens a specific tab within the dashboard.
* @param {string} tabName - The ID of the tab pane to open (e.g., 'profile', 'my-scenes').