From 43e997ba47eae664ff21f6bf635aca59ae39b648 Mon Sep 17 00:00:00 2001 From: Marius Mutu Date: Sun, 7 Sep 2025 15:32:32 +0300 Subject: [PATCH] Enhance mobile UI and network scanning experience MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix modal scroll issues on mobile devices with proper viewport constraints - Remove status column and apply status colors directly to device names - Reorder columns: IP before MAC for better logical flow - Optimize table layout for mobile with reduced padding and text ellipsis - Implement visual disabled state for existing devices in scan modal - Add intelligent device comparison to prevent duplicate additions - Exclude Docker bridge networks from Linux scanning fallback - Improve scan result handling with better error messaging 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- app/app.py | 17 +++++++- app/static/css/style.css | 80 +++++++++++++++++++++++++++++++++++- app/static/js/app.js | 89 +++++++++++++++++++++++++--------------- app/templates/index.html | 3 +- 4 files changed, 151 insertions(+), 38 deletions(-) diff --git a/app/app.py b/app/app.py index 4d75ef3..390ffbe 100644 --- a/app/app.py +++ b/app/app.py @@ -360,6 +360,9 @@ class WOLManager: parts = line.split() if len(parts) >= 2: gateway = parts[1] + # Skip Docker bridge networks + if gateway.startswith('172.17.') or gateway.startswith('172.18.') or gateway.startswith('172.19.') or gateway.startswith('172.20.'): + continue # Construiește rețeaua bazată pe gateway network_parts = gateway.split('.') network = f"{network_parts[0]}.{network_parts[1]}.{network_parts[2]}.0/24" @@ -377,6 +380,10 @@ class WOLManager: mac = match.group(2) hostname = line.split()[0] if line.split() else '?' + # Skip Docker bridge networks + if ip.startswith('172.17.') or ip.startswith('172.18.') or ip.startswith('172.19.') or ip.startswith('172.20.'): + continue + # Verifică dacă IP-ul este în rețeaua specificată if custom_network: import ipaddress @@ -392,7 +399,7 @@ class WOLManager: except: pass else: - # Autodetectare - adaugă toate intrările ARP + # Autodetectare - adaugă toate intrările ARP (fără Docker bridge) scanned.append({ 'ip': ip, 'mac': mac, @@ -408,6 +415,14 @@ class WOLManager: 'message': f'Nu s-au găsit dispozitive cu MAC addresses în rețeaua {custom_network}. În Docker Desktop Windows, scanarea automată este limitată. Rulează scanul Windows sau folosește butonul "➕ Adaugă Calculator" pentru a adăuga manual dispozitivele cu IP și MAC cunoscute.' } + # Dacă nu s-au găsit dispozitive în autodetectare, probabil rulează în Docker + if not custom_network and not scanned: + return { + 'success': True, + 'computers': [], + 'message': 'Nu s-au găsit dispozitive în rețeaua locală. Aplicația rulează în Docker cu acces limitat la rețea. Pentru rezultate complete, rulează scanul Windows din sistemul host sau specifică manual o rețea CIDR.' + } + return {'success': True, 'computers': scanned} wol_manager = WOLManager() diff --git a/app/static/css/style.css b/app/static/css/style.css index 3c8222b..658184c 100644 --- a/app/static/css/style.css +++ b/app/static/css/style.css @@ -129,6 +129,23 @@ body { background-color: #E0E0E0; } +.computer-name { + font-weight: bold; +} + +.computer-name.online { + color: #008000; +} + +.computer-name.offline { + color: #800000; +} + +.computer-name.unknown { + color: #808000; +} + +/* Keep status class for scan modal compatibility */ .status { font-size: 14px; padding: 2px 6px; @@ -479,9 +496,45 @@ body { margin: 0; } -/* Wider scan modal for the additional column */ +.device-checkbox:disabled { + cursor: not-allowed; + opacity: 0.5; +} + +/* Styles for existing/disabled devices in scan modal */ +.scan-table tr.device-exists { + background-color: #f5f5f5; + color: #999; + opacity: 0.7; +} + +.scan-table tr.device-exists td { + color: #999; +} + +.scan-table tr.device-exists:hover { + background-color: #f5f5f5; +} + +.scan-table tr.device-exists .add-btn { + background-color: #e8e8e8; + color: #999; + cursor: not-allowed; + border: 1px solid #d4d4d4; +} + +/* Wider scan modal for the additional column with proper scrolling */ #scanModal .modal-content { width: 700px; + max-height: 90vh; + display: flex; + flex-direction: column; +} + +#scanModal .modal-body { + overflow-y: auto; + max-height: 70vh; + flex: 1; } /* Mobile responsiveness */ @@ -633,6 +686,12 @@ body { #scanModal .modal-content { width: calc(100vw - 20px); max-width: 100%; + max-height: 90vh; + } + + #scanModal .modal-body { + max-height: 60vh; + overflow-y: auto; } .no-computers { @@ -646,6 +705,23 @@ body { } .computers-table { - min-width: 600px; + min-width: 500px; + } + + .computer-name { + font-size: 15px; + max-width: 140px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .computers-table td { + padding: 6px 4px; + } + + .computers-table td:first-child { + padding: 4px 2px; + gap: 2px; } } \ No newline at end of file diff --git a/app/static/js/app.js b/app/static/js/app.js index aa54278..cf6f409 100644 --- a/app/static/js/app.js +++ b/app/static/js/app.js @@ -53,10 +53,9 @@ function displayComputers(computers) { 🗑️ - ${computer.name} - ${computer.mac} + ${computer.name} ${computer.ip || '-'} - ${computer.status} + ${computer.mac} `).join(''); } @@ -182,20 +181,23 @@ function scanNetwork() { const network = getSelectedNetwork(); const requestData = network ? { network: network } : {}; - fetch('/api/scan', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(requestData) - }) - .then(response => response.json()) - .then(result => { + // Get current computers list to compare with scan results + Promise.all([ + fetch('/api/computers').then(response => response.json()), + fetch('/api/scan', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(requestData) + }).then(response => response.json()) + ]) + .then(([existingComputers, result]) => { document.getElementById('scan-loading').style.display = 'none'; if (result.success) { if (result.computers && result.computers.length > 0) { - displayScanResults(result.computers); + displayScanResults(result.computers, existingComputers); if (result.message) { // Afișează mesajul deasupra tabelului document.getElementById('scan-results').innerHTML = @@ -228,20 +230,23 @@ function triggerWindowsScan() { showMessage('Declanșând scanul Windows...', 'success'); - fetch('/api/scan/windows', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(requestData) - }) - .then(response => response.json()) - .then(data => { + // Get current computers list to compare with scan results + Promise.all([ + fetch('/api/computers').then(response => response.json()), + fetch('/api/scan/windows', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(requestData) + }).then(response => response.json()) + ]) + .then(([existingComputers, data]) => { document.getElementById('scan-loading').style.display = 'none'; if (data.success && data.computers) { showMessage(data.message || 'Scan Windows completat cu succes!', 'success'); - displayScanResults(data.computers); + displayScanResults(data.computers, existingComputers); } else { let message = data.message || 'Scanul Windows a eșuat'; @@ -281,13 +286,16 @@ function triggerWindowsScan() { }); } -function displayScanResults(computers) { +function displayScanResults(computers, existingComputers = []) { if (computers.length === 0) { document.getElementById('scan-results').innerHTML = '
Nu s-au găsit calculatoare în rețea
'; return; } + // Create a set of existing MAC addresses for quick lookup (normalize to lowercase) + const existingMACs = new Set(existingComputers.map(comp => comp.mac.toLowerCase())); + let html = `