comsync-pro-team-connect-hub / device-management.html
secutorpro's picture
🐳 24/02 - 16:53 - corrige le systeme
b56a800 verified
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gestion des Appareils - ComSync Pro</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.net.min.js"></script>
<link rel="stylesheet" href="style.css">
<style>
.device-type-tab {
transition: all 0.3s ease;
}
.device-type-tab.active {
background: rgba(59, 130, 246, 0.2);
border-color: #3b82f6;
color: #3b82f6;
}
.qr-scanner {
background: repeating-linear-gradient(
0deg,
rgba(0,0,0,0.15),
rgba(0,0,0,0.15) 1px,
transparent 1px,
transparent 2px
);
animation: scan 2s linear infinite;
}
@keyframes scan {
0% { background-position: 0 0; }
100% { background-position: 0 100px; }
}
.status-badge {
display: inline-flex;
align-items: center;
padding: 0.25rem 0.75rem;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 500;
}
.status-online { background: rgba(16, 185, 129, 0.2); color: #10b981; }
.status-offline { background: rgba(239, 68, 68, 0.2); color: #ef4444; }
.status-pending { background: rgba(245, 158, 11, 0.2); color: #f59e0b; }
.brand-logo {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
font-weight: bold;
font-size: 0.75rem;
}
</style>
</head>
<body class="bg-gray-900 text-gray-100">
<div id="vanta-bg" class="fixed inset-0 z-0"></div>
<script src="components/navbar.js"></script>
<custom-navbar></custom-navbar>
<div class="relative z-10 min-h-screen p-4 md:p-6">
<!-- Header -->
<header class="bg-gray-800/80 backdrop-blur-md rounded-xl p-4 mb-6 border border-gray-700/50 shadow-lg">
<div class="flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
<div>
<h1 class="text-2xl font-bold bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent">Gestion des Appareils</h1>
<p class="text-gray-400 text-sm mt-1" id="company-info">Société : <span class="text-white">Chargement...</span></p>
</div>
<div class="flex items-center gap-3">
<div class="bg-gray-700/50 rounded-lg p-2 flex items-center space-x-3">
<span class="text-sm text-gray-400">ID Société:</span>
<span class="font-mono text-sm text-blue-400" id="company-id-display">-</span>
<button onclick="copyCompanyId()" class="text-gray-400 hover:text-white">
<i data-feather="copy" class="w-4 h-4"></i>
</button>
</div>
<button onclick="openAddDeviceModal()" class="btn-primary flex items-center">
<i data-feather="plus" class="w-4 h-4 mr-2"></i>
Ajouter Appareil
</button>
</div>
</div>
</header>
<!-- Stats -->
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
<div class="bg-gray-800/80 backdrop-blur-sm rounded-xl p-4 border border-gray-700/50">
<div class="flex items-center justify-between mb-2">
<i data-feather="radio" class="w-5 h-5 text-green-400"></i>
<span class="text-xs text-gray-400">Talkies FM</span>
</div>
<p class="text-2xl font-bold" id="count-radios">0</p>
</div>
<div class="bg-gray-800/80 backdrop-blur-sm rounded-xl p-4 border border-gray-700/50">
<div class="flex items-center justify-between mb-2">
<i data-feather="smartphone" class="w-5 h-5 text-blue-400"></i>
<span class="text-xs text-gray-400">Android</span>
</div>
<p class="text-2xl font-bold" id="count-android">0</p>
</div>
<div class="bg-gray-800/80 backdrop-blur-sm rounded-xl p-4 border border-gray-700/50">
<div class="flex items-center justify-between mb-2">
<i data-feather="tablet" class="w-5 h-5 text-purple-400"></i>
<span class="text-xs text-gray-400">Tablettes</span>
</div>
<p class="text-2xl font-bold" id="count-tablets">0</p>
</div>
<div class="bg-gray-800/80 backdrop-blur-sm rounded-xl p-4 border border-gray-700/50">
<div class="flex items-center justify-between mb-2">
<i data-feather="activity" class="w-5 h-5 text-green-400"></i>
<span class="text-xs text-gray-400">En ligne</span>
</div>
<p class="text-2xl font-bold" id="count-online">0</p>
</div>
</div>
<!-- Tabs -->
<div class="bg-gray-800/80 backdrop-blur-md rounded-xl p-2 mb-6 border border-gray-700/50">
<div class="flex space-x-2 overflow-x-auto">
<button onclick="switchTab('all')" class="device-type-tab active px-4 py-2 rounded-lg text-sm font-medium border border-transparent" id="tab-all">
Tous les appareils
</button>
<button onclick="switchTab('radios')" class="device-type-tab px-4 py-2 rounded-lg text-sm font-medium border border-transparent" id="tab-radios">
<i data-feather="radio" class="w-4 h-4 inline mr-1"></i> Talkies FM
</button>
<button onclick="switchTab('android')" class="device-type-tab px-4 py-2 rounded-lg text-sm font-medium border border-transparent" id="tab-android">
<i data-feather="smartphone" class="w-4 h-4 inline mr-1"></i> Android
</button>
<button onclick="switchTab('tablets')" class="device-type-tab px-4 py-2 rounded-lg text-sm font-medium border border-transparent" id="tab-tablets">
<i data-feather="tablet" class="w-4 h-4 inline mr-1"></i> Tablettes
</button>
<button onclick="switchTab('pending')" class="device-type-tab px-4 py-2 rounded-lg text-sm font-medium border border-transparent" id="tab-pending">
<i data-feather="clock" class="w-4 h-4 inline mr-1"></i> En attente
</button>
</div>
</div>
<!-- Devices Grid -->
<div id="devices-container" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<!-- Devices will be loaded here -->
</div>
<!-- Empty State -->
<div id="empty-state" class="hidden text-center py-12">
<i data-feather="smartphone" class="w-16 h-16 text-gray-600 mx-auto mb-4"></i>
<h3 class="text-xl font-semibold text-gray-400 mb-2">Aucun appareil enregistré</h3>
<p class="text-gray-500 mb-6">Commencez par ajouter votre premier appareil</p>
<button onclick="openAddDeviceModal()" class="btn-primary">
Ajouter un appareil
</button>
</div>
</div>
<!-- Add Device Modal -->
<div id="add-device-modal" class="fixed inset-0 bg-black/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
<div class="bg-gray-800 rounded-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto border border-gray-700">
<div class="p-6 border-b border-gray-700 flex justify-between items-center">
<h2 class="text-xl font-semibold">Ajouter un nouvel appareil</h2>
<button onclick="closeAddDeviceModal()" class="text-gray-400 hover:text-white">
<i data-feather="x" class="w-6 h-6"></i>
</button>
</div>
<div class="p-6">
<!-- Device Type Selection -->
<div class="mb-6">
<label class="block text-sm font-medium mb-3">Type d'appareil *</label>
<div class="grid grid-cols-2 md:grid-cols-4 gap-3">
<button onclick="selectDeviceType('radio')" class="device-type-btn p-4 border border-gray-600 rounded-lg hover:border-blue-500 transition-all text-center" id="btn-type-radio">
<i data-feather="radio" class="w-8 h-8 mx-auto mb-2 text-green-400"></i>
<div class="text-sm font-medium">Talkie FM</div>
</button>
<button onclick="selectDeviceType('android')" class="device-type-btn p-4 border border-gray-600 rounded-lg hover:border-blue-500 transition-all text-center" id="btn-type-android">
<i data-feather="smartphone" class="w-8 h-8 mx-auto mb-2 text-blue-400"></i>
<div class="text-sm font-medium">Smartphone</div>
</button>
<button onclick="selectDeviceType('tablet')" class="device-type-btn p-4 border border-gray-600 rounded-lg hover:border-blue-500 transition-all text-center" id="btn-type-tablet">
<i data-feather="tablet" class="w-8 h-8 mx-auto mb-2 text-purple-400"></i>
<div class="text-sm font-medium">Tablette</div>
</button>
<button onclick="selectDeviceType('desktop')" class="device-type-btn p-4 border border-gray-600 rounded-lg hover:border-blue-500 transition-all text-center" id="btn-type-desktop">
<i data-feather="monitor" class="w-8 h-8 mx-auto mb-2 text-yellow-400"></i>
<div class="text-sm font-medium">PC/Mac</div>
</button>
</div>
</div>
<!-- Radio Specific Fields -->
<div id="radio-fields" class="hidden space-y-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium mb-2">Marque *</label>
<select id="radio-brand" class="w-full bg-gray-700 rounded-lg px-4 py-3 border border-gray-600">
<option value="">Sélectionner...</option>
<option value="Motorola">Motorola</option>
<option value="Kenwood">Kenwood</option>
<option value="Hytera">Hytera</option>
<option value="Icom">Icom</option>
<option value="Midland">Midland</option>
<option value="Retevis">Retevis</option>
<option value="Baofeng">Baofeng</option>
<option value="TYT">TYT</option>
<option value="Autre">Autre</option>
</select>
</div>
<div>
<label class="block text-sm font-medium mb-2">Modèle *</label>
<input type="text" id="radio-model" class="w-full bg-gray-700 rounded-lg px-4 py-3 border border-gray-600" placeholder="Ex: DP4400e">
</div>
<div>
<label class="block text-sm font-medium mb-2">Numéro de série / IMEI</label>
<input type="text" id="radio-serial" class="w-full bg-gray-700 rounded-lg px-4 py-3 border border-gray-600" placeholder="Numéro de série">
</div>
<div>
<label class="block text-sm font-medium mb-2">Fréquence par défaut (MHz)</label>
<input type="text" id="radio-freq" class="w-full bg-gray-700 rounded-lg px-4 py-3 border border-gray-600" placeholder="462.550">
</div>
</div>
<div>
<label class="block text-sm font-medium mb-2">Bluetooth MAC Address</label>
<input type="text" id="radio-mac" class="w-full bg-gray-700 rounded-lg px-4 py-3 border border-gray-600" placeholder="AA:BB:CC:DD:EE:FF">
</div>
</div>
<!-- Android/Tablet Fields -->
<div id="android-fields" class="hidden space-y-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium mb-2">Marque</label>
<select id="android-brand" class="w-full bg-gray-700 rounded-lg px-4 py-3 border border-gray-600">
<option value="">Sélectionner...</option>
<option value="Samsung">Samsung</option>
<option value="Google">Google Pixel</option>
<option value="Xiaomi">Xiaomi</option>
<option value="Huawei">Huawei</option>
<option value="Sony">Sony</option>
<option value="CAT">CAT (Caterpillar)</option>
<option value="Crosscall">Crosscall</option>
<option value="Autre">Autre</option>
</select>
</div>
<div>
<label class="block text-sm font-medium mb-2">Modèle</label>
<input type="text" id="android-model" class="w-full bg-gray-700 rounded-lg px-4 py-3 border border-gray-600" placeholder="Ex: Galaxy XCover Pro">
</div>
</div>
<div>
<label class="block text-sm font-medium mb-2">Numéro IMEI *</label>
<input type="text" id="android-imei" class="w-full bg-gray-700 rounded-lg px-4 py-3 border border-gray-600" placeholder="Tapez *#06# sur l'appareil">
</div>
<div class="bg-blue-900/20 border border-blue-700/50 rounded-lg p-4">
<div class="flex items-start space-x-3">
<i data-feather="info" class="w-5 h-5 text-blue-400 mt-0.5"></i>
<div class="text-sm">
<p class="text-blue-400 font-medium mb-1">Installation requise</p>
<p class="text-gray-300">L'appareil doit avoir l'application ComSync Pro installée. <a href="#" class="text-blue-400 hover:underline">Télécharger l'APK</a></p>
</div>
</div>
</div>
</div>
<!-- Common Fields -->
<div class="space-y-4 mt-4">
<div>
<label class="block text-sm font-medium mb-2">Nom de l'appareil (pour l'identifier)</label>
<input type="text" id="device-name" class="w-full bg-gray-700 rounded-lg px-4 py-3 border border-gray-600" placeholder="Ex: Radio Sécurité Nord">
</div>
<div>
<label class="block text-sm font-medium mb-2">Assigné à (optionnel)</label>
<select id="device-assignee" class="w-full bg-gray-700 rounded-lg px-4 py-3 border border-gray-600">
<option value="">Non assigné</option>
</select>
</div>
</div>
<!-- QR Code Section -->
<div class="mt-6 border-t border-gray-700 pt-6">
<h3 class="text-sm font-medium mb-4">Configuration rapide par QR Code</h3>
<div class="flex items-center space-x-4">
<div class="bg-white p-4 rounded-lg">
<!-- Simulated QR Code -->
<div class="w-32 h-32 bg-gray-900 rounded relative overflow-hidden">
<div class="absolute inset-0 qr-scanner"></div>
<div class="absolute inset-4 border-2 border-white rounded flex items-center justify-center">
<i data-feather="smartphone" class="w-8 h-8 text-white"></i>
</div>
</div>
</div>
<div class="flex-1">
<p class="text-sm text-gray-400 mb-2">Scannez ce QR code avec l'appareil pour l'enregistrer automatiquement.</p>
<button onclick="generateNewQR()" class="text-blue-400 text-sm hover:underline flex items-center">
<i data-feather="refresh-cw" class="w-4 h-4 mr-1"></i>
Générer un nouveau code
</button>
</div>
</div>
</div>
</div>
<div class="p-6 border-t border-gray-700 flex justify-end space-x-3">
<button onclick="closeAddDeviceModal()" class="btn-secondary">Annuler</button>
<button onclick="saveDevice()" class="btn-primary">Enregistrer l'appareil</button>
</div>
</div>
</div>
<!-- Device Details Modal -->
<div id="device-details-modal" class="fixed inset-0 bg-black/80 backdrop-blur-sm z-50 hidden flex items-center justify-center p-4">
<div class="bg-gray-800 rounded-xl max-w-lg w-full border border-gray-700" id="device-details-content">
<!-- Content loaded dynamically -->
</div>
</div>
<script>
// Initialize Vanta avec gestion d'erreur
let vantaInstance = null;
try {
if (typeof VANTA !== 'undefined' && document.getElementById('vanta-bg')) {
vantaInstance = VANTA.NET({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
scale: 1.00,
scaleMobile: 1.00,
color: 0x3b82f6,
backgroundColor: 0x111827,
points: 10.00,
maxDistance: 22.00,
spacing: 18.00
});
}
} catch (e) {
console.warn('Vanta.js non disponible:', e);
}
// Initialize Feather icons
document.addEventListener('DOMContentLoaded', function() {
setTimeout(function() {
if (typeof feather !== 'undefined') {
feather.replace();
}
}, 100);
});
let currentTab = 'all';
let selectedDeviceType = null;
let companyData = null;
// Load company data avec vérification null
function loadCompanyData() {
try {
const data = localStorage.getItem('comsync_current_company');
const companyInfo = document.getElementById('company-info');
const companyIdDisplay = document.getElementById('company-id-display');
if (data && companyInfo && companyIdDisplay) {
companyData = JSON.parse(data);
companyInfo.innerHTML = `Société : <span class="text-white font-semibold">${companyData.name || 'Sans nom'}</span>`;
companyIdDisplay.textContent = companyData.id || 'N/A';
} else {
// Demo mode if no company registered
if (companyInfo) {
companyInfo.innerHTML = `Société : <span class="text-white font-semibold">Démo Mode</span>`;
}
if (companyIdDisplay) {
companyIdDisplay.textContent = 'DEMO-001';
}
}
loadDevices();
} catch (e) {
console.error('Erreur chargement données société:', e);
// Mode dégradé
const companyInfo = document.getElementById('company-info');
const companyIdDisplay = document.getElementById('company-id-display');
if (companyInfo) companyInfo.innerHTML = `Société : <span class="text-white font-semibold">Erreur chargement</span>`;
if (companyIdDisplay) companyIdDisplay.textContent = 'ERROR';
loadDevices();
}
}
function copyCompanyId() {
const id = document.getElementById('company-id-display').textContent;
navigator.clipboard.writeText(id).then(() => {
alert('ID copié dans le presse-papier !');
});
}
function switchTab(tab) {
currentTab = tab;
// Update UI
document.querySelectorAll('.device-type-tab').forEach(t => t.classList.remove('active'));
document.getElementById(`tab-${tab}`).classList.add('active');
loadDevices();
}
function loadDevices() {
try {
const devices = JSON.parse(localStorage.getItem('comsync_devices') || '[]');
const container = document.getElementById('devices-container');
const emptyState = document.getElementById('empty-state');
if (!container || !emptyState) return;
// Filter devices avec vérification
let filtered = devices || [];
if (currentTab !== 'all' && currentTab !== 'pending') {
const typeFilter = currentTab === 'radios' ? 'radio' :
currentTab === 'tablets' ? 'tablet' : 'android';
filtered = devices.filter(d => d && d.type === typeFilter);
} else if (currentTab === 'pending') {
filtered = devices.filter(d => d && d.status === 'pending');
}
// Update counts avec vérification null
const countRadios = document.getElementById('count-radios');
const countAndroid = document.getElementById('count-android');
const countTablets = document.getElementById('count-tablets');
const countOnline = document.getElementById('count-online');
if (countRadios) countRadios.textContent = devices.filter(d => d && d.type === 'radio').length;
if (countAndroid) countAndroid.textContent = devices.filter(d => d && (d.type === 'android' || d.type === 'tablet')).length;
if (countTablets) countTablets.textContent = devices.filter(d => d && d.type === 'tablet').length;
if (countOnline) countOnline.textContent = devices.filter(d => d && d.status === 'online').length;
if (filtered.length === 0) {
container.innerHTML = '';
emptyState.classList.remove('hidden');
return;
}
emptyState.classList.add('hidden');
container.innerHTML = filtered.map(device => createDeviceCard(device)).join('');
// Réinitialiser Feather icons
setTimeout(() => {
if (typeof feather !== 'undefined') {
feather.replace();
}
}, 50);
} catch (e) {
console.error('Erreur chargement appareils:', e);
const container = document.getElementById('devices-container');
if (container) {
container.innerHTML = '<div class="col-span-full text-center text-red-400">Erreur lors du chargement des appareils</div>';
}
}
}
function createDeviceCard(device) {
if (!device || !device.id) return '';
const icons = {
radio: 'radio',
android: 'smartphone',
tablet: 'tablet',
desktop: 'monitor'
};
const colors = {
radio: 'green',
android: 'blue',
tablet: 'purple',
desktop: 'yellow'
};
const deviceType = device.type || 'unknown';
const deviceStatus = device.status || 'offline';
const deviceBrand = (device.brand && String(device.brand)) || 'Inconnu';
const deviceModel = (device.model && String(device.model)) || '';
const deviceName = (device.name && String(device.name)) || 'Appareil sans nom';
const statusClass = deviceStatus === 'online' ? 'status-online' :
deviceStatus === 'pending' ? 'status-pending' : 'status-offline';
const statusText = deviceStatus === 'online' ? 'En ligne' :
deviceStatus === 'pending' ? 'En attente' : 'Hors ligne';
const brandColors = {
'Motorola': 'bg-blue-600',
'Kenwood': 'bg-red-600',
'Hytera': 'bg-orange-600',
'Icom': 'bg-green-600',
'Midland': 'bg-purple-600',
'Retevis': 'bg-yellow-600',
'Baofeng': 'bg-gray-600',
'TYT': 'bg-indigo-600',
'Samsung': 'bg-blue-500',
'Google': 'bg-gray-600',
'Xiaomi': 'bg-orange-500',
'Huawei': 'bg-red-500',
'Sony': 'bg-black',
'CAT': 'bg-yellow-600',
'Crosscall': 'bg-green-500'
};
const brandColor = brandColors[deviceBrand] || 'bg-gray-600';
const iconName = icons[deviceType] || 'help-circle';
const colorName = colors[deviceType] || 'gray';
const shortId = device.id ? String(device.id).slice(-8) : 'UNKNOWN';
return `
<div class="bg-gray-800/80 backdrop-blur-sm rounded-xl p-5 border border-gray-700/50 hover:border-blue-500/50 transition-all cursor-pointer" onclick="showDeviceDetails('${device.id}')">
<div class="flex items-start justify-between mb-4">
<div class="flex items-center space-x-3">
<div class="brand-logo ${brandColor} text-white flex-shrink-0">
${deviceBrand.substring(0, 2).toUpperCase()}
</div>
<div class="min-w-0 flex-1">
<h3 class="font-semibold truncate">${deviceName}</h3>
<p class="text-xs text-gray-400 truncate">${deviceBrand} ${deviceModel}</p>
</div>
</div>
<span class="status-badge ${statusClass} flex-shrink-0">${statusText}</span>
</div>
<div class="grid grid-cols-2 gap-3 mb-4 text-sm">
<div class="bg-gray-700/50 rounded p-2 min-w-0">
<p class="text-gray-400 text-xs">ID</p>
<p class="font-mono text-xs truncate">${shortId}</p>
</div>
<div class="bg-gray-700/50 rounded p-2 min-w-0">
<p class="text-gray-400 text-xs">Dernier sync</p>
<p class="text-xs truncate">${device.lastSync || 'Jamais'}</p>
</div>
</div>
<div class="flex items-center justify-between pt-3 border-t border-gray-700">
<div class="flex items-center space-x-2">
<i data-feather="${iconName}" class="w-4 h-4 text-${colorName}-400"></i>
<span class="text-xs text-gray-400 capitalize">${deviceType}</span>
</div>
<div class="flex space-x-2">
<button onclick="event.stopPropagation(); toggleDeviceStatus('${device.id}')" class="p-2 hover:bg-gray-700 rounded transition-colors" title="Changer statut">
<i data-feather="power" class="w-4 h-4"></i>
</button>
<button onclick="event.stopPropagation(); deleteDevice('${device.id}')" class="p-2 hover:bg-gray-700 rounded text-red-400 transition-colors" title="Supprimer">
<i data-feather="trash-2" class="w-4 h-4"></i>
</button>
</div>
</div>
</div>
`;
}
function openAddDeviceModal() {
document.getElementById('add-device-modal').classList.remove('hidden');
selectDeviceType('radio');
}
function closeAddDeviceModal() {
document.getElementById('add-device-modal').classList.add('hidden');
// Reset form
document.querySelectorAll('.device-type-btn').forEach(b => b.classList.remove('border-blue-500', 'bg-blue-900/20'));
selectedDeviceType = null;
}
function selectDeviceType(type) {
selectedDeviceType = type;
// Update UI
document.querySelectorAll('.device-type-btn').forEach(b => {
b.classList.remove('border-blue-500', 'bg-blue-900/20');
});
document.getElementById(`btn-type-${type}`).classList.add('border-blue-500', 'bg-blue-900/20');
// Show/hide specific fields
document.getElementById('radio-fields').classList.add('hidden');
document.getElementById('android-fields').classList.add('hidden');
if (type === 'radio') {
document.getElementById('radio-fields').classList.remove('hidden');
} else if (type === 'android' || type === 'tablet') {
document.getElementById('android-fields').classList.remove('hidden');
}
}
function saveDevice() {
if (!selectedDeviceType) {
alert('Veuillez sélectionner un type d\'appareil');
return;
}
try {
const deviceId = 'DEV-' + Date.now().toString(36).toUpperCase();
// Fonction utilitaire pour récupérer les valeurs
const getInputValue = (id) => {
const el = document.getElementById(id);
return el ? el.value.trim() : '';
};
let deviceData = {
id: deviceId,
type: selectedDeviceType,
name: getInputValue('device-name'),
assignee: getInputValue('device-assignee'),
status: 'pending',
lastSync: null,
companyId: (companyData && companyData.id) ? companyData.id : 'DEMO-001',
createdAt: new Date().toISOString()
};
// Type specific data
if (selectedDeviceType === 'radio') {
deviceData.brand = getInputValue('radio-brand');
deviceData.model = getInputValue('radio-model');
deviceData.serial = getInputValue('radio-serial');
deviceData.frequency = getInputValue('radio-freq');
deviceData.macAddress = getInputValue('radio-mac');
if (!deviceData.brand || !deviceData.model) {
alert('Veuillez remplir la marque et le modèle');
return;
}
} else {
deviceData.brand = getInputValue('android-brand');
deviceData.model = getInputValue('android-model');
deviceData.imei = getInputValue('android-imei');
if (!deviceData.imei) {
alert('L\'IMEI est requis pour les appareils Android');
return;
}
}
// Save to localStorage avec gestion d'erreur
let devices = [];
try {
const existing = localStorage.getItem('comsync_devices');
if (existing) {
devices = JSON.parse(existing);
}
} catch (parseError) {
console.warn('Erreur parsing devices:', parseError);
devices = [];
}
devices.push(deviceData);
localStorage.setItem('comsync_devices', JSON.stringify(devices));
closeAddDeviceModal();
loadDevices();
alert(`Appareil enregistré avec succès !\n\nID: ${deviceId}\nNom: ${deviceData.name || 'Non nommé'}\n\nL'appareil doit être activé pour apparaître en ligne.`);
} catch (error) {
console.error('Erreur sauvegarde appareil:', error);
alert('Erreur lors de l\'enregistrement. Veuillez réessayer.');
}
}
function deleteDevice(deviceId) {
if (!confirm('Êtes-vous sûr de vouloir supprimer cet appareil ?')) return;
let devices = JSON.parse(localStorage.getItem('comsync_devices') || '[]');
devices = devices.filter(d => d.id !== deviceId);
localStorage.setItem('comsync_devices', JSON.stringify(devices));
loadDevices();
}
function toggleDeviceStatus(deviceId) {
let devices = JSON.parse(localStorage.getItem('comsync_devices') || '[]');
const device = devices.find(d => d.id === deviceId);
if (device) {
device.status = device.status === 'online' ? 'offline' : 'online';
if (device.status === 'online') {
device.lastSync = new Date().toLocaleString();
}
localStorage.setItem('comsync_devices', JSON.stringify(devices));
loadDevices();
}
}
function showDeviceDetails(deviceId) {
const devices = JSON.parse(localStorage.getItem('comsync_devices') || '[]');
const device = devices.find(d => d.id === deviceId);
if (!device) return;
const content = `
<div class="p-6 border-b border-gray-700 flex justify-between items-center">
<h2 class="text-xl font-semibold">Détails de l'appareil</h2>
<button onclick="closeDeviceDetails()" class="text-gray-400 hover:text-white">
<i data-feather="x" class="w-6 h-6"></i>
</button>
</div>
<div class="p-6">
<div class="flex items-center space-x-4 mb-6">
<div class="w-16 h-16 bg-blue-600 rounded-xl flex items-center justify-center text-2xl font-bold">
${device.brand ? device.brand.substring(0, 2).toUpperCase() : 'NA'}
</div>
<div>
<h3 class="text-lg font-semibold">${device.name || 'Appareil sans nom'}</h3>
<p class="text-gray-400">${device.brand} ${device.model}</p>
<span class="status-badge ${device.status === 'online' ? 'status-online' : 'status-offline'} mt-1">
${device.status === 'online' ? 'En ligne' : 'Hors ligne'}
</span>
</div>
</div>
<div class="space-y-3 mb-6">
<div class="flex justify-between py-2 border-b border-gray-700">
<span class="text-gray-400">ID Appareil</span>
<span class="font-mono text-sm">${device.id}</span>
</div>
<div class="flex justify-between py-2 border-b border-gray-700">
<span class="text-gray-400">Type</span>
<span class="capitalize">${device.type}</span>
</div>
${device.imei ? `
<div class="flex justify-between py-2 border-b border-gray-700">
<span class="text-gray-400">IMEI</span>
<span class="font-mono text-sm">${device.imei}</span>
</div>` : ''}
${device.frequency ? `
<div class="flex justify-between py-2 border-b border-gray-700">
<span class="text-gray-400">Fréquence</span>
<span>${device.frequency} MHz</span>
</div>` : ''}
${device.macAddress ? `
<div class="flex justify-between py-2 border-b border-gray-700">
<span class="text-gray-400">Adresse MAC</span>
<span class="font-mono text-sm">${device.macAddress}</span>
</div>` : ''}
<div class="flex justify-between py-2 border-b border-gray-700">
<span class="text-gray-400">Dernière synchronisation</span>
<span>${device.lastSync || 'Jamais'}</span>
</div>
<div class="flex justify-between py-2 border-b border-gray-700">
<span class="text-gray-400">Ajouté le</span>
<span>${new Date(device.createdAt).toLocaleDateString()}</span>
</div>
</div>
<div class="flex space-x-3">
<button onclick="closeDeviceDetails()" class="flex-1 btn-secondary">Fermer</button>
<button onclick="closeDeviceDetails(); deleteDevice('${device.id}')" class="flex-1 bg-red-600 hover:bg-red-700 text-white py-2 rounded-lg">
Supprimer
</button>
</div>
</div>
`;
document.getElementById('device-details-content').innerHTML = content;
document.getElementById('device-details-modal').classList.remove('hidden');
feather.replace();
}
function closeDeviceDetails() {
document.getElementById('device-details-modal').classList.add('hidden');
}
function generateNewQR() {
alert('Nouveau QR Code généré pour l\'appareil.');
}
// Simulate real-time updates avec gestion d'erreur
setInterval(() => {
try {
const devices = JSON.parse(localStorage.getItem('comsync_devices') || '[]');
if (!Array.isArray(devices)) return;
let updated = false;
devices.forEach(device => {
if (device && device.status === 'online' && Math.random() > 0.95) {
device.lastSync = new Date().toLocaleString();
updated = true;
}
});
if (updated) {
localStorage.setItem('comsync_devices', JSON.stringify(devices));
loadDevices();
}
} catch (e) {
console.warn('Erreur mise à jour temps réel:', e);
}
}, 5000);
// Initialize
loadCompanyData();
// Handle browser navigation
window.addEventListener('popstate', function(event) {
location.reload();
});
history.pushState(null, null, location.href);
</script>
</body>
</html>