180 lines
5.4 KiB
HTML
180 lines
5.4 KiB
HTML
{% extends 'base.html' %}
|
|
{% block title %}User List{% endblock %}
|
|
|
|
{% block content %}
|
|
<h1 class="page-title">User List</h1>
|
|
|
|
<table class="styled-table fade-in">
|
|
<thead>
|
|
<tr>
|
|
<th>MAC Address</th>
|
|
<th>
|
|
Vendor
|
|
<button class="icon-button" onclick="refreshVendors(this)" title="Refresh Vendor">🔄</button>
|
|
</th>
|
|
<th>Description</th>
|
|
<th>Group</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="user-body">
|
|
<!-- New User Row -->
|
|
<tr class="new-row">
|
|
<td><input type="text" id="new-mac" placeholder="MAC address"></td>
|
|
<td><em>(auto)</em></td>
|
|
<td><input type="text" id="new-description" placeholder="Description"></td>
|
|
<td>
|
|
<select id="new-vlan">
|
|
<option value="">-- Select Group --</option>
|
|
{% for group in groups %}
|
|
<option value="{{ group.groupname }}">{{ group.groupname }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<button class="icon-button pulse" onclick="addUser()" title="Save User">💾</button>
|
|
<button class="icon-button" onclick="clearUserFields()" title="Reset">❌</button>
|
|
</td>
|
|
</tr>
|
|
|
|
{% for row in results %}
|
|
<tr>
|
|
<td><input type="text" value="{{ row.mac_address }}" id="mac-{{ loop.index }}" disabled></td>
|
|
<td>{{ row.vendor or 'Unknown Vendor' }}</td>
|
|
<td><input type="text" value="{{ row.description }}" id="desc-{{ loop.index }}"></td>
|
|
<td>
|
|
<select id="vlan-{{ loop.index }}">
|
|
{% for group in groups %}
|
|
<option value="{{ group.groupname }}" {% if group.groupname == row.vlan_id %}selected{% endif %}>
|
|
{{ group.groupname }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
</td>
|
|
<td>
|
|
<button class="icon-button" onclick="enableUserEdit({{ loop.index }})" title="Edit">✏️</button>
|
|
<button class="icon-button" onclick="updateUser({{ loop.index }}, '{{ row.mac_address }}')" title="Save">💾</button>
|
|
<button class="icon-button" onclick="location.reload()" title="Cancel">❌</button>
|
|
<a class="icon-button" href="/user/delete_user/{{ row.mac_address }}" onclick="saveScrollPosition()" title="Delete">🗑️</a>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
|
|
<script>
|
|
function enableUserEdit(index) {
|
|
const input = document.getElementById(`mac-${index}`);
|
|
input.disabled = false;
|
|
input.focus();
|
|
}
|
|
|
|
function clearUserFields() {
|
|
document.getElementById("new-mac").value = "";
|
|
document.getElementById("new-description").value = "";
|
|
document.getElementById("new-vlan").selectedIndex = 0;
|
|
}
|
|
|
|
function addUser() {
|
|
const mac = document.getElementById("new-mac").value;
|
|
const desc = document.getElementById("new-description").value;
|
|
const vlan = document.getElementById("new-vlan").value;
|
|
|
|
if (!mac || !vlan) {
|
|
showToast("MAC address and group are required.");
|
|
return;
|
|
}
|
|
|
|
fetch("/user/add_user", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ mac_address: mac, description: desc, vlan_id: vlan })
|
|
})
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
showToast("User added.");
|
|
setTimeout(() => location.reload(), 800);
|
|
} else {
|
|
showToast("Error: " + data.message);
|
|
}
|
|
});
|
|
}
|
|
|
|
function updateUser(index, originalMac) {
|
|
const macInput = document.getElementById(`mac-${index}`);
|
|
const desc = document.getElementById(`desc-${index}`).value;
|
|
const vlan = document.getElementById(`vlan-${index}`).value;
|
|
|
|
fetch("/user/update_user", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
body: `mac_address=${originalMac}&new_mac_address=${macInput.value}&description=${desc}&vlan_id=${vlan}`
|
|
})
|
|
.then(res => res.text())
|
|
.then(data => {
|
|
if (data === "success") {
|
|
showToast("User updated.");
|
|
setTimeout(() => location.reload(), 800);
|
|
} else {
|
|
showToast("Update failed: " + data);
|
|
}
|
|
});
|
|
}
|
|
|
|
// function refreshVendors() {
|
|
// showToast("Refreshing vendor info...");
|
|
// fetch("/user/refresh_vendors", {
|
|
// method: "POST"
|
|
// })
|
|
// .then(res => res.json())
|
|
// .then(data => {
|
|
// showToast(data.message || "Refreshed.");
|
|
// setTimeout(() => location.reload(), 1200);
|
|
// })
|
|
// .catch(() => showToast("Failed to refresh vendor info."));
|
|
// }
|
|
|
|
function refreshVendors(btn) {
|
|
btn.disabled = true;
|
|
showToast("Refreshing vendor info...");
|
|
|
|
function refreshCycle() {
|
|
fetch("/user/refresh_vendors", { method: "POST" })
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
showToast(`Updated ${data.updated} vendors`);
|
|
if (data.remaining) {
|
|
setTimeout(refreshCycle, 1500); // Pause before next batch
|
|
} else {
|
|
showToast("Vendor refresh complete.");
|
|
setTimeout(() => location.reload(), 1000);
|
|
}
|
|
} else {
|
|
showToast("Refresh failed: " + data.message);
|
|
}
|
|
})
|
|
.catch(err => {
|
|
console.error(err);
|
|
showToast("Error during vendor refresh.");
|
|
});
|
|
}
|
|
|
|
refreshCycle();
|
|
}
|
|
|
|
function saveScrollPosition() {
|
|
sessionStorage.setItem("scrollPosition", window.scrollY);
|
|
}
|
|
|
|
window.onload = function () {
|
|
const scroll = sessionStorage.getItem("scrollPosition");
|
|
if (scroll) {
|
|
window.scrollTo(0, parseInt(scroll) - 100);
|
|
sessionStorage.removeItem("scrollPosition");
|
|
}
|
|
};
|
|
</script>
|
|
{% endblock %}
|