bmc_hub/app/modules/hardware/templates/eset_import.html

294 lines
11 KiB
HTML
Raw Normal View History

{% extends "shared/frontend/base.html" %}
{% block title %}ESET Import - Hardware - BMC Hub{% endblock %}
{% block extra_css %}
<style>
.page-header {
margin-bottom: 2rem;
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
flex-wrap: wrap;
}
.section-card {
background: var(--bg-card);
border-radius: 12px;
border: 1px solid rgba(0,0,0,0.1);
padding: 1.5rem;
margin-bottom: 2rem;
}
.table thead th {
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.02em;
color: var(--text-secondary);
}
.device-uuid {
max-width: 240px;
word-break: break-all;
}
.status-pill {
display: inline-flex;
align-items: center;
gap: 0.35rem;
font-size: 0.85rem;
padding: 0.35rem 0.6rem;
border-radius: 999px;
background: var(--accent-light);
color: var(--accent);
}
.contact-results {
max-height: 220px;
overflow: auto;
border: 1px solid rgba(0,0,0,0.1);
border-radius: 8px;
background: var(--bg-body);
}
.contact-result {
padding: 0.6rem 0.8rem;
cursor: pointer;
display: flex;
justify-content: space-between;
gap: 1rem;
}
.contact-result:hover {
background: var(--accent-light);
}
.contact-muted {
color: var(--text-secondary);
font-size: 0.85rem;
}
</style>
{% endblock %}
{% block content %}
<div class="container-fluid" style="margin-top: 2rem; max-width: 1400px;">
<div class="page-header">
<h1>⬇️ ESET Import</h1>
<div class="d-flex gap-2">
<a href="/hardware/eset" class="btn btn-outline-secondary">ESET Oversigt</a>
<a href="/hardware" class="btn btn-outline-secondary">Tilbage til hardware</a>
</div>
</div>
<div class="section-card">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">
<div class="status-pill" id="deviceStatus">Ingen data indlaest</div>
<button class="btn btn-primary" onclick="loadDevices()">Hent devices</button>
</div>
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead>
<tr>
<th>Navn</th>
<th>Serial</th>
<th>Gruppe</th>
<th>Device UUID</th>
<th>Handling</th>
</tr>
</thead>
<tbody id="devicesTable">
<tr>
<td colspan="5" class="text-center text-muted">Klik "Hent devices" for at hente ESET-listen.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Import Modal -->
<div class="modal fade" id="esetImportModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Import fra ESET</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Device UUID</label>
<input type="text" class="form-control" id="importDeviceUuid" readonly>
</div>
<div class="mb-3">
<label class="form-label">Find kontakt</label>
<div class="input-group mb-2">
<input type="text" class="form-control" id="contactSearch" placeholder="Navn eller email">
<button class="btn btn-outline-secondary" type="button" onclick="searchContacts()">Sog</button>
</div>
<div id="contactResults" class="contact-results"></div>
</div>
<div class="mb-3">
<label class="form-label">Valgt kontakt</label>
<input type="text" class="form-control" id="selectedContact" placeholder="Ingen valgt" readonly>
</div>
<div id="importStatus" class="contact-muted"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Annuller</button>
<button type="button" class="btn btn-primary" onclick="importDevice()">Importer</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
const devicesTable = document.getElementById('devicesTable');
const deviceStatus = document.getElementById('deviceStatus');
const importModal = new bootstrap.Modal(document.getElementById('esetImportModal'));
let selectedContactId = null;
function parseDevices(payload) {
if (Array.isArray(payload)) return payload;
if (!payload || typeof payload !== 'object') return [];
return payload.devices || payload.items || payload.results || payload.data || [];
}
function getField(device, keys) {
for (const key of keys) {
if (device[key]) return device[key];
}
return '';
}
function renderDevices(devices) {
if (!devices.length) {
devicesTable.innerHTML = '<tr><td colspan="5" class="text-center text-muted">Ingen devices fundet.</td></tr>';
return;
}
devicesTable.innerHTML = devices.map(device => {
const uuid = getField(device, ['deviceUuid', 'uuid', 'id']);
const name = getField(device, ['displayName', 'deviceName', 'name']);
const serial = getField(device, ['serialNumber', 'serial', 'serial_number']);
const group = getField(device, ['parentGroup', 'groupPath', 'group', 'path']);
return `
<tr>
<td>${name || '-'}</td>
<td>${serial || '-'}</td>
<td>${group || '-'}</td>
<td class="device-uuid">${uuid || '-'}</td>
<td>
<button class="btn btn-sm btn-outline-primary" onclick="openImportModal('${uuid || ''}')">Importer</button>
</td>
</tr>
`;
}).join('');
}
async function loadDevices() {
deviceStatus.textContent = 'Henter...';
try {
const response = await fetch('/api/v1/hardware/eset/devices');
if (!response.ok) {
const err = await response.text();
throw new Error(err || 'Request failed');
}
const data = await response.json();
const devices = parseDevices(data);
deviceStatus.textContent = `${devices.length} devices hentet`;
renderDevices(devices);
} catch (err) {
deviceStatus.textContent = 'Fejl ved hentning';
devicesTable.innerHTML = `<tr><td colspan="5" class="text-center text-danger">${err.message}</td></tr>`;
}
}
function openImportModal(uuid) {
document.getElementById('importDeviceUuid').value = uuid;
document.getElementById('contactSearch').value = '';
document.getElementById('contactResults').innerHTML = '';
document.getElementById('selectedContact').value = '';
document.getElementById('importStatus').textContent = '';
selectedContactId = null;
importModal.show();
}
async function searchContacts() {
const query = document.getElementById('contactSearch').value.trim();
const results = document.getElementById('contactResults');
if (!query) {
results.innerHTML = '<div class="p-2 text-muted">Indtast soegning.</div>';
return;
}
results.innerHTML = '<div class="p-2 text-muted">Soeger...</div>';
try {
const response = await fetch(`/api/v1/contacts?search=${encodeURIComponent(query)}&limit=20`);
if (!response.ok) {
const err = await response.text();
throw new Error(err || 'Request failed');
}
const data = await response.json();
const contacts = data.contacts || [];
if (!contacts.length) {
results.innerHTML = '<div class="p-2 text-muted">Ingen kontakter fundet.</div>';
return;
}
results.innerHTML = contacts.map(c => {
const name = `${c.first_name || ''} ${c.last_name || ''}`.trim();
const company = (c.company_names || []).join(', ');
return `
<div class="contact-result" onclick="selectContact(${c.id}, '${name.replace(/'/g, "\\'")}', '${company.replace(/'/g, "\\'")}')">
<div>
<div>${name || 'Ukendt'}</div>
<div class="contact-muted">${c.email || ''}</div>
</div>
<div class="contact-muted">${company || '-'}</div>
</div>
`;
}).join('');
} catch (err) {
results.innerHTML = `<div class="p-2 text-danger">${err.message}</div>`;
}
}
function selectContact(id, name, company) {
selectedContactId = id;
const label = company ? `${name} (${company})` : name;
document.getElementById('selectedContact').value = label;
document.getElementById('contactResults').innerHTML = '';
}
async function importDevice() {
const uuid = document.getElementById('importDeviceUuid').value.trim();
const statusEl = document.getElementById('importStatus');
statusEl.textContent = 'Importer...';
try {
const payload = { device_uuid: uuid };
if (selectedContactId) {
payload.contact_id = selectedContactId;
}
const response = await fetch('/api/v1/hardware/eset/import', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!response.ok) {
const err = await response.text();
throw new Error(err || 'Request failed');
}
const data = await response.json();
statusEl.textContent = `Importeret hardware #${data.id}`;
} catch (err) {
statusEl.textContent = `Fejl: ${err.message}`;
}
}
</script>
{% endblock %}