Files
Server_Monitorizare_v2/templates/device_management.html

279 lines
11 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}Devices {{ app_name }}{% endblock %}
{% block page_title %}Devices{% endblock %}
{% block extra_css %}
<style>
.mac-badge { font-size: 0.75rem; letter-spacing: 0.03em; }
.sync-ok { color: #2ecc71; }
.sync-old { color: #e74c3c; }
.tbl-sm td, .tbl-sm th { padding: 0.45rem 0.6rem; font-size: 0.88rem; vertical-align: middle; }
</style>
{% endblock %}
{% block content %}
<!-- Stats row -->
<div class="row g-3 mb-4">
<div class="col-6 col-md-2">
<div class="card text-center h-100">
<div class="card-body py-3">
<h4 class="mb-0 text-success">{{ devices|selectattr('status','equalto','active')|list|length }}</h4>
<small class="text-muted">Active</small>
</div>
</div>
</div>
<div class="col-6 col-md-2">
<div class="card text-center h-100">
<div class="card-body py-3">
<h4 class="mb-0 text-danger">{{ devices|selectattr('status','equalto','inactive')|list|length }}</h4>
<small class="text-muted">Offline</small>
</div>
</div>
</div>
<div class="col-6 col-md-2">
<div class="card text-center h-100">
<div class="card-body py-3">
<h4 class="mb-0 text-warning">{{ devices|selectattr('status','equalto','maintenance')|list|length }}</h4>
<small class="text-muted">Maintenance</small>
</div>
</div>
</div>
<div class="col-6 col-md-2">
<div class="card text-center h-100">
<div class="card-body py-3">
<h4 class="mb-0 text-primary">{{ devices|length }}</h4>
<small class="text-muted">Total</small>
</div>
</div>
</div>
<div class="col-6 col-md-2">
<div class="card text-center h-100">
<div class="card-body py-3">
<h4 class="mb-0 text-info">{{ devices|selectattr('mac_address')|list|length }}</h4>
<small class="text-muted">WMT Clients</small>
</div>
</div>
</div>
<div class="col-6 col-md-2">
<a href="{{ url_for('wmt_web.update_requests', status='pending') }}" class="card text-center h-100 text-decoration-none">
<div class="card-body py-3">
<h4 class="mb-0 {% if pending_count > 0 %}text-danger{% else %}text-secondary{% endif %}">{{ pending_count }}</h4>
<small class="text-muted">Pending Requests</small>
</div>
</a>
</div>
</div>
<!-- Toolbar -->
<div class="d-flex flex-wrap gap-2 mb-3 align-items-center">
<input type="text" id="deviceSearch" class="form-control" style="max-width:340px"
placeholder="Search hostname, IP, name, MAC…" oninput="filterTable()">
<button class="btn btn-primary ms-auto" data-bs-toggle="modal" data-bs-target="#addDeviceModal">
<i class="fas fa-plus me-1"></i>Add Device
</button>
<button class="btn btn-outline-secondary" onclick="location.reload()">
<i class="fas fa-sync-alt me-1"></i>Refresh
</button>
</div>
<!-- Device table -->
<div class="card">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover tbl-sm mb-0" id="deviceTable">
<thead class="table-light">
<tr>
<th>Work Place</th>
<th>Hostname</th>
<th>IP</th>
<th>MAC Address</th>
<th>Status</th>
<th>Logs</th>
<th>Last Seen</th>
<th>Config Sync</th>
<th class="text-end">Actions</th>
</tr>
</thead>
<tbody>
{% for device in devices %}
<tr class="device-row"
data-search="{{ device.hostname|lower }} {{ device.device_ip }} {{ (device.nume_masa or '')|lower }} {{ (device.mac_address or '')|lower }}">
<td>
<strong>{{ device.nume_masa or '—' }}</strong>
</td>
<td>{{ device.hostname }}</td>
<td><code>{{ device.device_ip }}</code></td>
<td>
{% if device.mac_address %}
<code class="mac-badge">{{ device.mac_address }}</code>
{% else %}
<span class="text-muted"></span>
{% endif %}
</td>
<td>
{% if device.status == 'active' %}
<span class="badge bg-success">Active</span>
{% elif device.status == 'maintenance' %}
<span class="badge bg-warning text-dark">Maintenance</span>
{% else %}
<span class="badge bg-danger">Offline</span>
{% endif %}
</td>
<td>{{ device_log_counts.get(device.id, 0) }}</td>
<td class="text-muted">
{% if device.last_seen %}
{{ device.last_seen | local_dt }}
{% else %}—{% endif %}
</td>
<td>
{% if device.mac_address and device.config_updated_at %}
<span class="sync-ok" title="{{ device.config_updated_at | local_dt('%Y-%m-%d %H:%M:%S') }}">
<i class="fas fa-check-circle"></i>
{{ device.config_updated_at | local_dt('%m-%d %H:%M') }}
</span>
{% elif device.mac_address %}
<span class="sync-old"><i class="fas fa-clock"></i> Never</span>
{% else %}
<span class="text-muted"></span>
{% endif %}
</td>
<td class="text-end text-nowrap">
<a href="{{ url_for('main.device_detail', device_id=device.id) }}"
class="btn btn-sm btn-outline-primary py-0" title="View Details">
<i class="fas fa-eye"></i>
</a>
<a href="{{ url_for('main.logs', device_id=device.id) }}"
class="btn btn-sm btn-outline-info py-0" title="View Logs">
<i class="fas fa-list"></i>
</a>
<a href="{{ url_for('main.device_edit', device_id=device.id) }}"
class="btn btn-sm btn-outline-secondary py-0" title="Edit">
<i class="fas fa-edit"></i>
</a>
<form method="post" action="{{ url_for('main.device_delete', device_id=device.id) }}"
class="d-inline"
onsubmit="return confirm('Delete device {{ device.hostname }}? This also removes all its logs.')">
<button type="submit" class="btn btn-sm btn-outline-danger py-0" title="Delete">
<i class="fas fa-trash"></i>
</button>
</form>
</td>
</tr>
{% else %}
<tr>
<td colspan="9" class="text-center text-muted py-5">
<i class="fas fa-desktop fa-2x mb-2 d-block"></i>
No devices registered yet.
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<!-- Add Device Modal -->
<div class="modal fade" id="addDeviceModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-plus-circle me-2"></i>Add Device</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form id="addDeviceForm" onsubmit="submitAddDevice(event)">
<div class="modal-body">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">Hostname <span class="text-danger">*</span></label>
<input type="text" class="form-control" name="hostname" required placeholder="RPI-Masa-01">
</div>
<div class="col-md-6">
<label class="form-label">IP Address <span class="text-danger">*</span></label>
<input type="text" class="form-control" name="device_ip" required placeholder="192.168.1.100">
</div>
<div class="col-md-6">
<label class="form-label">Work Place <span class="text-danger">*</span></label>
<input type="text" class="form-control" name="nume_masa" required placeholder="Masa-01">
</div>
<div class="col-md-6">
<label class="form-label">MAC Address
<small class="text-muted">(WMT clients only)</small>
</label>
<input type="text" class="form-control" name="mac_address"
placeholder="b8:27:eb:aa:bb:cc"
pattern="^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$">
</div>
<div class="col-md-6">
<label class="form-label">Device Type</label>
<select class="form-select" name="device_type">
<option value="Raspberry Pi">Raspberry Pi</option>
<option value="PC">PC/Workstation</option>
<option value="Server">Server</option>
<option value="unknown" selected>Unknown</option>
</select>
</div>
<div class="col-md-6">
<label class="form-label">Status</label>
<select class="form-select" name="status">
<option value="active" selected>Active</option>
<option value="inactive">Inactive</option>
<option value="maintenance">Maintenance</option>
</select>
</div>
<div class="col-md-6">
<label class="form-label">Physical Location</label>
<input type="text" class="form-control" name="location" placeholder="Floor 2, Room 201">
</div>
<div class="col-md-6">
<label class="form-label">OS Version</label>
<input type="text" class="form-control" name="os_version" placeholder="Raspberry Pi OS 11">
</div>
<div class="col-12">
<label class="form-label">Description</label>
<textarea class="form-control" name="description" rows="2"></textarea>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary"><i class="fas fa-save me-1"></i>Add Device</button>
</div>
</form>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
function filterTable() {
const q = document.getElementById('deviceSearch').value.toLowerCase();
document.querySelectorAll('.device-row').forEach(row => {
row.style.display = row.dataset.search.includes(q) ? '' : 'none';
});
}
async function submitAddDevice(event) {
event.preventDefault();
const data = Object.fromEntries(new FormData(event.target).entries());
const btn = event.target.querySelector('[type=submit]');
btn.disabled = true;
try {
const resp = await fetch('/api/devices/add', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
});
const result = await resp.json();
if (resp.ok) { location.reload(); }
else { alert('Error: ' + (result.message || 'Unknown error')); }
} catch(e) { alert('Error: ' + e.message); }
finally { btn.disabled = false; }
}
</script>
{% endblock %}