1
This commit is contained in:
11
frontend/js/api.js
Normal file → Executable file
11
frontend/js/api.js
Normal file → Executable file
@@ -1,6 +1,7 @@
|
||||
// Linux BenchTools - API Client
|
||||
|
||||
const API_BASE_URL = window.location.protocol + '//' + window.location.hostname + ':8007/api';
|
||||
const API_BASE_URL = (window.BenchConfig && window.BenchConfig.backendApiUrl)
|
||||
|| `${window.location.protocol}//${window.location.hostname}:8007/api`;
|
||||
|
||||
class BenchAPI {
|
||||
constructor(baseURL = API_BASE_URL) {
|
||||
@@ -143,6 +144,14 @@ class BenchAPI {
|
||||
return this.get(`/benchmarks/${benchmarkId}`);
|
||||
}
|
||||
|
||||
// Update benchmark fields
|
||||
async updateBenchmark(benchmarkId, data) {
|
||||
return this.request(`/benchmarks/${benchmarkId}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
}
|
||||
|
||||
// Get all benchmarks
|
||||
async getAllBenchmarks(params = {}) {
|
||||
return this.get('/benchmarks', params);
|
||||
|
||||
45
frontend/js/dashboard.js
Normal file → Executable file
45
frontend/js/dashboard.js
Normal file → Executable file
@@ -6,6 +6,23 @@ const api = window.BenchAPI;
|
||||
// Global state
|
||||
let allDevices = [];
|
||||
let isLoading = false;
|
||||
let apiToken = null;
|
||||
let iperfServer = null;
|
||||
|
||||
// Load backend config (API token, etc.)
|
||||
async function loadBackendConfig() {
|
||||
try {
|
||||
const response = await fetch(`${window.BenchConfig.backendApiUrl}/config`);
|
||||
if (response.ok) {
|
||||
const config = await response.json();
|
||||
apiToken = config.api_token;
|
||||
iperfServer = config.iperf_server || '10.0.1.97';
|
||||
updateBenchCommandDisplay();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load backend config:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Load dashboard data
|
||||
async function loadDashboard() {
|
||||
@@ -83,7 +100,7 @@ async function loadStats() {
|
||||
}
|
||||
});
|
||||
|
||||
const avgScore = scoreCount > 0 ? Math.round(scoreSum / scoreCount) : 0;
|
||||
const avgScore = scoreCount > 0 ? Math.ceil(scoreSum / scoreCount) : 0;
|
||||
|
||||
// Update UI
|
||||
document.getElementById('totalDevices').textContent = totalDevices;
|
||||
@@ -221,6 +238,26 @@ function createDeviceRow(device, rank) {
|
||||
`;
|
||||
}
|
||||
|
||||
function buildBenchCommand() {
|
||||
const cfg = window.BenchConfig || {};
|
||||
const frontendBase = (cfg.frontendBaseUrl || window.location.origin).replace(/\/$/, '');
|
||||
const scriptPath = cfg.benchScriptPath || '/scripts/bench.sh';
|
||||
const backendBase = (cfg.backendApiUrl || `${window.location.protocol}//${window.location.hostname}:8007/api`).replace(/\/$/, '');
|
||||
const token = apiToken || 'LOADING...';
|
||||
const iperf = iperfServer || '10.0.1.97';
|
||||
|
||||
// Extract backend URL without /api suffix
|
||||
const backendUrl = backendBase.replace(/\/api$/, '');
|
||||
|
||||
return `curl -fsSL ${frontendBase}${scriptPath} | sudo bash -s -- --server ${backendUrl} --token "${token}" --iperf-server ${iperf}`;
|
||||
}
|
||||
|
||||
function updateBenchCommandDisplay() {
|
||||
const element = document.getElementById('benchCommand');
|
||||
if (!element) return;
|
||||
element.textContent = buildBenchCommand();
|
||||
}
|
||||
|
||||
// Copy bench command to clipboard
|
||||
async function copyBenchCommand() {
|
||||
const command = document.getElementById('benchCommand').textContent;
|
||||
@@ -282,7 +319,11 @@ function refreshDashboard() {
|
||||
}
|
||||
|
||||
// Initialize dashboard on page load
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
// Load backend config first to get API token
|
||||
await loadBackendConfig();
|
||||
|
||||
// Then load dashboard data
|
||||
loadDashboard();
|
||||
|
||||
// Setup search input listener
|
||||
|
||||
152
frontend/js/device_detail.js
Normal file → Executable file
152
frontend/js/device_detail.js
Normal file → Executable file
@@ -1,6 +1,6 @@
|
||||
// Linux BenchTools - Device Detail Logic
|
||||
|
||||
const { formatDate, formatRelativeTime, formatFileSize, createScoreBadge, getScoreBadgeText, escapeHtml, showError, showEmptyState, formatTags, initTabs, openModal, showToast, formatHardwareInfo } = window.BenchUtils;
|
||||
const { formatDate, formatRelativeTime, formatFileSize, createScoreBadge, getScoreBadgeText, escapeHtml, showError, showEmptyState, formatTags, initTabs, openModal, showToast, formatHardwareInfo, formatDuration, formatStorage } = window.BenchUtils;
|
||||
const api = window.BenchAPI;
|
||||
|
||||
let currentDeviceId = null;
|
||||
@@ -46,6 +46,11 @@ async function loadDeviceDetail() {
|
||||
await loadDocuments();
|
||||
await loadLinks();
|
||||
|
||||
const deleteBtn = document.getElementById('deleteDeviceBtn');
|
||||
if (deleteBtn) {
|
||||
deleteBtn.addEventListener('click', handleDeleteDevice);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to load device:', error);
|
||||
document.getElementById('loadingState').innerHTML =
|
||||
@@ -300,7 +305,7 @@ function renderStorageDetails() {
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 0.75rem; font-size: 0.9rem;">
|
||||
${disk.capacity_gb ? `
|
||||
<div>
|
||||
<strong>Capacité:</strong> ${disk.capacity_gb} GB
|
||||
<strong>Capacité:</strong> ${formatStorage(disk.capacity_gb)}
|
||||
</div>
|
||||
` : ''}
|
||||
${disk.type ? `
|
||||
@@ -331,7 +336,114 @@ function renderStorageDetails() {
|
||||
console.error('Failed to parse storage devices:', e);
|
||||
html = '<p style="color: var(--text-danger);">Erreur lors du parsing des données de stockage</p>';
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
// Parse partitions
|
||||
if (snapshot.partitions_json) {
|
||||
try {
|
||||
const partitions = typeof snapshot.partitions_json === 'string'
|
||||
? JSON.parse(snapshot.partitions_json)
|
||||
: snapshot.partitions_json;
|
||||
|
||||
if (Array.isArray(partitions) && partitions.length > 0) {
|
||||
html += `
|
||||
<div style="margin-top: 1.5rem;">
|
||||
<h4 style="margin-bottom: 0.75rem; color: var(--text-secondary);">Partitions et volumes</h4>
|
||||
<div style="overflow-x: auto;">
|
||||
<table style="width: 100%; border-collapse: collapse;">
|
||||
<thead>
|
||||
<tr style="text-align: left; border-bottom: 1px solid var(--border-color); color: var(--text-secondary);">
|
||||
<th style="padding: 0.5rem;">Partition</th>
|
||||
<th style="padding: 0.5rem;">Montage</th>
|
||||
<th style="padding: 0.5rem;">Type</th>
|
||||
<th style="padding: 0.5rem;">Utilisé</th>
|
||||
<th style="padding: 0.5rem;">Libre</th>
|
||||
<th style="padding: 0.5rem;">Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${partitions.map(part => {
|
||||
const used = typeof part.used_gb === 'number' ? formatStorage(part.used_gb) : 'N/A';
|
||||
const free = typeof part.free_gb === 'number'
|
||||
? formatStorage(part.free_gb)
|
||||
: (typeof part.total_gb === 'number' && typeof part.used_gb === 'number'
|
||||
? formatStorage(part.total_gb - part.used_gb)
|
||||
: 'N/A');
|
||||
const total = typeof part.total_gb === 'number' ? formatStorage(part.total_gb) : 'N/A';
|
||||
|
||||
return `
|
||||
<tr style="border-bottom: 1px solid var(--border-color);">
|
||||
<td style="padding: 0.5rem; font-weight: 600;">${escapeHtml(part.name || 'N/A')}</td>
|
||||
<td style="padding: 0.5rem;">${part.mount_point ? escapeHtml(part.mount_point) : '<span style="color: var(--text-muted);">Non monté</span>'}</td>
|
||||
<td style="padding: 0.5rem;">${part.fs_type ? escapeHtml(part.fs_type) : 'N/A'}</td>
|
||||
<td style="padding: 0.5rem;">${used}</td>
|
||||
<td style="padding: 0.5rem;">${free}</td>
|
||||
<td style="padding: 0.5rem;">${total}</td>
|
||||
</tr>
|
||||
`;
|
||||
}).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to parse partitions:', error);
|
||||
html += '<p style="color: var(--color-danger); margin-top: 1rem;">Erreur lors de la lecture des partitions</p>';
|
||||
}
|
||||
}
|
||||
|
||||
if (snapshot.network_shares_json) {
|
||||
try {
|
||||
const shares = typeof snapshot.network_shares_json === 'string'
|
||||
? JSON.parse(snapshot.network_shares_json)
|
||||
: snapshot.network_shares_json;
|
||||
|
||||
if (Array.isArray(shares) && shares.length > 0) {
|
||||
html += `
|
||||
<div style="margin-top: 1.5rem;">
|
||||
<h4 style="margin-bottom: 0.75rem; color: var(--text-secondary);">Partages réseau montés</h4>
|
||||
<div style="overflow-x: auto;">
|
||||
<table style="width: 100%; border-collapse: collapse;">
|
||||
<thead>
|
||||
<tr style="text-align: left; border-bottom: 1px solid var(--border-color); color: var(--text-secondary);">
|
||||
<th style="padding: 0.5rem;">Source</th>
|
||||
<th style="padding: 0.5rem;">Montage</th>
|
||||
<th style="padding: 0.5rem;">Protocole</th>
|
||||
<th style="padding: 0.5rem;">Type</th>
|
||||
<th style="padding: 0.5rem;">Utilisé</th>
|
||||
<th style="padding: 0.5rem;">Libre</th>
|
||||
<th style="padding: 0.5rem;">Total</th>
|
||||
<th style="padding: 0.5rem;">Options</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${shares.map(share => `
|
||||
<tr style="border-bottom: 1px solid var(--border-color);">
|
||||
<td style="padding: 0.5rem;">${escapeHtml(share.source || 'N/A')}</td>
|
||||
<td style="padding: 0.5rem;">${escapeHtml(share.mount_point || 'N/A')}</td>
|
||||
<td style="padding: 0.5rem;">${escapeHtml(share.protocol || share.fs_type || 'N/A')}</td>
|
||||
<td style="padding: 0.5rem;">${share.fs_type ? escapeHtml(share.fs_type) : 'N/A'}</td>
|
||||
<td style="padding: 0.5rem;">${typeof share.used_gb === 'number' ? formatStorage(share.used_gb) : 'N/A'}</td>
|
||||
<td style="padding: 0.5rem;">${typeof share.free_gb === 'number' ? formatStorage(share.free_gb) : 'N/A'}</td>
|
||||
<td style="padding: 0.5rem;">${typeof share.total_gb === 'number' ? formatStorage(share.total_gb) : 'N/A'}</td>
|
||||
<td style="padding: 0.5rem; max-width: 200px;">${share.options ? escapeHtml(share.options) : '<span style="color: var(--text-muted);">N/A</span>'}</td>
|
||||
</tr>
|
||||
`).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to parse network shares:', error);
|
||||
html += '<p style="color: var(--color-danger); margin-top: 1rem;">Erreur lors de la lecture des partages réseau</p>';
|
||||
}
|
||||
}
|
||||
|
||||
if (!html) {
|
||||
html = '<p style="color: var(--text-muted);">Aucune information de stockage disponible</p>';
|
||||
}
|
||||
|
||||
@@ -407,11 +519,25 @@ function renderOSDetails() {
|
||||
return;
|
||||
}
|
||||
|
||||
const displayServer = snapshot.display_server || snapshot.session_type || 'N/A';
|
||||
const resolution = snapshot.screen_resolution || 'N/A';
|
||||
const lastBoot = snapshot.last_boot_time || 'N/A';
|
||||
const uptime = snapshot.uptime_seconds != null ? formatDuration(snapshot.uptime_seconds) : 'N/A';
|
||||
const battery = snapshot.battery_percentage != null
|
||||
? `${snapshot.battery_percentage}%${snapshot.battery_status ? ` (${snapshot.battery_status})` : ''}`
|
||||
: 'N/A';
|
||||
|
||||
const items = [
|
||||
{ label: 'Nom', value: snapshot.os_name || 'N/A' },
|
||||
{ label: 'Version', value: snapshot.os_version || 'N/A' },
|
||||
{ label: 'Kernel', value: snapshot.kernel_version || 'N/A' },
|
||||
{ label: 'Architecture', value: snapshot.architecture || 'N/A' },
|
||||
{ label: 'Environnement', value: snapshot.desktop_environment || 'N/A' },
|
||||
{ label: 'Session', value: displayServer },
|
||||
{ label: 'Résolution écran', value: resolution },
|
||||
{ label: 'Dernier boot', value: lastBoot },
|
||||
{ label: 'Uptime', value: uptime },
|
||||
{ label: 'Batterie', value: battery },
|
||||
{ label: 'Virtualisation', value: snapshot.virtualization_type || 'none' }
|
||||
];
|
||||
|
||||
@@ -427,6 +553,26 @@ function renderOSDetails() {
|
||||
`;
|
||||
}
|
||||
|
||||
async function handleDeleteDevice() {
|
||||
if (!currentDevice) return;
|
||||
|
||||
const confirmed = confirm(`Voulez-vous vraiment supprimer le device "${currentDevice.hostname}" ? Cette action est définitive.`);
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await api.deleteDevice(currentDevice.id);
|
||||
showToast('Device supprimé avec succès', 'success');
|
||||
setTimeout(() => {
|
||||
window.location.href = 'devices.html';
|
||||
}, 800);
|
||||
} catch (error) {
|
||||
console.error('Failed to delete device:', error);
|
||||
showToast(error.message || 'Impossible de supprimer le device', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Render Benchmark Results
|
||||
function renderBenchmarkResults() {
|
||||
const bench = currentDevice.last_benchmark;
|
||||
|
||||
1812
frontend/js/devices.js
Normal file → Executable file
1812
frontend/js/devices.js
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
104
frontend/js/settings.js
Normal file → Executable file
104
frontend/js/settings.js
Normal file → Executable file
@@ -3,14 +3,103 @@
|
||||
const { copyToClipboard, showToast, escapeHtml } = window.BenchUtils;
|
||||
|
||||
let tokenVisible = false;
|
||||
const API_TOKEN = 'YOUR_API_TOKEN_HERE'; // Will be replaced by actual token or fetched from backend
|
||||
let API_TOKEN = 'LOADING...';
|
||||
|
||||
// Load backend config (API token, etc.)
|
||||
async function loadBackendConfig() {
|
||||
try {
|
||||
const backendApiUrl = window.BenchConfig?.backendApiUrl || `${window.location.protocol}//${window.location.hostname}:8007/api`;
|
||||
const response = await fetch(`${backendApiUrl}/config`);
|
||||
if (response.ok) {
|
||||
const config = await response.json();
|
||||
API_TOKEN = config.api_token;
|
||||
document.getElementById('apiToken').value = API_TOKEN;
|
||||
generateBenchCommand();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load backend config:', error);
|
||||
API_TOKEN = 'ERROR_LOADING_TOKEN';
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize settings page
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
loadDisplayPreferences();
|
||||
loadSettings();
|
||||
generateBenchCommand();
|
||||
await loadBackendConfig();
|
||||
});
|
||||
|
||||
// Load display preferences
|
||||
function loadDisplayPreferences() {
|
||||
const memoryUnit = localStorage.getItem('displayPref_memoryUnit') || 'GB';
|
||||
const storageUnit = localStorage.getItem('displayPref_storageUnit') || 'GB';
|
||||
const cacheUnit = localStorage.getItem('displayPref_cacheUnit') || 'KB';
|
||||
const temperatureUnit = localStorage.getItem('displayPref_temperatureUnit') || 'C';
|
||||
const sectionIconSize = localStorage.getItem('displayPref_sectionIconSize') || '32';
|
||||
const buttonIconSize = localStorage.getItem('displayPref_buttonIconSize') || '24';
|
||||
|
||||
document.getElementById('memoryUnit').value = memoryUnit;
|
||||
document.getElementById('storageUnit').value = storageUnit;
|
||||
document.getElementById('cacheUnit').value = cacheUnit;
|
||||
document.getElementById('temperatureUnit').value = temperatureUnit;
|
||||
document.getElementById('sectionIconSize').value = sectionIconSize;
|
||||
document.getElementById('buttonIconSize').value = buttonIconSize;
|
||||
|
||||
// Apply icon sizes
|
||||
applyIconSizes(sectionIconSize, buttonIconSize);
|
||||
}
|
||||
|
||||
// Apply icon sizes dynamically
|
||||
function applyIconSizes(sectionIconSize, buttonIconSize) {
|
||||
document.documentElement.style.setProperty('--section-icon-size', `${sectionIconSize}px`);
|
||||
document.documentElement.style.setProperty('--button-icon-size', `${buttonIconSize}px`);
|
||||
}
|
||||
|
||||
// Save display preferences
|
||||
function saveDisplayPreferences() {
|
||||
const memoryUnit = document.getElementById('memoryUnit').value;
|
||||
const storageUnit = document.getElementById('storageUnit').value;
|
||||
const cacheUnit = document.getElementById('cacheUnit').value;
|
||||
const temperatureUnit = document.getElementById('temperatureUnit').value;
|
||||
const sectionIconSize = document.getElementById('sectionIconSize').value;
|
||||
const buttonIconSize = document.getElementById('buttonIconSize').value;
|
||||
|
||||
localStorage.setItem('displayPref_memoryUnit', memoryUnit);
|
||||
localStorage.setItem('displayPref_storageUnit', storageUnit);
|
||||
localStorage.setItem('displayPref_cacheUnit', cacheUnit);
|
||||
localStorage.setItem('displayPref_temperatureUnit', temperatureUnit);
|
||||
localStorage.setItem('displayPref_sectionIconSize', sectionIconSize);
|
||||
localStorage.setItem('displayPref_buttonIconSize', buttonIconSize);
|
||||
|
||||
// Apply icon sizes immediately
|
||||
applyIconSizes(sectionIconSize, buttonIconSize);
|
||||
|
||||
showToast('Préférences enregistrées ! Rechargez la page pour voir les changements.', 'success');
|
||||
}
|
||||
|
||||
// Reset display preferences to defaults
|
||||
function resetDisplayPreferences() {
|
||||
localStorage.removeItem('displayPref_memoryUnit');
|
||||
localStorage.removeItem('displayPref_storageUnit');
|
||||
localStorage.removeItem('displayPref_cacheUnit');
|
||||
localStorage.removeItem('displayPref_temperatureUnit');
|
||||
localStorage.removeItem('displayPref_sectionIconSize');
|
||||
localStorage.removeItem('displayPref_buttonIconSize');
|
||||
|
||||
// Reset form to defaults
|
||||
document.getElementById('memoryUnit').value = 'GB';
|
||||
document.getElementById('storageUnit').value = 'GB';
|
||||
document.getElementById('cacheUnit').value = 'KB';
|
||||
document.getElementById('temperatureUnit').value = 'C';
|
||||
document.getElementById('sectionIconSize').value = '32';
|
||||
document.getElementById('buttonIconSize').value = '24';
|
||||
|
||||
// Apply default icon sizes
|
||||
applyIconSizes('32', '24');
|
||||
|
||||
showToast('Préférences réinitialisées aux valeurs par défaut', 'success');
|
||||
}
|
||||
|
||||
// Load settings
|
||||
function loadSettings() {
|
||||
// In a real scenario, these would be fetched from backend or localStorage
|
||||
@@ -22,8 +111,7 @@ function loadSettings() {
|
||||
document.getElementById('iperfServer').value = savedIperfServer;
|
||||
document.getElementById('benchMode').value = savedBenchMode;
|
||||
|
||||
// Set API token (in production, this should be fetched securely)
|
||||
document.getElementById('apiToken').value = API_TOKEN;
|
||||
// API token will be loaded asynchronously from backend
|
||||
|
||||
// Add event listeners for auto-generation
|
||||
document.getElementById('backendUrl').addEventListener('input', () => {
|
||||
@@ -74,8 +162,8 @@ function generateBenchCommand() {
|
||||
const scriptUrl = `${backendUrl.replace(':8007', ':8087')}/scripts/bench.sh`;
|
||||
|
||||
// Build command parts
|
||||
let command = `curl -s ${scriptUrl} | bash -s -- \\
|
||||
--server ${backendUrl}/api/benchmark \\
|
||||
let command = `curl -s ${scriptUrl} | sudo bash -s -- \\
|
||||
--server ${backendUrl} \\
|
||||
--token "${API_TOKEN}"`;
|
||||
|
||||
if (iperfServer) {
|
||||
@@ -143,3 +231,5 @@ window.generateBenchCommand = generateBenchCommand;
|
||||
window.copyGeneratedCommand = copyGeneratedCommand;
|
||||
window.toggleTokenVisibility = toggleTokenVisibility;
|
||||
window.copyToken = copyToken;
|
||||
window.saveDisplayPreferences = saveDisplayPreferences;
|
||||
window.resetDisplayPreferences = resetDisplayPreferences;
|
||||
|
||||
127
frontend/js/utils.js
Normal file → Executable file
127
frontend/js/utils.js
Normal file → Executable file
@@ -40,6 +40,26 @@ function formatFileSize(bytes) {
|
||||
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
// Format duration (seconds) into human readable form
|
||||
function formatDuration(seconds) {
|
||||
if (seconds === null || seconds === undefined) return 'N/A';
|
||||
const totalSeconds = Number(seconds);
|
||||
if (!Number.isFinite(totalSeconds) || totalSeconds < 0) return 'N/A';
|
||||
|
||||
const days = Math.floor(totalSeconds / 86400);
|
||||
const hours = Math.floor((totalSeconds % 86400) / 3600);
|
||||
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
||||
const secs = Math.floor(totalSeconds % 60);
|
||||
|
||||
const parts = [];
|
||||
if (days > 0) parts.push(`${days} j`);
|
||||
if (hours > 0) parts.push(`${hours} h`);
|
||||
if (minutes > 0) parts.push(`${minutes} min`);
|
||||
if (parts.length === 0) parts.push(`${secs} s`);
|
||||
|
||||
return parts.join(' ');
|
||||
}
|
||||
|
||||
// Get score badge class based on value
|
||||
function getScoreBadgeClass(score) {
|
||||
if (score === null || score === undefined) return 'score-badge';
|
||||
@@ -51,7 +71,9 @@ function getScoreBadgeClass(score) {
|
||||
// Get score badge text
|
||||
function getScoreBadgeText(score) {
|
||||
if (score === null || score === undefined) return '--';
|
||||
return Math.round(score);
|
||||
const numeric = Number(score);
|
||||
if (!Number.isFinite(numeric)) return '--';
|
||||
return Math.ceil(numeric);
|
||||
}
|
||||
|
||||
// Create score badge HTML
|
||||
@@ -299,8 +321,21 @@ function closeModal(modalId) {
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize modal close buttons
|
||||
// Apply icon size preferences from localStorage
|
||||
function applyIconSizePreferences() {
|
||||
const sectionIconSize = localStorage.getItem('displayPref_sectionIconSize') || '32';
|
||||
const buttonIconSize = localStorage.getItem('displayPref_buttonIconSize') || '24';
|
||||
|
||||
document.documentElement.style.setProperty('--section-icon-size', `${sectionIconSize}px`);
|
||||
document.documentElement.style.setProperty('--button-icon-size', `${buttonIconSize}px`);
|
||||
}
|
||||
|
||||
// Initialize modal close buttons and apply icon preferences
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Apply icon size preferences on all pages
|
||||
applyIconSizePreferences();
|
||||
|
||||
// Initialize modal close buttons
|
||||
document.querySelectorAll('.modal').forEach(modal => {
|
||||
modal.addEventListener('click', (e) => {
|
||||
if (e.target === modal) {
|
||||
@@ -318,10 +353,90 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
|
||||
// Export functions for use in other files
|
||||
// Unit conversion functions
|
||||
function getDisplayPreferences() {
|
||||
return {
|
||||
memoryUnit: localStorage.getItem('displayPref_memoryUnit') || 'GB',
|
||||
storageUnit: localStorage.getItem('displayPref_storageUnit') || 'GB',
|
||||
cacheUnit: localStorage.getItem('displayPref_cacheUnit') || 'KB',
|
||||
temperatureUnit: localStorage.getItem('displayPref_temperatureUnit') || 'C'
|
||||
};
|
||||
}
|
||||
|
||||
// Convert memory from MB to preferred unit
|
||||
function formatMemory(mb, forceUnit = null) {
|
||||
if (!mb || mb === 0) return '0 MB';
|
||||
|
||||
const prefs = getDisplayPreferences();
|
||||
const unit = forceUnit || prefs.memoryUnit;
|
||||
|
||||
if (unit === 'GB') {
|
||||
return (mb / 1024).toFixed(2) + ' GB';
|
||||
}
|
||||
return mb.toFixed(0) + ' MB';
|
||||
}
|
||||
|
||||
// Convert storage from GB to preferred unit
|
||||
function formatStorage(gb, forceUnit = null) {
|
||||
if (!gb || gb === 0) return '0 GB';
|
||||
|
||||
const prefs = getDisplayPreferences();
|
||||
const unit = forceUnit || prefs.storageUnit;
|
||||
|
||||
if (unit === 'TB') {
|
||||
return (gb / 1024).toFixed(2) + ' TB';
|
||||
} else if (unit === 'MB') {
|
||||
return (gb * 1024).toFixed(0) + ' MB';
|
||||
}
|
||||
return gb.toFixed(2) + ' GB';
|
||||
}
|
||||
|
||||
// Convert cache from KB to preferred unit
|
||||
function formatCache(kb, forceUnit = null) {
|
||||
if (!kb || kb === 0) return '0 KB';
|
||||
|
||||
const prefs = getDisplayPreferences();
|
||||
const unit = forceUnit || prefs.cacheUnit;
|
||||
|
||||
if (unit === 'MB') {
|
||||
return (kb / 1024).toFixed(2) + ' MB';
|
||||
}
|
||||
return kb.toFixed(0) + ' KB';
|
||||
}
|
||||
|
||||
// Convert temperature to preferred unit
|
||||
function formatTemperature(celsius, forceUnit = null) {
|
||||
if (celsius === null || celsius === undefined) return 'N/A';
|
||||
|
||||
const prefs = getDisplayPreferences();
|
||||
const unit = forceUnit || prefs.temperatureUnit;
|
||||
|
||||
if (unit === 'F') {
|
||||
const fahrenheit = (celsius * 9/5) + 32;
|
||||
return fahrenheit.toFixed(1) + '°F';
|
||||
}
|
||||
return celsius.toFixed(1) + '°C';
|
||||
}
|
||||
|
||||
function renderMarkdown(text) {
|
||||
if (!text || !text.trim()) {
|
||||
return '<div class="markdown-block" style="color: var(--text-secondary);">Aucune note disponible</div>';
|
||||
}
|
||||
|
||||
let html = escapeHtml(text);
|
||||
html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
||||
html = html.replace(/\*(.+?)\*/g, '<em>$1</em>');
|
||||
html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
|
||||
html = html.replace(/\n/g, '<br>');
|
||||
|
||||
return `<div class="markdown-block">${html}</div>`;
|
||||
}
|
||||
|
||||
window.BenchUtils = {
|
||||
formatDate,
|
||||
formatRelativeTime,
|
||||
formatFileSize,
|
||||
formatDuration,
|
||||
getScoreBadgeClass,
|
||||
getScoreBadgeText,
|
||||
createScoreBadge,
|
||||
@@ -340,5 +455,11 @@ window.BenchUtils = {
|
||||
formatHardwareInfo,
|
||||
initTabs,
|
||||
openModal,
|
||||
closeModal
|
||||
closeModal,
|
||||
getDisplayPreferences,
|
||||
formatMemory,
|
||||
formatStorage,
|
||||
formatCache,
|
||||
formatTemperature,
|
||||
renderMarkdown
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user