Cài đặt quota cho các thành viên

This commit is contained in:
2026-06-09 12:29:38 +07:00
parent 9f9c38e6e7
commit 0eaaf074ba
6 changed files with 346 additions and 16 deletions
+63
View File
@@ -422,6 +422,69 @@ html, body {
color: #ccc;
}
/* Storage Progress Bar */
.storage-info {
margin-top: 25px;
padding: 15px;
background: rgba(255, 255, 255, 0.05);
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.storage-info label {
display: block;
margin-bottom: 10px;
font-size: 14px;
color: #aaa;
}
.progress-container {
height: 8px;
background: #444;
border-radius: 4px;
overflow: hidden;
margin-bottom: 8px;
}
.progress-bar {
height: 100%;
background: #28a745;
width: 0%;
transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
#storage-text {
font-size: 12px;
color: #888;
display: block;
text-align: right;
}
.top-files-section {
margin-top: 15px;
padding-top: 15px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.top-files-section h4 {
font-size: 13px;
color: #ffd700;
margin-bottom: 10px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.top-file-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 0;
font-size: 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.03);
}
.top-file-name {
color: #eee;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 180px;
}
.top-file-size { color: #888; font-family: monospace; }
/* Dashboard List Styles */
.dashboard-list {
display: flex;
+95 -10
View File
@@ -133,23 +133,107 @@ function applySystemSettings() {
if (logoutBtn) logoutBtn.innerText = t.logout;
}
/**
* Hàm định dạng dung lượng file cho Frontend
*/
function formatBytes(bytes, decimals = 2) {
if (!bytes || bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
/**
* Tải và hiển thị thống kê các tệp tin lớn nhất
*/
async function loadMediaStats() {
const token = localStorage.getItem('jwt');
const statsContainer = document.getElementById('media-library-stats');
if (!statsContainer) return;
try {
const res = await fetch(`${API_BASE_URL}/me/assets/top-large`, {
headers: { 'Authorization': `Bearer ${token}` }
});
const topFiles = await res.json();
if (topFiles && topFiles.length > 0) {
let html = `
<div class="top-files-section">
<h4><i class="fas fa-database"></i> Tệp tin chiếm dụng lớn nhất</h4>
<div class="top-files-list">
`;
topFiles.forEach(file => {
const fileName = file.scene?.name || file.scene?.title || 'Ảnh chưa gắn Scene';
html += `
<div class="top-file-item">
<span class="top-file-name">● ${fileName}</span>
<span class="top-file-size">${formatBytes(file.fileSize)}</span>
</div>
`;
});
html += `</div></div>`;
statsContainer.innerHTML = html;
} else {
statsContainer.innerHTML = '';
}
} catch (e) {
console.warn("Không thể nạp thống kê media:", e);
}
}
/**
* Cập nhật nội dung tab Hồ sơ với thông tin người dùng
*/
function updateProfileTabContent() {
async function updateProfileTabContent() {
const token = localStorage.getItem('jwt');
const username = localStorage.getItem('username');
const role = localStorage.getItem('role');
if (username) {
const avatar = document.getElementById('profile-avatar-initials');
const userDisplay = document.getElementById('profile-username-display');
const statusDisplay = document.getElementById('profile-status-display');
const userInput = document.getElementById('profile-username');
const avatar = document.getElementById('profile-avatar-initials');
const userDisplay = document.getElementById('profile-username-display');
const statusDisplay = document.getElementById('profile-status-display');
const userInput = document.getElementById('profile-username');
if (avatar) avatar.innerText = username.charAt(0).toUpperCase();
if (userDisplay) userDisplay.innerText = username;
if (statusDisplay) statusDisplay.innerText = role || 'Thành viên';
if (userInput) userInput.value = username;
if (avatar && username) avatar.innerText = username.charAt(0).toUpperCase();
if (userDisplay) userDisplay.innerText = username;
if (statusDisplay) statusDisplay.innerText = role || 'Thành viên';
if (userInput) userInput.value = username;
// Lấy dữ liệu dung lượng thực tế từ server
try {
const res = await fetch(`${API_BASE_URL}/me/profile`, {
headers: { 'Authorization': `Bearer ${token}` }
});
const data = await res.json();
if (data.storage) {
const { used, quota } = data.storage;
const progress = document.getElementById('storage-progress-bar');
const text = document.getElementById('storage-text');
if (progress && text) {
const usedMB = (used / (1024 * 1024)).toFixed(1);
const quotaMB = quota === -1 ? '∞' : (quota / (1024 * 1024)).toFixed(0);
text.innerText = `${usedMB} MB / ${quotaMB} MB`;
if (quota !== -1) {
const percent = Math.min((used / quota) * 100, 100);
progress.style.width = percent + '%';
// Đổi màu thanh tiến trình dựa trên mức độ sử dụng
if (percent > 90) progress.style.background = '#dc3545'; // Đỏ (sắp hết)
else if (percent > 75) progress.style.background = '#ffc107'; // Vàng (cảnh báo)
else progress.style.background = '#28a745'; // Xanh (an toàn)
} else {
progress.style.width = '100%';
progress.style.background = '#007bff'; // Màu xanh dương cho không giới hạn
}
}
}
} catch (e) {
console.warn("Không thể tải thông tin dung lượng:", e);
}
}
@@ -1953,6 +2037,7 @@ function openDashboardTab(tabName) {
loadMyScenes();
}
if (tabName === 'media-library') {
loadMediaStats();
loadMyAssets();
}
if (tabName === 'user-management') {