diff --git a/backend/routes/apiRoutes.js b/backend/routes/apiRoutes.js
index aab4a8a..c5d80f4 100644
--- a/backend/routes/apiRoutes.js
+++ b/backend/routes/apiRoutes.js
@@ -638,7 +638,7 @@ router.delete('/assets/:id', protect, async (req, res) => {
if (!asset) return res.status(404).json({ message: 'Ảnh không tồn tại' });
// Kiểm tra quyền: Người upload hoặc Admin (Chủ sở hữu)
- const isOwner = asset.uploadedBy.toString() === req.user._id.toString();
+ const isOwner = asset.uploadedBy && asset.uploadedBy.toString() === req.user._id.toString();
const isAdmin = req.user.role === 'Chủ sở hữu' || req.user.role === 'admin';
if (!isOwner && !isAdmin) {
@@ -659,7 +659,7 @@ router.delete('/assets/:id', protect, async (req, res) => {
}
// 2. Xóa file vật lý trên disk
- if (fs.existsSync(asset.filePath)) {
+ if (asset.filePath && fs.existsSync(asset.filePath)) {
fs.unlinkSync(asset.filePath);
}
diff --git a/frontend/css/style.css b/frontend/css/style.css
index cb70bb3..c1f86f5 100644
--- a/frontend/css/style.css
+++ b/frontend/css/style.css
@@ -594,7 +594,9 @@ html, body {
box-shadow: 0 10px 40px rgba(0,0,0,0.6) !important;
}
-#logout-confirm-modal {
+#logout-confirm-modal,
+#delete-asset-confirm-modal,
+#success-modal {
z-index: 5500; /* Cao hơn Dashboard (4500) và Close Button (5000) */
}
diff --git a/frontend/index.html b/frontend/index.html
index 820873f..b17f809 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -132,7 +132,7 @@
×
-
Create New 3D Scene
+ Create New 3D Scene
+
+
+
+
Xác nhận xóa ảnh
+
Bạn có chắc chắn muốn xóa ảnh này? Nếu ảnh đang được gắn vào một cảnh, cảnh đó cũng sẽ bị xóa vĩnh viễn.
+
+
+
+
+
+
+
+
+
+
+
✓
+
Thành công
+
Dữ liệu đã được cập nhật.
+
+
+
diff --git a/frontend/js/main_map.js b/frontend/js/main_map.js
index a9db2cc..b5c2993 100644
--- a/frontend/js/main_map.js
+++ b/frontend/js/main_map.js
@@ -8,6 +8,8 @@ let previousSceneId = null;
let miniMap = null;
let miniMapMarker = null;
let systemSettings = { timezone: 'Asia/Ho_Chi_Minh', language: 'vi' };
+let returnToDashboardAfterEdit = false;
+let assetIdToDelete = null;
// Initialize when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
@@ -420,6 +422,10 @@ function openCreateSceneModal(lat, lng) {
document.getElementById('modal-scene-id').value = '';
document.getElementById('modal-lat').value = lat.toFixed(6);
document.getElementById('modal-lng').value = lng.toFixed(6);
+
+ const lang = systemSettings.language || 'vi';
+ const modalTitle = document.getElementById('create-scene-modal-title');
+ if (modalTitle) modalTitle.innerText = lang === 'vi' ? "Tạo 3D scene mới" : "Create New 3D Scene";
}
/**
@@ -689,6 +695,10 @@ function openEditSceneModal(scene) {
document.getElementById('modal-privacy').value = scene.privacy;
document.getElementById('modal-panorama').required = false; // Photo update is optional
+ const lang = systemSettings.language || 'vi';
+ const modalTitle = document.getElementById('create-scene-modal-title');
+ if (modalTitle) modalTitle.innerText = lang === 'vi' ? "Sửa 3D scene" : "Edit 3D scene";
+
toggleSharedUsers();
document.getElementById('create-scene-modal').style.display = 'flex';
}
@@ -1277,22 +1287,38 @@ async function loadMyAssets() {
/**
* Xóa ảnh khỏi kho media
*/
-window.deleteAsset = async function(assetId) {
- if (!confirm('Bạn có chắc chắn muốn xóa ảnh này? Nếu ảnh đang được gắn vào một cảnh, cảnh đó cũng sẽ bị xóa vĩnh viễn.')) {
- return;
- }
+window.deleteAsset = function(assetId) {
+ assetIdToDelete = assetId;
+ document.getElementById('delete-asset-confirm-modal').style.display = 'flex';
+};
+window.closeDeleteAssetModal = function() {
+ document.getElementById('delete-asset-confirm-modal').style.display = 'none';
+ assetIdToDelete = null;
+};
+
+window.confirmDeleteAsset = async function() {
+ if (!assetIdToDelete) return;
+
const token = localStorage.getItem('jwt');
try {
- const res = await fetch(`${API_BASE_URL}/assets/${assetId}`, {
+ const res = await fetch(`${API_BASE_URL}/assets/${assetIdToDelete}`, {
method: 'DELETE',
headers: { 'Authorization': `Bearer ${token}` }
});
- const data = await res.json();
+
+ let data;
+ const contentType = res.headers.get("content-type");
+ if (contentType && contentType.includes("application/json")) {
+ data = await res.json();
+ }
- if (!res.ok) throw new Error(data.message);
+ if (!res.ok) {
+ throw new Error(data?.message || `Lỗi máy chủ (${res.status})`);
+ }
- alert(data.message);
+ closeDeleteAssetModal();
+ showSuccessModal(data.message || "Đã xóa thành công");
loadMyAssets(); // Nạp lại kho ảnh
loadScenes(); // Nạp lại bản đồ nếu có scene bị xóa kèm theo
} catch (e) {
@@ -1300,11 +1326,38 @@ window.deleteAsset = async function(assetId) {
}
};
+/**
+ * Hiển thị Modal thông báo thành công
+ */
+window.showSuccessModal = function(message) {
+ const modal = document.getElementById('success-modal');
+ const msgElem = document.getElementById('success-modal-message');
+ if (modal && msgElem) {
+ msgElem.innerText = message;
+ modal.style.display = 'flex';
+ // Tự động ẩn sau 3 giây
+ setTimeout(() => closeSuccessModal(), 3000);
+ }
+};
+
+/**
+ * Đóng Modal thành công (hỗ trợ click ra ngoài)
+ */
+window.closeSuccessModal = function(e) {
+ const modal = document.getElementById('success-modal');
+ 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';
+};
+
window.openEditFromMedia = function(scene) {
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);
};