Вот мой html:
```html
<!-- templates/admin_panel.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Admin Panel</title>
    <!-- Include CSS -->
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/admin_panel.css') }}">
    <!-- Include Google Fonts -->
    <link rel="preconnect" href="https://fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" rel="stylesheet">
    <!-- Include Socket.IO library -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.1.3/socket.io.min.js"></script>
</head>
<body>
    <!-- Sidebar -->
    <div class="sidebar">
        <h2>Admin Panel</h2>
        <button id="add-user-btn">Add New User</button>
    </div>

    <!-- Main content -->
    <div class="main-content">
        <h2>Users</h2>
        <div id="user-grid">
            <!-- User tiles will be dynamically added here -->
        </div>
    </div>

    <!-- User Creation Modal -->
    <div id="create-user-modal" class="modal">
        <div class="modal-content">
            <button class="modal-close">&times;</button>
            <h3>Add New User</h3>
            <input type="text" id="new-username" placeholder="Username">
            <input type="number" id="new-user-days" placeholder="Subscription Duration (days)">
            <button id="create-user-confirm">Create</button>
        </div>
    </div>

    <!-- Display User Key Modal -->
    <div id="user-key-modal" class="modal">
        <div class="modal-content">
            <button class="modal-close">&times;</button>
            <h3>User Key</h3>
            <input type="text" id="user-key-display" readonly>
            <button id="copy-user-key">Copy</button>
        </div>
    </div>

    <!-- Modal for Renew Subscription -->
    <div id="renew-modal" class="modal">
        <div class="modal-content">
            <button class="modal-close">&times;</button>
            <h3>Renew Subscription</h3>
            <p id="renew-user-info"></p>
            <input type="number" id="renew-days" placeholder="Days to renew">
            <button id="renew-confirm">Renew</button>
        </div>
    </div>

    <!-- Include JS -->
    <script src="{{ url_for('static', filename='js/admin_panel.js') }}"></script>
</body>
</html>
```
Вот мой js:
```js
/* static/js/admin_panel.js */
const socket = io();

const userGrid = document.getElementById('user-grid');
const addUserBtn = document.getElementById('add-user-btn');

// Modals
const createUserModal = document.getElementById('create-user-modal');
const userKeyModal = document.getElementById('user-key-modal');
const renewModal = document.getElementById('renew-modal');

let currentUserKey = null; // For actions like renew

// Event listener for "Add New User" button
addUserBtn.addEventListener('click', function() {
    createUserModal.style.display = 'block';
});

// Event listener for creating a new user
document.getElementById('create-user-confirm').addEventListener('click', function() {
    const username = document.getElementById('new-username').value.trim();
    const days = parseInt(document.getElementById('new-user-days').value);

    if (!username || isNaN(days) || days <= 0) {
        alert('Please enter a valid username and subscription duration.');
        return;
    }

    fetch('/admin/add_user', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username: username, days: days })
    })
    .then(response => response.json())
    .then(data => {
        if (data.status === 'ok') {
            // Close the create user modal
            createUserModal.style.display = 'none';
            // Open the user key modal
            document.getElementById('user-key-display').value = data.userKey;
            userKeyModal.style.display = 'block';
        } else {
            alert('Error creating user: ' + data.message);
        }
    });
});

// Event listener for copying the user key
document.getElementById('copy-user-key').addEventListener('click', function() {
    const userKeyInput = document.getElementById('user-key-display');
    userKeyInput.select();
    userKeyInput.setSelectionRange(0, 99999); // For mobile devices

    document.execCommand('copy');
});

// Close modals when clicking on the close buttons
document.querySelectorAll('.modal-close').forEach(function(btn) {
    btn.addEventListener('click', function() {
        this.parentElement.parentElement.style.display = 'none';
    });
});

// Handle clicks outside the modal content to close the modal
window.onclick = function(event) {
    if (event.target === createUserModal) {
        createUserModal.style.display = 'none';
    } else if (event.target === userKeyModal) {
        userKeyModal.style.display = 'none';
    } else if (event.target === renewModal) {
        renewModal.style.display = 'none';
    }
};

// Socket.IO event for user updates
socket.on('user_update', function(data) {
    let userTile = document.getElementById('user-' + data.key);
    if (!userTile) {
        userTile = document.createElement('div');
        userTile.id = 'user-' + data.key;
        userTile.className = 'user-tile';
        userGrid.appendChild(userTile);
    }

    // Determine user status classes
    let statusClass = '';
    if (data.is_banned) {
        statusClass = 'banned';
    } else if (data.is_stopped) {
        statusClass = 'stopped';
    } else if (new Date(data.subscription_expires) < new Date()) {
        statusClass = 'expired';
    } else if (data.is_connected) {
        statusClass = 'connected';
    } else {
        statusClass = 'disconnected';
    }

    userTile.className = 'user-tile ' + statusClass;

    userTile.innerHTML = `
        <h3>${data.username}</h3>
        <p>IP: <span class="user-ip">${data.ip || 'N/A'}</span></p>
        <p>Connected: <span class="user-connected">${data.is_connected}</span></p>
        <p>Expires: <span class="user-expires">${formatDate(data.subscription_expires)}</span></p>
        <p>Current URL: <span class="user-url">${data.current_url || 'N/A'}</span></p>
        <div class="user-actions">
            <button class="ban-button" onclick="banUser('${data.key}')">${data.is_banned ? 'Unban' : 'Ban'}</button>
            <button class="stop-button" onclick="stopUser('${data.key}')">${data.is_stopped ? 'Unstop' : 'Stop'}</button>
            <button class="copy-button" onclick="copyUserKey('${data.key}')">Copy Key</button>
            <button class="renew-button" onclick="openRenewModal('${data.key}', '${data.username}')">Renew</button>
            <button class="delete-button" onclick="deleteUser('${data.key}')">Delete</button>
        </div>
    `;
});

// Handle 'user_deleted' event
socket.on('user_deleted', function(data) {
    let userTile = document.getElementById('user-' + data.key);
    if (userTile) {
        userTile.remove();
    }
});

// Function to format date
function formatDate(dateString) {
    const date = new Date(dateString);
    return date.toLocaleString();
}

// Functions for user actions
function banUser(userKey) {
    fetch('/admin/action', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'ban', userKey: userKey })
    });
}

function stopUser(userKey) {
    fetch('/admin/action', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'stop', userKey: userKey })
    });
}

function deleteUser(userKey) {
    if (confirm('Are you sure you want to delete this user? This action cannot be undone.')) {
        fetch('/admin/action', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ action: 'delete', userKey: userKey })
        });
    }
}

function copyUserKey(userKey) {
    const tempInput = document.createElement('input');
    tempInput.value = userKey;
    document.body.appendChild(tempInput);
    tempInput.select();
    tempInput.setSelectionRange(0, 99999); // For mobile devices
    document.execCommand('copy');
    document.body.removeChild(tempInput);
}

function openRenewModal(userKey, username) {
    currentUserKey = userKey;
    renewModal.style.display = 'block';
    document.getElementById('renew-user-info').textContent = `User: ${username}`;
    document.getElementById('renew-days').value = '';
}

// Renew subscription
document.getElementById('renew-confirm').addEventListener('click', function() {
    const days = parseInt(document.getElementById('renew-days').value);
    if (isNaN(days)) {
        alert('Please enter a valid number of days.');
        return;
    }
    fetch('/admin/action', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'renew', userKey: currentUserKey, days: days })
    })
    .then(response => response.json())
    .then(data => {
        if (data.status === 'ok') {
            renewModal.style.display = 'none';
        } else {
            alert('Error renewing subscription: ' + data.message);
        }
    });
});
```
Сделай так, чтобы как только мы вносили обновление в пользователя, например, изменяли дату подписки, то, если успешно, tile пользователя резко станет зеленым и плавно превратится обратно в белый(маргнет). Если же не успешно, то маргнет красным.