wifi-pass/public/admin.html
gregorio.petruzzi c294e2faf1 add: features
2025-12-17 13:22:16 +01:00

248 lines
15 KiB
HTML

<!DOCTYPE html>
<html lang="it" class="">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ADM WiFi Admin Panel</title>
<link rel="shortcut icon" href="/favicon/favicon.png">
<script>
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark') { document.documentElement.classList.add('dark'); }
</script>
<script src="./css/tailwind.min.js"></script>
<script>
tailwind.config = { darkMode: 'class' }
</script>
<style>
* { transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease; }
</style>
</head>
<body class="bg-gradient-to-br from-blue-50 to-blue-100 dark:from-gray-900 dark:to-gray-800 min-h-screen">
<div class="container mx-auto px-4 py-4 sm:py-8">
<div class="bg-white dark:bg-gray-800 shadow-xl rounded-lg p-4 sm:p-6">
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-6 gap-4">
<div class="flex items-center space-x-3">
<svg class="w-6 h-6 sm:w-8 sm:h-8 text-blue-500 dark:text-blue-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5.121 17.804A13.937 13.937 0 0112 16c2.5 0 4.847.655 6.879 1.804M15 10a3 3 0 11-6 0 3 3 0 016 0zm6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<h1 class="text-xl sm:text-2xl font-bold text-gray-800 dark:text-gray-100">Pannello Amministratore</h1>
</div>
<div class="flex items-center space-x-2 sm:space-x-4 w-full sm:w-auto">
<span id="totalUsers" class="text-gray-600 dark:text-gray-400 text-xs sm:text-sm"></span>
<button
onclick="goToManagement()"
class="bg-purple-500 hover:bg-purple-700 dark:bg-purple-600 dark:hover:bg-purple-700 text-white font-bold py-2 px-3 sm:px-4 rounded focus:outline-none focus:shadow-outline transition duration-300 text-xs sm:text-sm"
title="Gestione Connessioni"
>
<svg class="w-4 h-4 sm:w-5 sm:h-5 inline-block" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0"></path>
</svg>
<span class="hidden sm:inline ml-1">Connessioni</span>
</button>
<button
id="themeToggle"
class="bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 text-gray-800 dark:text-gray-200 font-semibold py-2 px-3 sm:px-4 rounded focus:outline-none transition duration-300 text-xs sm:text-sm"
>
<svg class="w-4 h-4 sm:w-5 sm:h-5 hidden dark:block" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"></path>
</svg>
<svg class="w-4 h-4 sm:w-5 sm:h-5 block dark:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"></path>
</svg>
</button>
<button onclick="window.location.href='index.html'" class="bg-gray-500 hover:bg-gray-700 dark:bg-gray-600 dark:hover:bg-gray-700 text-white font-bold py-2 px-3 sm:px-4 rounded focus:outline-none focus:shadow-outline transition duration-300 text-xs sm:text-sm">
Logout
</button>
</div>
</div>
<!-- Filtri e Ricerca -->
<div class="mb-6 flex flex-col gap-3">
<div class="w-full">
<input
type="text"
id="searchInput"
placeholder="Cerca per nome, cognome o email..."
class="w-full px-3 sm:px-4 py-2 border dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 text-sm sm:text-base"
>
</div>
<div class="flex flex-wrap gap-2">
<button onclick="filterUsers('all')" class="flex-1 sm:flex-none bg-blue-500 hover:bg-blue-700 dark:bg-blue-600 dark:hover:bg-blue-700 text-white font-bold py-2 px-3 sm:px-4 rounded focus:outline-none focus:shadow-outline transition duration-300 text-xs sm:text-sm">
Tutti
</button>
<button onclick="filterUsers('authorized')" class="flex-1 sm:flex-none bg-green-500 hover:bg-green-700 dark:bg-green-600 dark:hover:bg-green-700 text-white font-bold py-2 px-3 sm:px-4 rounded focus:outline-none focus:shadow-outline transition duration-300 text-xs sm:text-sm">
Autorizzati
</button>
<button onclick="filterUsers('unauthorized')" class="flex-1 sm:flex-none bg-red-500 hover:bg-red-700 dark:bg-red-600 dark:hover:bg-red-700 text-white font-bold py-2 px-3 sm:px-4 rounded focus:outline-none focus:shadow-outline transition duration-300 text-xs sm:text-sm">
Non Autorizzati
</button>
</div>
</div>
<!-- Tabella Utenti -->
<div class="overflow-x-auto -mx-4 sm:mx-0">
<table class="min-w-full bg-white dark:bg-gray-800">
<thead>
<tr class="bg-gray-100 dark:bg-gray-700">
<th class="px-3 sm:px-6 py-2 sm:py-3 border-b-2 border-gray-200 dark:border-gray-600 text-left text-xs font-semibold text-gray-600 dark:text-gray-300 uppercase tracking-wider">
Nome
</th>
<th class="px-3 sm:px-6 py-2 sm:py-3 border-b-2 border-gray-200 dark:border-gray-600 text-left text-xs font-semibold text-gray-600 dark:text-gray-300 uppercase tracking-wider hidden sm:table-cell">
Cognome
</th>
<th class="px-3 sm:px-6 py-2 sm:py-3 border-b-2 border-gray-200 dark:border-gray-600 text-left text-xs font-semibold text-gray-600 dark:text-gray-300 uppercase tracking-wider">
Email
</th>
<th class="px-3 sm:px-6 py-2 sm:py-3 border-b-2 border-gray-200 dark:border-gray-600 text-left text-xs font-semibold text-gray-600 dark:text-gray-300 uppercase tracking-wider hidden md:table-cell">
Username
</th>
<th class="px-3 sm:px-6 py-2 sm:py-3 border-b-2 border-gray-200 dark:border-gray-600 text-left text-xs font-semibold text-gray-600 dark:text-gray-300 uppercase tracking-wider">
Stato
</th>
<th class="px-3 sm:px-6 py-2 sm:py-3 border-b-2 border-gray-200 dark:border-gray-600 text-center text-xs font-semibold text-gray-600 dark:text-gray-300 uppercase tracking-wider">
Azioni
</th>
</tr>
</thead>
<tbody id="usersTableBody">
<!-- La tabella verrà popolata dinamicamente -->
</tbody>
</table>
</div>
</div>
</div>
<script>
// Funzione per caricare gli utenti dal server
async function loadUsers() {
try {
const response = await fetch('/api/users');
const users = await response.json();
displayUsers(users);
updateTotalUsers(users.length);
} catch (error) {
console.error('Errore nel caricamento degli utenti:', error);
}
}
// Funzione per visualizzare gli utenti nella tabella
function displayUsers(users) {
const tableBody = document.getElementById('usersTableBody');
tableBody.innerHTML = '';
users.forEach(user => {
const row = document.createElement('tr');
row.className = 'hover:bg-gray-50 dark:hover:bg-gray-700';
row.innerHTML = `
<td class="px-3 sm:px-6 py-3 sm:py-4 whitespace-nowrap border-b border-gray-200 dark:border-gray-600">
<div class="text-xs sm:text-sm text-gray-900 dark:text-gray-100">${user.nome}</div>
</td>
<td class="px-3 sm:px-6 py-3 sm:py-4 whitespace-nowrap border-b border-gray-200 dark:border-gray-600 hidden sm:table-cell">
<div class="text-xs sm:text-sm text-gray-900 dark:text-gray-100">${user.cognome}</div>
</td>
<td class="px-3 sm:px-6 py-3 sm:py-4 whitespace-nowrap border-b border-gray-200 dark:border-gray-600">
<div class="text-xs sm:text-sm text-gray-900 dark:text-gray-100">${user.email}</div>
</td>
<td class="px-3 sm:px-6 py-3 sm:py-4 whitespace-nowrap border-b border-gray-200 dark:border-gray-600 hidden md:table-cell">
<div class="text-xs sm:text-sm text-gray-900 dark:text-gray-100">${user.username}</div>
</td>
<td class="px-3 sm:px-6 py-3 sm:py-4 whitespace-nowrap border-b border-gray-200 dark:border-gray-600">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${user.authorized ? 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400' : 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400'}">
${user.authorized ? 'Autorizzato' : 'Non Autorizzato'}
</span>
</td>
<td class="px-3 sm:px-6 py-3 sm:py-4 whitespace-nowrap border-b border-gray-200 dark:border-gray-600 text-center">
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" class="sr-only peer" ${user.authorized ? 'checked' : ''} onchange="toggleAuthorization('${user.username}', this.checked)">
<div class="w-11 h-6 bg-gray-200 dark:bg-gray-600 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600 dark:peer-checked:bg-blue-500"></div>
</label>
</td>
`;
tableBody.appendChild(row);
});
}
// Funzione per aggiornare il conteggio totale degli utenti
function updateTotalUsers(count) {
document.getElementById('totalUsers').textContent = `Totale utenti: ${count}`;
}
// Funzione per filtrare gli utenti
async function filterUsers(filter) {
try {
const response = await fetch('/api/users');
const users = await response.json();
let filteredUsers = users;
if (filter === 'authorized') {
filteredUsers = users.filter(user => user.authorized);
} else if (filter === 'unauthorized') {
filteredUsers = users.filter(user => !user.authorized);
}
displayUsers(filteredUsers);
updateTotalUsers(filteredUsers.length);
} catch (error) {
console.error('Errore nel filtraggio degli utenti:', error);
}
}
// Funzione per cambiare lo stato di autorizzazione
async function toggleAuthorization(username, authorized) {
try {
const endpoint = authorized ? `/api/users/${username}/authorize` : `/api/users/${username}/deauthorize`;
const response = await fetch(endpoint, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
}
});
if (response.ok) {
// Ricaricare la lista degli utenti per mostrare lo stato aggiornato
loadUsers();
} else {
console.error('Errore nell\'aggiornamento dell\'autorizzazione');
}
} catch (error) {
console.error('Errore nella modifica dell\'autorizzazione:', error);
}
}
// Event listener per la ricerca
document.getElementById('searchInput').addEventListener('input', function(e) {
const searchTerm = e.target.value.toLowerCase();
const rows = document.querySelectorAll('#usersTableBody tr');
rows.forEach(row => {
const text = row.textContent.toLowerCase();
row.style.display = text.includes(searchTerm) ? '' : 'none';
});
});
// Carica gli utenti quando la pagina viene caricata
document.addEventListener('DOMContentLoaded', () => {
loadUsers();
// Dark mode toggle
const themeToggle = document.getElementById('themeToggle');
const htmlElement = document.documentElement;
themeToggle.addEventListener('click', () => {
htmlElement.classList.toggle('dark');
const newTheme = htmlElement.classList.contains('dark') ? 'dark' : 'light';
localStorage.setItem('theme', newTheme);
});
});
// Funzione per andare alla pagina di management
function goToManagement() {
const token = localStorage.getItem('adminToken');
if (token) {
window.location.href = 'management.html';
} else {
alert('Devi essere autenticato come admin per accedere a questa pagina');
window.location.href = 'login_admin.html';
}
}
</script>
</body>
</html>