/** * Linux BenchTools - Peripherals Management */ // Global state let currentPage = 1; let pageSize = 10; // Items per page let totalPages = 1; let sortBy = 'date_creation'; let sortOrder = 'desc'; let filterType = ''; let filterLocation = ''; let filterEtat = ''; let searchQuery = ''; let peripheralTypes = []; let locations = []; let devices = []; let hosts = []; // Initialize document.addEventListener('DOMContentLoaded', () => { loadStatistics(); loadPeripheralTypes(); loadLocations(); loadStockageLocations(); loadHosts(); loadBoutiques(); loadDevices(); loadPeripherals(); initStarRating(); const utilisationSelect = document.getElementById('utilisation'); if (utilisationSelect) { utilisationSelect.addEventListener('change', updateUtilisationFields); updateUtilisationFields(); } const photoUrlInput = document.getElementById('photo-url-add'); if (photoUrlInput) { photoUrlInput.addEventListener('input', updatePhotoUrlAddUI); } const photoFileInput = document.getElementById('photo-file-add'); if (photoFileInput) { photoFileInput.addEventListener('change', onPhotoFileAddChange); } // Auto-refresh every 30 seconds setInterval(() => { loadStatistics(); loadPeripherals(); }, 30000); }); // Initialize star rating system function initStarRating() { const starContainer = document.querySelector('.star-rating'); if (!starContainer) return; const stars = starContainer.querySelectorAll('i[data-rating]'); const ratingInput = document.getElementById('rating'); // Click handler stars.forEach(star => { star.addEventListener('click', () => { const rating = parseInt(star.dataset.rating); ratingInput.value = rating; updateStarDisplay(rating); }); // Hover effect star.addEventListener('mouseenter', () => { const rating = parseInt(star.dataset.rating); updateStarDisplay(rating, true); }); }); // Reset to actual rating on mouse leave starContainer.addEventListener('mouseleave', () => { const currentRating = parseInt(ratingInput.value) || 0; updateStarDisplay(currentRating); }); } // Update star display function updateStarDisplay(rating, isHover = false) { const stars = document.querySelectorAll('.star-rating i[data-rating]'); stars.forEach(star => { const starRating = parseInt(star.dataset.rating); if (starRating <= rating) { star.classList.add('active'); } else { star.classList.remove('active'); } }); } // Set rating value (used when loading peripheral data) function setRating(value) { const ratingInput = document.getElementById('rating'); if (ratingInput) { ratingInput.value = value || 0; updateStarDisplay(parseInt(value) || 0); } } // Fill USB detailed information fields function fillUSBDetails(caracteristiques) { if (!caracteristiques) { // Hide USB details section if no data const section = document.getElementById('usb-details-section'); if (section) section.style.display = 'none'; return; } // Check if we have USB-specific fields const hasUSBData = caracteristiques.vendor_id || caracteristiques.product_id || caracteristiques.usb_type || caracteristiques.interface_classes; const section = document.getElementById('usb-details-section'); if (!hasUSBData) { if (section) section.style.display = 'none'; return; } // Show section and fill fields if (section) section.style.display = 'block'; // Helper function to set field value const setField = (id, value) => { const field = document.getElementById(id); if (field && value !== null && value !== undefined) { field.value = value; } }; // Fill all USB fields setField('usb_vendor_id', caracteristiques.vendor_id); setField('usb_product_id', caracteristiques.product_id); setField('usb_fabricant', caracteristiques.fabricant); setField('usb_type', caracteristiques.usb_type); setField('usb_version_declared', caracteristiques.usb_version_declared); setField('usb_negotiated_speed', caracteristiques.negotiated_speed); setField('usb_max_power', caracteristiques.max_power_ma ? `${caracteristiques.max_power_ma} mA` : ''); // Power mode let powerMode = ''; if (caracteristiques.is_bus_powered && caracteristiques.is_self_powered) { powerMode = 'Bus Powered + Self Powered'; } else if (caracteristiques.is_bus_powered) { powerMode = 'Bus Powered'; } else if (caracteristiques.is_self_powered) { powerMode = 'Self Powered'; } setField('usb_power_mode', powerMode); // Power sufficient (Yes/No with color indicator) const powerSufficient = caracteristiques.power_sufficient === true ? '✅ Oui' : caracteristiques.power_sufficient === false ? '⚠️ Non' : 'N/A'; setField('usb_power_sufficient', powerSufficient); // Firmware required const firmwareReq = caracteristiques.requires_firmware === true ? '⚠️ Oui (classe 255)' : caracteristiques.requires_firmware === false ? '✅ Non' : 'N/A'; setField('usb_requires_firmware', firmwareReq); // Device class const deviceClass = caracteristiques.device_class_nom ? `${caracteristiques.device_class} (${caracteristiques.device_class_nom})` : caracteristiques.device_class || 'N/A'; setField('usb_device_class', deviceClass); // Interface classes (normative) if (caracteristiques.interface_classes && caracteristiques.interface_classes.length > 0) { const interfaceClassesStr = caracteristiques.interface_classes .map(ic => `${ic.code} (${ic.name})`) .join(', '); setField('usb_interface_classes', interfaceClassesStr); } else { setField('usb_interface_classes', 'N/A'); } } // Load statistics async function loadStatistics() { try { const stats = await apiRequest('/peripherals/statistics/summary'); document.getElementById('stat-total').textContent = stats.total_peripherals || 0; document.getElementById('stat-disponible').textContent = stats.disponible || 0; document.getElementById('stat-pret').textContent = stats.en_pret || 0; document.getElementById('stat-low-stock').textContent = stats.low_stock_count || 0; } catch (error) { console.error('Error loading statistics:', error); } } // Load peripheral types from YAML config async function loadPeripheralTypes() { try { // For now, use hardcoded types (later can be loaded from API) peripheralTypes = [ 'USB', 'Bluetooth', 'Réseau', 'Stockage', 'Video', 'Audio', 'Câble', 'Quincaillerie', 'Console', 'Microcontrôleur' ]; const typeSelect = document.getElementById('filter-type'); const typeFormSelect = document.getElementById('type_principal'); peripheralTypes.forEach(type => { const option1 = document.createElement('option'); option1.value = type; option1.textContent = type; typeSelect.appendChild(option1); const option2 = document.createElement('option'); option2.value = type; option2.textContent = type; typeFormSelect.appendChild(option2); }); } catch (error) { console.error('Error loading peripheral types:', error); } } // Load locations async function loadLocations() { try { const response = await apiRequest('/locations'); locations = response; const locationSelects = [ document.getElementById('filter-location'), document.getElementById('location_id') ]; locationSelects.forEach(select => { if (!select) return; // Clear existing options except first while (select.options.length > 1) { select.remove(1); } locations.forEach(loc => { const option = document.createElement('option'); option.value = loc.id; option.textContent = loc.nom; select.appendChild(option); }); }); } catch (error) { console.error('Error loading locations:', error); } } // Load storage locations from YAML async function loadStockageLocations() { try { const result = await apiRequest('/peripherals/config/stockage-locations'); if (!result.success) return; const select = document.getElementById('stockage_location'); if (!select) return; // Clear existing options except first while (select.options.length > 1) { select.remove(1); } result.locations.forEach(name => { const option = document.createElement('option'); option.value = name; option.textContent = name; select.appendChild(option); }); } catch (error) { console.error('Error loading storage locations:', error); } } // Load hosts from YAML async function loadHosts() { try { const result = await apiRequest('/peripherals/config/hosts'); if (!result.success) return; hosts = result.hosts || []; const select = document.getElementById('connecte_a'); if (!select) return; // Clear existing options except first while (select.options.length > 1) { select.remove(1); } hosts.forEach(host => { const option = document.createElement('option'); option.value = host.nom; option.textContent = host.localisation ? `${host.nom} (${host.localisation})` : host.nom; select.appendChild(option); }); } catch (error) { console.error('Error loading hosts:', error); } } function updateUtilisationFields() { const utilisation = document.getElementById('utilisation')?.value || 'non_utilise'; const hostGroup = document.getElementById('group-connecte_a'); const locationGroup = document.getElementById('group-location_id'); const stockageGroup = document.getElementById('group-stockage_location'); if (utilisation === 'utilise') { if (hostGroup) hostGroup.style.display = 'block'; if (locationGroup) locationGroup.style.display = 'none'; if (stockageGroup) stockageGroup.style.display = 'none'; } else { if (hostGroup) hostGroup.style.display = 'none'; if (locationGroup) locationGroup.style.display = 'none'; if (stockageGroup) stockageGroup.style.display = 'block'; } } // Load devices (hosts) for dropdown async function loadDevices() { try { const response = await apiRequest('/peripherals/config/devices'); if (response.success) { devices = response.devices; const deviceSelect = document.getElementById('device_id'); if (!deviceSelect) return; // Clear existing options except first (En stock) while (deviceSelect.options.length > 1) { deviceSelect.remove(1); } // Add devices to dropdown devices.forEach(device => { const option = document.createElement('option'); option.value = device.id; // Display format: "hostname (location)" or just "hostname" if no location const displayText = device.location ? `${device.hostname} (${device.location})` : device.hostname; option.textContent = displayText; deviceSelect.appendChild(option); }); } } catch (error) { console.error('Error loading devices:', error); } } // Load boutiques async function loadBoutiques() { try { const result = await apiRequest('/peripherals/config/boutiques'); if (!result.success) return; const select = document.getElementById('boutique'); if (!select) return; // Clear existing options except first while (select.options.length > 1) { select.remove(1); } result.boutiques.forEach(name => { const option = document.createElement('option'); option.value = name; option.textContent = name; select.appendChild(option); }); } catch (error) { console.error('Error loading boutiques:', error); } } // Get device display text by ID (for showing assignment status) function getDeviceDisplayText(deviceId) { if (!deviceId) { return 'En stock'; } const device = devices.find(d => d.id === deviceId); if (!device) { return 'En stock'; } // Return "hostname + location" if location exists, otherwise just hostname return device.location ? `${device.hostname} → ${device.location}` : device.hostname; } // Load peripherals async function loadPeripherals() { try { const params = new URLSearchParams({ page: currentPage, page_size: pageSize, sort_by: sortBy, sort_order: sortOrder }); if (filterType) params.append('type_filter', filterType); if (filterLocation) params.append('location_id', filterLocation); if (filterEtat) params.append('etat', filterEtat); if (searchQuery) params.append('search', searchQuery); const data = await apiRequest(`/peripherals?${params}`); totalPages = data.total_pages; displayPeripherals(data.items); updatePagination(data); } catch (error) { console.error('Error loading peripherals:', error); showError('Erreur lors du chargement des périphériques'); } } // Display peripherals in table function displayPeripherals(peripherals) { const tbody = document.getElementById('peripherals-tbody'); if (peripherals.length === 0) { tbody.innerHTML = 'Aucun périphérique trouvé'; return; } tbody.innerHTML = peripherals.map(p => `
${p.thumbnail_url ? `${escapeHtml(p.nom)} ` : `` }
${escapeHtml(p.nom)} ${p.en_pret ? 'En prêt' : ''} ${p.is_complete_device ? 'Appareil complet' : ''} ${escapeHtml(p.type_principal)} ${escapeHtml(p.marque || '-')} ${escapeHtml(p.modele || '-')} ${escapeHtml(p.etat)} ${renderStars(p.rating)} ${p.quantite_disponible} ${p.quantite_disponible === 0 ? '' : ''} ${p.prix ? `${p.prix.toFixed(2)} €` : '-'} `).join(''); } // Update pagination function updatePagination(data) { document.getElementById('pagination-info').textContent = `Page ${data.page} / ${data.total_pages} (${data.total} périphériques)`; document.getElementById('btn-prev').disabled = data.page <= 1; document.getElementById('btn-next').disabled = data.page >= data.total_pages; } // Pagination functions function previousPage() { if (currentPage > 1) { currentPage--; loadPeripherals(); } } function nextPage() { if (currentPage < totalPages) { currentPage++; loadPeripherals(); } } // Sort table function sortTable(column) { if (sortBy === column) { sortOrder = sortOrder === 'asc' ? 'desc' : 'asc'; } else { sortBy = column; sortOrder = 'asc'; } loadPeripherals(); } // Filter and search function filterPeripherals() { filterType = document.getElementById('filter-type').value; filterLocation = document.getElementById('filter-location').value; filterEtat = document.getElementById('filter-etat').value; currentPage = 1; loadPeripherals(); } let searchTimeout; function searchPeripherals() { clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { searchQuery = document.getElementById('search').value; currentPage = 1; loadPeripherals(); }, 300); } // View peripheral details function viewPeripheral(id) { window.location.href = `peripheral-detail.html?id=${id}`; } // Show add modal function showAddModal() { document.getElementById('form-add-peripheral').reset(); document.getElementById('modal-add').style.display = 'block'; updateUtilisationFields(); updatePhotoUrlAddUI(); } // Add peripheral async function addPeripheral(event) { event.preventDefault(); const formData = new FormData(event.target); const data = {}; const photoFile = document.getElementById('photo-file-add')?.files?.[0] || null; const photoUrl = (document.getElementById('photo-url-add')?.value || '').trim(); const photoDescription = document.getElementById('photo-description-add')?.value || ''; const photoPrimary = document.getElementById('photo-primary-add')?.checked || false; for (let [key, value] of formData.entries()) { if (value === '') continue; // Convert numeric fields if (['photo_file', 'photo_url', 'photo_description', 'photo_primary'].includes(key)) { continue; } else if (key === 'utilisation') { continue; } else if (['prix', 'rating', 'quantite_totale', 'quantite_disponible', 'garantie_duree_mois'].includes(key)) { data[key] = parseFloat(value); } else if (['location_id', 'device_id'].includes(key)) { data[key] = parseInt(value); } else { data[key] = value; } } const utilisation = document.getElementById('utilisation')?.value || 'non_utilise'; if (utilisation === 'utilise') { const selectedHost = document.getElementById('connecte_a')?.value || null; data.connecte_a = selectedHost; data.location_id = null; const host = hosts.find(h => h.nom === selectedHost); if (host && host.localisation) { data.location_details = host.localisation; } } else { data.connecte_a = null; data.location_id = null; const stockage = document.getElementById('stockage_location')?.value || null; data.location_details = stockage; } try { const created = await apiRequest('/peripherals', { method: 'POST', body: JSON.stringify(data) }); if (created && (photoFile || photoUrl)) { if (photoFile) { const photoData = new FormData(); photoData.append('file', photoFile); photoData.append('description', photoDescription); photoData.append('is_primary', photoPrimary ? 'true' : 'false'); await apiRequest(`/peripherals/${created.id}/photos`, { method: 'POST', body: photoData }); } else if (/^https?:\/\//i.test(photoUrl)) { const urlData = new FormData(); urlData.append('image_url', photoUrl); urlData.append('description', photoDescription); urlData.append('is_primary', photoPrimary ? 'true' : 'false'); await apiRequest(`/peripherals/${created.id}/photos/from-url`, { method: 'POST', body: urlData }); } else if (photoUrl) { showError('URL invalide (http/https requis)'); } } closeModal('modal-add'); showSuccess('Périphérique ajouté avec succès'); loadPeripherals(); loadStatistics(); } catch (error) { console.error('Error adding peripheral:', error); showError('Erreur lors de l\'ajout du périphérique'); } } function updatePhotoUrlAddUI() { const url = (document.getElementById('photo-url-add')?.value || '').trim(); const button = document.getElementById('btn-photo-url-add'); const previewWrap = document.getElementById('photo-url-preview-wrap'); const previewImg = document.getElementById('photo-url-preview'); const isValid = /^https?:\/\//i.test(url); if (button) button.disabled = !isValid; if (!previewWrap || !previewImg) return; if (isValid) { previewImg.src = url; previewWrap.style.display = 'flex'; } else { previewImg.removeAttribute('src'); previewWrap.style.display = 'none'; } } function onPhotoFileAddChange() { const photoFile = document.getElementById('photo-file-add')?.files?.[0] || null; if (photoFile) { const urlInput = document.getElementById('photo-url-add'); if (urlInput) urlInput.value = ''; updatePhotoUrlAddUI(); } } function submitPhotoUrlAdd() { const url = (document.getElementById('photo-url-add')?.value || '').trim(); if (!/^https?:\/\//i.test(url)) { showError('URL invalide (http/https requis)'); return; } const form = document.getElementById('form-add-peripheral'); if (form) { form.requestSubmit(); } } // Edit peripheral async function editPeripheral(id) { // Redirect to detail page in edit mode window.location.href = `peripheral-detail.html?id=${id}&edit=true`; } // Delete peripheral async function deletePeripheral(id) { if (!confirm('Êtes-vous sûr de vouloir supprimer ce périphérique ?')) { return; } try { await apiRequest(`/peripherals/${id}`, { method: 'DELETE' }); showSuccess('Périphérique supprimé avec succès'); loadPeripherals(); loadStatistics(); } catch (error) { console.error('Error deleting peripheral:', error); showError('Erreur lors de la suppression du périphérique'); } } // Show import USB modal function showImportUSBModal() { // Reset form and steps document.getElementById('form-detect-usb').reset(); document.getElementById('usb-import-step-1').style.display = 'block'; document.getElementById('usb-import-step-2').style.display = 'none'; document.getElementById('modal-import-usb').style.display = 'block'; } // Show import USB structured modal function showImportUSBStructuredModal() { document.getElementById('form-import-usb-structured').reset(); document.getElementById('modal-import-usb-structured').style.display = 'block'; } // Copy USB command to clipboard function copyUSBCommand() { const command = document.getElementById('usb-command').textContent; const button = document.getElementById('btn-copy-usb'); const tooltip = button.querySelector('.tooltip-copied'); navigator.clipboard.writeText(command).then(() => { // Show tooltip tooltip.classList.add('show'); // Hide tooltip after 2 seconds setTimeout(() => { tooltip.classList.remove('show'); }, 2000); }).catch((error) => { console.error('Erreur lors de la copie:', error); showError('Erreur lors de la copie de la commande'); }); } // Import USB - Step 1: Detect devices let detectedUSBDevices = []; let fullUSBOutput = ''; async function detectUSBDevices(event) { event.preventDefault(); const formData = new FormData(event.target); fullUSBOutput = formData.get('lsusb_output'); try { const result = await apiRequest('/peripherals/import/usb-cli/detect', { method: 'POST', body: formData }); if (result.success && result.devices && result.devices.length > 0) { detectedUSBDevices = result.devices; // Build device list HTML const deviceListHTML = result.devices.map((device, index) => `
${device.bus_line}
ID: ${device.id} • Bus ${device.bus} Device ${device.device}
`).join(''); document.getElementById('usb-devices-list').innerHTML = deviceListHTML; // Show step 2, hide step 1 document.getElementById('usb-import-step-1').style.display = 'none'; document.getElementById('usb-import-step-2').style.display = 'block'; showSuccess(`${result.total_devices} périphérique(s) USB détecté(s)`); } } catch (error) { console.error('Error detecting USB devices:', error); showError(error.message || 'Erreur lors de la détection des périphériques USB'); } } // Select USB device (radio button click) function selectUSBDevice(index) { const radio = document.getElementById(`usb-device-${index}`); if (radio) { radio.checked = true; // Enable Finaliser button when a device is selected document.getElementById('btn-finalize-usb').disabled = false; } } // Go back to step 1 function backToUSBStep1() { document.getElementById('usb-import-step-2').style.display = 'none'; document.getElementById('usb-import-step-1').style.display = 'block'; document.getElementById('btn-finalize-usb').disabled = true; detectedUSBDevices = []; } // Import selected USB device async function importSelectedUSBDevice() { const selectedRadio = document.querySelector('input[name="selected_usb_device"]:checked'); if (!selectedRadio) { showError('Veuillez sélectionner un périphérique'); return; } const selectedIndex = parseInt(selectedRadio.value); const selectedDevice = detectedUSBDevices[selectedIndex]; if (!selectedDevice) { showError('Périphérique sélectionné invalide'); return; } // Extract the device section const formData = new FormData(); formData.append('lsusb_output', fullUSBOutput); formData.append('bus', selectedDevice.bus); formData.append('device', selectedDevice.device); try { const result = await apiRequest('/peripherals/import/usb-cli/extract', { method: 'POST', body: formData }); if (result.success) { if (result.already_exists) { // Device already exists const existing = result.existing_peripheral; const message = `Ce périphérique existe déjà dans la base de données:\n\n` + `Nom: ${existing.nom}\n` + `Marque: ${existing.marque || 'N/A'}\n` + `Modèle: ${existing.modele || 'N/A'}\n` + `Fabricant: ${existing.fabricant || 'N/A'}\n` + `Produit: ${existing.produit || 'N/A'}\n` + `Quantité disponible: ${existing.quantite_disponible}\n\n` + `Voulez-vous voir sa fiche ?`; if (confirm(message)) { window.location.href = `peripheral-detail.html?id=${existing.id}`; } } else { // New device - pre-fill form closeModal('modal-import-usb'); showAddModal(); const suggested = result.suggested_peripheral; // Fill form fields if (suggested.nom) document.getElementById('nom').value = suggested.nom; if (suggested.marque) document.getElementById('marque').value = suggested.marque; if (suggested.modele) document.getElementById('modele').value = suggested.modele; if (suggested.fabricant) document.getElementById('fabricant').value = suggested.fabricant; if (suggested.produit) document.getElementById('produit').value = suggested.produit; if (suggested.numero_serie) document.getElementById('numero_serie').value = suggested.numero_serie; // Fill documentation fields if (suggested.cli_yaml) { const cliYamlField = document.getElementById('cli_yaml'); if (cliYamlField) { cliYamlField.value = suggested.cli_yaml; } } if (suggested.cli_raw) { const cliRawField = document.getElementById('cli_raw'); if (cliRawField) { cliRawField.value = suggested.cli_raw; } } if (suggested.synthese) { const syntheseField = document.getElementById('synthese'); if (syntheseField) { syntheseField.value = suggested.synthese; } } // Fill USB detailed information fillUSBDetails(suggested.caracteristiques_specifiques); // Set type_principal and wait for subtypes to load before setting sous_type if (suggested.type_principal) { document.getElementById('type_principal').value = suggested.type_principal; // Trigger the change event to load subtypes const typePrincipalSelect = document.getElementById('type_principal'); const changeEvent = new Event('change'); typePrincipalSelect.dispatchEvent(changeEvent); // Wait for subtypes to load, then set sous_type if (suggested.sous_type) { // Use a Promise-based approach with retry logic const setSousType = async () => { for (let i = 0; i < 10; i++) { await new Promise(resolve => setTimeout(resolve, 100)); const sousTypeSelect = document.getElementById('sous_type'); if (sousTypeSelect && sousTypeSelect.options.length > 1) { // Options are loaded sousTypeSelect.value = suggested.sous_type; break; } } }; setSousType(); } } showSuccess('Données USB importées avec succès. Vérifiez et complétez les informations.'); // Reset USB import state detectedUSBDevices = []; fullUSBOutput = ''; document.getElementById('usb-import-step-2').style.display = 'none'; document.getElementById('usb-import-step-1').style.display = 'block'; document.getElementById('form-detect-usb').reset(); } } } catch (error) { console.error('Error extracting USB device:', error); showError(error.message || 'Erreur lors de l\'extraction du périphérique USB'); } } // Import USB structured information async function importUSBStructured(event) { event.preventDefault(); const formData = new FormData(event.target); try { const result = await apiRequest('/peripherals/import/usb-structured', { method: 'POST', body: formData }); if (result.success) { if (result.already_exists) { // Device already exists const existing = result.existing_peripheral; const message = `Ce périphérique existe déjà dans la base de données:\n\n` + `Nom: ${existing.nom}\n` + `Marque: ${existing.marque || 'N/A'}\n` + `Modèle: ${existing.modele || 'N/A'}\n` + `Fabricant: ${existing.fabricant || 'N/A'}\n` + `Produit: ${existing.produit || 'N/A'}\n` + `Quantité disponible: ${existing.quantite_disponible}\n\n` + `Voulez-vous voir sa fiche ?`; if (confirm(message)) { window.location.href = `peripheral-detail.html?id=${existing.id}`; } } else { // New device - pre-fill form closeModal('modal-import-usb-structured'); showAddModal(); const suggested = result.suggested_peripheral; // Fill form fields if (suggested.nom) document.getElementById('nom').value = suggested.nom; if (suggested.marque) document.getElementById('marque').value = suggested.marque; if (suggested.modele) document.getElementById('modele').value = suggested.modele; if (suggested.fabricant) document.getElementById('fabricant').value = suggested.fabricant; if (suggested.produit) document.getElementById('produit').value = suggested.produit; if (suggested.numero_serie) document.getElementById('numero_serie').value = suggested.numero_serie; // Fill documentation fields if (suggested.cli_yaml) { const cliYamlField = document.getElementById('cli_yaml'); if (cliYamlField) { cliYamlField.value = suggested.cli_yaml; } } if (suggested.cli_raw) { const cliRawField = document.getElementById('cli_raw'); if (cliRawField) { cliRawField.value = suggested.cli_raw; } } // Fill USB detailed information fillUSBDetails(suggested.caracteristiques_specifiques); // Set type_principal and wait for subtypes to load before setting sous_type if (suggested.type_principal) { document.getElementById('type_principal').value = suggested.type_principal; // Trigger the change event to load subtypes const typePrincipalSelect = document.getElementById('type_principal'); const changeEvent = new Event('change'); typePrincipalSelect.dispatchEvent(changeEvent); // Wait for subtypes to load, then set sous_type if (suggested.sous_type) { const setSousType = async () => { for (let i = 0; i < 10; i++) { await new Promise(resolve => setTimeout(resolve, 100)); const sousTypeSelect = document.getElementById('sous_type'); if (sousTypeSelect && sousTypeSelect.options.length > 1) { sousTypeSelect.value = suggested.sous_type; break; } } }; setSousType(); } } showSuccess('Informations USB importées avec succès. Vérifiez et complétez les informations.'); // Reset form document.getElementById('form-import-usb-structured').reset(); } } } catch (error) { console.error('Error importing USB structured info:', error); showError(error.message || 'Erreur lors de l\'import des informations USB'); } } // Show import markdown modal function showImportMDModal() { document.getElementById('form-import-md').reset(); document.getElementById('md-file-preview').innerHTML = ''; document.getElementById('modal-import-md').style.display = 'block'; // Add file change listener for preview const fileInput = document.getElementById('md-file'); fileInput.addEventListener('change', function() { const file = fileInput.files[0]; if (file) { document.getElementById('md-file-preview').innerHTML = `
${file.name} (${(file.size / 1024).toFixed(2)} KB)
`; } }); } // Import Markdown async function importMarkdown(event) { event.preventDefault(); const formData = new FormData(event.target); const file = document.getElementById('md-file').files[0]; if (!file) { showError('Veuillez sélectionner un fichier .md'); return; } if (!file.name.endsWith('.md')) { showError('Seuls les fichiers .md sont acceptés'); return; } try { const result = await apiRequest('/peripherals/import/markdown', { method: 'POST', body: formData }); if (result.success) { // Check if peripheral already exists if (result.already_exists) { closeModal('modal-import-md'); const existing = result.existing_peripheral; const message = `Ce périphérique existe déjà dans la base de données:\n\n` + `Nom: ${existing.nom}\n` + `Marque: ${existing.marque || 'N/A'}\n` + `Modèle: ${existing.modele || 'N/A'}\n` + `Fabricant: ${existing.fabricant || 'N/A'}\n` + `Produit: ${existing.produit || 'N/A'}\n` + `Quantité: ${existing.quantite_totale}\n\n` + `Voulez-vous voir ce périphérique?`; if (confirm(message)) { // Redirect to detail page window.location.href = `peripheral-detail.html?id=${existing.id}`; } else { showInfo(`Import annulé - le périphérique "${existing.nom}" existe déjà.`); } } else if (result.suggested_peripheral) { // New peripheral - pre-fill form closeModal('modal-import-md'); showAddModal(); const suggested = result.suggested_peripheral; // Fill basic form fields if (suggested.nom) document.getElementById('nom').value = suggested.nom; if (suggested.type_principal) { document.getElementById('type_principal').value = suggested.type_principal; loadPeripheralSubtypes(); // Reload subtypes } // Wait for subtypes to load before setting sous_type setTimeout(() => { if (suggested.sous_type) document.getElementById('sous_type').value = suggested.sous_type; }, 100); if (suggested.marque) document.getElementById('marque').value = suggested.marque; if (suggested.modele) document.getElementById('modele').value = suggested.modele; if (suggested.fabricant) document.getElementById('fabricant').value = suggested.fabricant; if (suggested.produit) document.getElementById('produit').value = suggested.produit; if (suggested.numero_serie) document.getElementById('numero_serie').value = suggested.numero_serie; if (suggested.description) document.getElementById('description').value = suggested.description; if (suggested.notes) document.getElementById('notes').value = suggested.notes; if (suggested.etat) document.getElementById('etat').value = suggested.etat; if (suggested.quantite_totale) document.getElementById('quantite_totale').value = suggested.quantite_totale; if (suggested.quantite_disponible) document.getElementById('quantite_disponible').value = suggested.quantite_disponible; // Show success message with filename showSuccess(`Fichier ${result.filename} importé avec succès. Vérifiez et complétez les informations.`); } } } catch (error) { console.error('Error importing markdown:', error); showError('Erreur lors de l\'import du fichier .md. Vérifiez le format du fichier.'); } } // Cache for peripheral types from API let peripheralTypesCache = null; // Load peripheral types from API async function loadPeripheralTypesFromAPI() { if (peripheralTypesCache) { return peripheralTypesCache; } try { const result = await apiRequest('/peripherals/config/types'); if (result.success && result.types) { peripheralTypesCache = result.types; return result.types; } } catch (error) { console.error('Failed to load peripheral types from API:', error); } // Fallback to hardcoded types if API fails return { 'USB': ['Clavier', 'Souris', 'Hub', 'Clé USB', 'Webcam', 'Adaptateur WiFi', 'Autre'], 'Bluetooth': ['Clavier', 'Souris', 'Audio', 'Autre'], 'Réseau': ['Wi-Fi', 'Ethernet', 'Autre'], 'Stockage': ['SSD', 'HDD', 'Clé USB', 'Carte SD', 'Autre'], 'Video': ['GPU', 'Écran', 'Webcam', 'Autre'], 'Audio': ['Haut-parleur', 'Microphone', 'Casque', 'Carte son', 'Autre'], 'Câble': ['USB', 'HDMI', 'DisplayPort', 'Ethernet', 'Audio', 'Alimentation', 'Autre'], 'Quincaillerie': ['Vis', 'Écrou', 'Entretoise', 'Autre'], 'Console': ['PlayStation', 'Xbox', 'Nintendo', 'Autre'], 'Microcontrôleur': ['Raspberry Pi', 'Arduino', 'ESP32', 'Autre'] }; } // Load peripheral subtypes based on type async function loadPeripheralSubtypes() { const type = document.getElementById('type_principal').value; const subtypeSelect = document.getElementById('sous_type'); // Clear current options subtypeSelect.innerHTML = ''; // Load types from API const subtypes = await loadPeripheralTypesFromAPI(); if (subtypes[type]) { // Add "Autre" at the end if not present const typeSubtypes = [...subtypes[type]]; if (!typeSubtypes.includes('Autre')) { typeSubtypes.push('Autre'); } typeSubtypes.forEach(subtype => { const option = document.createElement('option'); option.value = subtype; option.textContent = subtype; subtypeSelect.appendChild(option); }); } } // Helper: Get CSS class for état function getEtatClass(etat) { const classes = { 'Neuf': 'success', 'Bon': 'info', 'Usagé': 'warning', 'Défectueux': 'danger', 'Retiré': 'secondary' }; return classes[etat] || 'secondary'; } // Get icon based on peripheral type function getTypeIcon(type) { if (!type) return 'fa-microchip'; const typeUpper = type.toUpperCase(); // USB devices if (typeUpper.includes('USB')) return 'fa-plug'; // Storage devices if (typeUpper.includes('STOCKAGE') || typeUpper.includes('DISK') || typeUpper.includes('SSD') || typeUpper.includes('HDD') || typeUpper.includes('FLASH')) return 'fa-hard-drive'; // Network devices if (typeUpper.includes('RÉSEAU') || typeUpper.includes('RESEAU') || typeUpper.includes('NETWORK') || typeUpper.includes('WIFI') || typeUpper.includes('ETHERNET')) return 'fa-network-wired'; // Audio devices if (typeUpper.includes('AUDIO') || typeUpper.includes('SOUND') || typeUpper.includes('SPEAKER') || typeUpper.includes('HEADPHONE')) return 'fa-volume-up'; // Video devices if (typeUpper.includes('VIDEO') || typeUpper.includes('VIDÉO') || typeUpper.includes('WEBCAM') || typeUpper.includes('CAMERA')) return 'fa-video'; // Input devices if (typeUpper.includes('CLAVIER') || typeUpper.includes('KEYBOARD')) return 'fa-keyboard'; if (typeUpper.includes('SOURIS') || typeUpper.includes('MOUSE')) return 'fa-mouse'; // Other devices if (typeUpper.includes('BLUETOOTH')) return 'fa-bluetooth'; if (typeUpper.includes('HUB')) return 'fa-sitemap'; if (typeUpper.includes('ADAPTATEUR') || typeUpper.includes('ADAPTER')) return 'fa-plug'; if (typeUpper.includes('CÂBLE') || typeUpper.includes('CABLE')) return 'fa-link'; // Default return 'fa-microchip'; } // Helper: Render star rating function renderStars(rating) { const fullStars = Math.floor(rating); const hasHalfStar = rating % 1 >= 0.5; const emptyStars = 5 - fullStars - (hasHalfStar ? 1 : 0); let html = ''; for (let i = 0; i < fullStars; i++) { html += ''; } if (hasHalfStar) { html += ''; } for (let i = 0; i < emptyStars; i++) { html += ''; } return html; } // Close modal function closeModal(modalId) { document.getElementById(modalId).style.display = 'none'; } // Close modal when clicking outside window.onclick = function(event) { if (event.target.classList.contains('modal')) { event.target.style.display = 'none'; } }