// Linux BenchTools - Device Detail Logic const { formatDate, formatRelativeTime, formatFileSize, createScoreBadge, getScoreBadgeText, escapeHtml, showError, showEmptyState, formatTags, initTabs, openModal, showToast, formatHardwareInfo } = window.BenchUtils; const api = window.BenchAPI; let currentDeviceId = null; let currentDevice = null; // Initialize page document.addEventListener('DOMContentLoaded', async () => { // Get device ID from URL currentDeviceId = window.BenchUtils.getUrlParameter('id'); if (!currentDeviceId) { document.getElementById('loadingState').innerHTML = '
Device ID manquant dans l\'URL
'; return; } // Initialize tabs initTabs('.tabs-container'); // Load device data await loadDeviceDetail(); }); // Load device detail async function loadDeviceDetail() { try { currentDevice = await api.getDevice(currentDeviceId); // Show content, hide loading document.getElementById('loadingState').style.display = 'none'; document.getElementById('deviceContent').style.display = 'block'; // Render all sections renderDeviceHeader(); renderMotherboardDetails(); renderCPUDetails(); renderMemoryDetails(); renderStorageDetails(); renderGPUDetails(); renderNetworkDetails(); renderOSDetails(); renderBenchmarkResults(); await loadBenchmarkHistory(); await loadDocuments(); await loadLinks(); } catch (error) { console.error('Failed to load device:', error); document.getElementById('loadingState').innerHTML = `
Erreur lors du chargement du device: ${escapeHtml(error.message)}
`; } } // Render device header function renderDeviceHeader() { document.getElementById('deviceHostname').textContent = currentDevice.hostname; document.getElementById('deviceDescription').textContent = currentDevice.description || 'Aucune description'; // Global score const globalScore = currentDevice.last_benchmark?.global_score; document.getElementById('globalScoreContainer').innerHTML = globalScore !== null && globalScore !== undefined ? `
${getScoreBadgeText(globalScore)}
` : 'N/A'; // Meta information const metaParts = []; if (currentDevice.location) metaParts.push(`📍 ${escapeHtml(currentDevice.location)}`); if (currentDevice.owner) metaParts.push(`👤 ${escapeHtml(currentDevice.owner)}`); if (currentDevice.asset_tag) metaParts.push(`🏷️ ${escapeHtml(currentDevice.asset_tag)}`); if (currentDevice.last_benchmark?.run_at) metaParts.push(`⏱️ ${formatRelativeTime(currentDevice.last_benchmark.run_at)}`); document.getElementById('deviceMeta').innerHTML = metaParts.map(part => `${part}` ).join(''); // Tags if (currentDevice.tags) { document.getElementById('deviceTags').innerHTML = formatTags(currentDevice.tags); } } // Render Motherboard Details function renderMotherboardDetails() { const snapshot = currentDevice.last_hardware_snapshot; const container = document.getElementById('motherboardDetails'); if (!snapshot) { container.innerHTML = '

Aucune information disponible

'; return; } // Helper to clean empty/whitespace-only strings const cleanValue = (val) => { if (!val || (typeof val === 'string' && val.trim() === '')) return 'N/A'; return val; }; const items = [ { label: 'Fabricant', value: cleanValue(snapshot.motherboard_vendor) }, { label: 'Modèle', value: cleanValue(snapshot.motherboard_model) }, { label: 'Version BIOS', value: cleanValue(snapshot.bios_version) }, { label: 'Date BIOS', value: cleanValue(snapshot.bios_date) }, { label: 'Slots RAM', value: `${snapshot.ram_slots_used || '?'} utilisés / ${snapshot.ram_slots_total || '?'} total` } ]; container.innerHTML = `
${items.map(item => `
${item.label}
${escapeHtml(String(item.value))}
`).join('')}
`; } // Render CPU Details function renderCPUDetails() { const snapshot = currentDevice.last_hardware_snapshot; const container = document.getElementById('cpuDetails'); if (!snapshot) { container.innerHTML = '

Aucune information disponible

'; return; } const items = [ { label: 'Fabricant', value: snapshot.cpu_vendor || 'N/A' }, { label: 'Modèle', value: snapshot.cpu_model || 'N/A' }, { label: 'Microarchitecture', value: snapshot.cpu_microarchitecture || 'N/A' }, { label: 'Cores', value: snapshot.cpu_cores != null ? snapshot.cpu_cores : 'N/A' }, { label: 'Threads', value: snapshot.cpu_threads != null ? snapshot.cpu_threads : 'N/A' }, { label: 'Fréquence de base', value: snapshot.cpu_base_freq_ghz ? `${snapshot.cpu_base_freq_ghz} GHz` : 'N/A' }, { label: 'Fréquence max', value: snapshot.cpu_max_freq_ghz ? `${snapshot.cpu_max_freq_ghz} GHz` : 'N/A' }, { label: 'TDP', value: snapshot.cpu_tdp_w ? `${snapshot.cpu_tdp_w} W` : 'N/A' }, { label: 'Cache L1', value: snapshot.cpu_cache_l1_kb ? `${snapshot.cpu_cache_l1_kb} KB` : 'N/A' }, { label: 'Cache L2', value: snapshot.cpu_cache_l2_kb ? `${snapshot.cpu_cache_l2_kb} KB` : 'N/A' }, { label: 'Cache L3', value: snapshot.cpu_cache_l3_kb ? `${snapshot.cpu_cache_l3_kb} KB` : 'N/A' } ]; let html = `
${items.map(item => `
${item.label}
${escapeHtml(String(item.value))}
`).join('')}
`; // CPU Flags if (snapshot.cpu_flags) { try { const flags = typeof snapshot.cpu_flags === 'string' ? JSON.parse(snapshot.cpu_flags) : snapshot.cpu_flags; if (Array.isArray(flags) && flags.length > 0) { html += `
Instructions supportées :
${flags.slice(0, 50).map(flag => `${escapeHtml(flag)}`).join('')} ${flags.length > 50 ? `+${flags.length - 50} autres...` : ''}
`; } } catch (e) { console.warn('Failed to parse CPU flags:', e); } } container.innerHTML = html; } // Render Memory Details function renderMemoryDetails() { const snapshot = currentDevice.last_hardware_snapshot; const container = document.getElementById('memoryDetails'); if (!snapshot) { container.innerHTML = '

Aucune information disponible

'; return; } const ramTotalGB = Math.round((snapshot.ram_total_mb || 0) / 1024); const ramUsedGB = snapshot.ram_used_mb != null ? Math.round(snapshot.ram_used_mb / 1024) : null; const ramFreeGB = snapshot.ram_free_mb != null ? Math.round(snapshot.ram_free_mb / 1024) : null; const ramSharedGB = snapshot.ram_shared_mb != null ? Math.round(snapshot.ram_shared_mb / 1024) : null; const usagePercent = (ramTotalGB > 0 && snapshot.ram_used_mb != null) ? Math.round((snapshot.ram_used_mb / snapshot.ram_total_mb) * 100) : null; const items = [ { label: 'Capacité totale', value: `${ramTotalGB} GB` }, { label: 'Mémoire utilisée', value: ramUsedGB != null ? `${ramUsedGB} GB${usagePercent != null ? ` (${usagePercent}%)` : ''}` : 'N/A' }, { label: 'Mémoire libre', value: ramFreeGB != null ? `${ramFreeGB} GB` : 'N/A' }, { label: 'Mémoire partagée', value: ramSharedGB != null ? `${ramSharedGB} GB` : 'N/A' }, { label: 'Slots utilisés', value: `${snapshot.ram_slots_used || '?'} / ${snapshot.ram_slots_total || '?'}` }, { label: 'ECC', value: snapshot.ram_ecc ? 'Oui' : 'Non' } ]; let html = `
${items.map(item => `
${item.label}
${escapeHtml(String(item.value))}
`).join('')}
`; // RAM Layout (DIMM details) if (snapshot.ram_layout_json) { try { const layout = typeof snapshot.ram_layout_json === 'string' ? JSON.parse(snapshot.ram_layout_json) : snapshot.ram_layout_json; if (Array.isArray(layout) && layout.length > 0) { html += `
Configuration des barrettes :
${layout.map(dimm => `
Slot ${escapeHtml(dimm.slot || 'N/A')}
${dimm.size_mb ? `${Math.round(dimm.size_mb / 1024)} GB` : 'N/A'} ${dimm.type ? `• ${escapeHtml(dimm.type)}` : ''} ${dimm.speed_mhz ? `• ${dimm.speed_mhz} MHz` : ''}
${dimm.vendor || dimm.manufacturer ? ` Fabricant ${escapeHtml(dimm.vendor || dimm.manufacturer)} ` : ''} ${dimm.part_number ? ` Part Number ${escapeHtml(dimm.part_number)} ` : ''}
`).join('')}
`; } } catch (e) { console.warn('Failed to parse RAM layout:', e); } } container.innerHTML = html; } // Render Storage Details function renderStorageDetails() { const snapshot = currentDevice.last_hardware_snapshot; const container = document.getElementById('storageDetails'); if (!snapshot) { container.innerHTML = '

Aucune information disponible

'; return; } let html = ''; // Parse storage devices if (snapshot.storage_devices_json) { try { const devices = typeof snapshot.storage_devices_json === 'string' ? JSON.parse(snapshot.storage_devices_json) : snapshot.storage_devices_json; if (Array.isArray(devices) && devices.length > 0) { html += '
'; devices.forEach(disk => { const typeIcon = disk.type === 'SSD' ? '💾' : '💿'; const healthColor = disk.smart_health === 'PASSED' ? 'var(--color-success)' : disk.smart_health === 'FAILED' ? 'var(--color-danger)' : 'var(--text-secondary)'; html += `
${typeIcon} ${escapeHtml(disk.name || disk.device || 'N/A')}
${escapeHtml(disk.model || 'Unknown model')}
${disk.smart_health ? ` ${escapeHtml(disk.smart_health)} ` : ''}
${disk.capacity_gb ? `
Capacité: ${disk.capacity_gb} GB
` : ''} ${disk.type ? `
Type: ${escapeHtml(disk.type)}
` : ''} ${disk.interface ? `
Interface: ${escapeHtml(disk.interface)}
` : ''} ${disk.temperature_c ? `
Température: ${disk.temperature_c}°C
` : ''}
`; }); html += '
'; } else { html = '

Aucun disque détecté

'; } } catch (e) { console.error('Failed to parse storage devices:', e); html = '

Erreur lors du parsing des données de stockage

'; } } else { html = '

Aucune information de stockage disponible

'; } container.innerHTML = html; } // Render GPU Details function renderGPUDetails() { const snapshot = currentDevice.last_hardware_snapshot; const container = document.getElementById('gpuDetails'); if (!snapshot) { container.innerHTML = '

Aucune information disponible

'; return; } if (!snapshot.gpu_vendor && !snapshot.gpu_model && !snapshot.gpu_summary) { container.innerHTML = '

Aucun GPU détecté

'; return; } const items = [ { label: 'Fabricant', value: snapshot.gpu_vendor || 'N/A' }, { label: 'Modèle', value: snapshot.gpu_model || snapshot.gpu_summary || 'N/A' }, { label: 'Driver', value: snapshot.gpu_driver_version || 'N/A' }, { label: 'Mémoire dédiée', value: snapshot.gpu_memory_dedicated_mb ? `${snapshot.gpu_memory_dedicated_mb} MB` : 'N/A' }, { label: 'Mémoire partagée', value: snapshot.gpu_memory_shared_mb ? `${snapshot.gpu_memory_shared_mb} MB` : 'N/A' } ]; let html = `
${items.map(item => `
${item.label}
${escapeHtml(String(item.value))}
`).join('')}
`; // API Support if (snapshot.gpu_api_support) { try { const apiSupport = typeof snapshot.gpu_api_support === 'string' ? JSON.parse(snapshot.gpu_api_support) : snapshot.gpu_api_support; if (Array.isArray(apiSupport) && apiSupport.length > 0) { html += `
APIs supportées :
${apiSupport.map(api => `${escapeHtml(api)}`).join('')}
`; } } catch (e) { console.warn('Failed to parse GPU API support:', e); } } container.innerHTML = html; } // Render OS Details function renderOSDetails() { const snapshot = currentDevice.last_hardware_snapshot; const container = document.getElementById('osDetails'); if (!snapshot) { container.innerHTML = '

Aucune information disponible

'; return; } 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: 'Virtualisation', value: snapshot.virtualization_type || 'none' } ]; container.innerHTML = `
${items.map(item => `
${item.label}
${escapeHtml(String(item.value))}
`).join('')}
`; } // Render Benchmark Results function renderBenchmarkResults() { const bench = currentDevice.last_benchmark; const container = document.getElementById('benchmarkResults'); if (!bench) { container.innerHTML = '

Aucun benchmark disponible

'; return; } container.innerHTML = `
Dernier benchmark: ${formatDate(bench.run_at)} Version: ${escapeHtml(bench.bench_script_version || 'N/A')}
${createScoreBadge(bench.global_score, 'Score Global')} ${createScoreBadge(bench.cpu_score, 'CPU')} ${createScoreBadge(bench.memory_score, 'Mémoire')} ${createScoreBadge(bench.disk_score, 'Disque')} ${createScoreBadge(bench.network_score, 'Réseau')} ${createScoreBadge(bench.gpu_score, 'GPU')}
`; } // Render Network Details function renderNetworkDetails() { const container = document.getElementById('networkDetails'); const snapshot = currentDevice.last_hardware_snapshot; if (!snapshot || !snapshot.network_interfaces_json) { container.innerHTML = '

Aucune information réseau disponible

'; return; } try { const interfaces = typeof snapshot.network_interfaces_json === 'string' ? JSON.parse(snapshot.network_interfaces_json) : snapshot.network_interfaces_json; if (!interfaces || interfaces.length === 0) { container.innerHTML = '

Aucune interface réseau détectée

'; return; } let html = '
'; // Interface details interfaces.forEach(iface => { const typeIcon = iface.type === 'ethernet' ? '🔌' : (iface.type === 'wifi' ? '📡' : '🌐'); const wol = iface.wake_on_lan; const wolBadge = wol === true ? 'WoL ✓' : (wol === false ? 'WoL ✗' : ''); html += `
${typeIcon} ${escapeHtml(iface.name)}
${escapeHtml(iface.type || 'unknown')}
${wolBadge}
${iface.ip ? `
Adresse IP:
${escapeHtml(iface.ip)}
` : ''} ${iface.mac ? `
MAC:
${escapeHtml(iface.mac)}
` : ''} ${iface.speed_mbps ? `
Vitesse:
${iface.speed_mbps} Mbps
` : ''} ${iface.driver ? `
Driver:
${escapeHtml(iface.driver)}
` : ''}
`; }); html += '
'; container.innerHTML = html; } catch (error) { console.error('Failed to parse network interfaces:', error); container.innerHTML = '

Erreur lors du parsing des données réseau

'; } } // Load benchmark history async function loadBenchmarkHistory() { const container = document.getElementById('benchmarkHistory'); try { const data = await api.getDeviceBenchmarks(currentDeviceId, { limit: 20 }); if (!data.items || data.items.length === 0) { showEmptyState(container, 'Aucun benchmark dans l\'historique', '📊'); return; } container.innerHTML = `
${data.items.map(bench => ` `).join('')}
Date Score Global CPU MEM DISK NET GPU Version Action
${formatDate(bench.run_at)} ${getScoreBadgeText(bench.global_score)} ${getScoreBadgeText(bench.cpu_score)} ${getScoreBadgeText(bench.memory_score)} ${getScoreBadgeText(bench.disk_score)} ${getScoreBadgeText(bench.network_score)} ${getScoreBadgeText(bench.gpu_score)} ${escapeHtml(bench.bench_script_version || 'N/A')}
`; } catch (error) { console.error('Failed to load benchmarks:', error); showError(container, 'Erreur lors du chargement de l\'historique'); } } // View benchmark details async function viewBenchmarkDetails(benchmarkId) { const modalBody = document.getElementById('benchmarkModalBody'); openModal('benchmarkModal'); try { const benchmark = await api.getBenchmark(benchmarkId); modalBody.innerHTML = `
${JSON.stringify(benchmark.details || benchmark, null, 2)}
`; } catch (error) { console.error('Failed to load benchmark details:', error); modalBody.innerHTML = `
Erreur: ${escapeHtml(error.message)}
`; } } // Load documents async function loadDocuments() { const container = document.getElementById('documentsList'); try { // Use documents from currentDevice (already loaded) const docs = currentDevice.documents || []; if (!docs || docs.length === 0) { showEmptyState(container, 'Aucun document uploadé', '📄'); return; } // Separate images from other documents const images = docs.filter(doc => doc.doc_type === 'image'); const otherDocs = docs.filter(doc => doc.doc_type !== 'image'); let html = ''; // Display images with preview if (images.length > 0) { html += '

🖼️ Images

'; html += '
'; images.forEach(doc => { const downloadUrl = api.getDocumentDownloadUrl(doc.id); html += `
${escapeHtml(doc.filename)}
📎 ${escapeHtml(doc.filename)}
${formatFileSize(doc.size_bytes)} • ${formatDate(doc.uploaded_at)}
⬇️ Télécharger
`; }); html += '
'; } // Display other documents (PDFs, manuals, etc.) if (otherDocs.length > 0) { html += '

📄 Autres Documents

'; html += ''; } container.innerHTML = html; } catch (error) { console.error('Failed to load documents:', error); showError(container, 'Erreur lors du chargement des documents'); } } // Get document icon function getDocIcon(docType) { const icons = { image: '🖼️', manual: '📘', warranty: '📜', invoice: '🧾', photo: '📷', other: '📄' }; return icons[docType] || '📄'; } // Upload document async function uploadDocument() { const fileInput = document.getElementById('fileInput'); const docTypeSelect = document.getElementById('docTypeSelect'); if (!fileInput.files || fileInput.files.length === 0) { showToast('Veuillez sélectionner un fichier', 'error'); return; } const file = fileInput.files[0]; const docType = docTypeSelect.value; try { await api.uploadDocument(currentDeviceId, file, docType); showToast('Document uploadé avec succès', 'success'); // Reset form fileInput.value = ''; docTypeSelect.value = 'manual'; // Reload device to get updated documents currentDevice = await api.getDevice(currentDeviceId); await loadDocuments(); } catch (error) { console.error('Failed to upload document:', error); showToast('Erreur lors de l\'upload: ' + error.message, 'error'); } } // Delete document async function deleteDocument(docId) { if (!confirm('Êtes-vous sûr de vouloir supprimer ce document ?')) { return; } try { await api.deleteDocument(docId); showToast('Document supprimé', 'success'); // Reload device to get updated documents currentDevice = await api.getDevice(currentDeviceId); await loadDocuments(); } catch (error) { console.error('Failed to delete document:', error); showToast('Erreur lors de la suppression: ' + error.message, 'error'); } } // Load links async function loadLinks() { const container = document.getElementById('linksList'); try { const links = await api.getDeviceLinks(currentDeviceId); if (!links || links.length === 0) { showEmptyState(container, 'Aucun lien ajouté', '🔗'); return; } container.innerHTML = ` `; } catch (error) { console.error('Failed to load links:', error); showError(container, 'Erreur lors du chargement des liens'); } } // Add link async function addLink() { const labelInput = document.getElementById('linkLabel'); const urlInput = document.getElementById('linkUrl'); const label = labelInput.value.trim(); const url = urlInput.value.trim(); if (!label || !url) { showToast('Veuillez remplir tous les champs', 'error'); return; } try { await api.addDeviceLink(currentDeviceId, { label, url }); showToast('Lien ajouté avec succès', 'success'); // Reset form labelInput.value = ''; urlInput.value = ''; // Reload links await loadLinks(); } catch (error) { console.error('Failed to add link:', error); showToast('Erreur lors de l\'ajout: ' + error.message, 'error'); } } // Delete link async function deleteLink(linkId) { if (!confirm('Êtes-vous sûr de vouloir supprimer ce lien ?')) { return; } try { await api.deleteLink(linkId); showToast('Lien supprimé', 'success'); await loadLinks(); } catch (error) { console.error('Failed to delete link:', error); showToast('Erreur lors de la suppression: ' + error.message, 'error'); } } // Make functions available globally window.viewBenchmarkDetails = viewBenchmarkDetails; window.uploadDocument = uploadDocument; window.deleteDocument = deleteDocument; window.addLink = addLink; window.deleteLink = deleteLink;