Files
2025-11-23 19:57:05 +01:00

134 lines
5.5 KiB
Plaintext

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DevBench Manager</title>
<link rel="icon" type="image/png" href="/images/tbm-icon.png">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<link href="/css/style.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="/">
<img src="/images/tbm-icon.png" alt="TBM" class="tbm-icon me-2" style="height: 32px; width: 32px;">
<img src="/images/nabd-logo-white.svg" alt="NABD Solutions" class="company-logo">
DevBench Manager
</a>
<div class="navbar-nav ms-auto">
<% if (typeof username !== 'undefined') { %>
<a class="nav-link" href="/help" title="Help">
<i class="fas fa-question-circle"></i> Help
</a>
<span class="navbar-text me-3">Welcome, <%= username %></span>
<a class="nav-link" href="/logout">
<i class="fas fa-sign-out-alt"></i> Logout
</a>
<% } %>
</div>
</div>
</nav>
<div class="container mt-4">
<%- body %>
</div>
<!-- Theme Toggle Button -->
<button class="theme-toggle" id="themeToggle" title="Toggle Dark/Light Theme">
<i class="fas fa-moon" id="themeIcon"></i>
</button>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
// Theme Toggle Functionality
const themeToggle = document.getElementById('themeToggle');
const themeIcon = document.getElementById('themeIcon');
const body = document.body;
// Load saved theme preference
const savedTheme = localStorage.getItem('theme') || 'light';
if (savedTheme === 'dark') {
body.classList.add('dark-theme');
themeIcon.classList.remove('fa-moon');
themeIcon.classList.add('fa-sun');
}
// Toggle theme
themeToggle.addEventListener('click', () => {
body.classList.toggle('dark-theme');
if (body.classList.contains('dark-theme')) {
themeIcon.classList.remove('fa-moon');
themeIcon.classList.add('fa-sun');
localStorage.setItem('theme', 'dark');
} else {
themeIcon.classList.remove('fa-sun');
themeIcon.classList.add('fa-moon');
localStorage.setItem('theme', 'light');
}
});
</script>
<script>
// WebSocket connection for real-time updates
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const ws = new WebSocket(`${protocol}//${window.location.host}`);
ws.onopen = function() {
console.log('WebSocket connected');
// Register this connection with user ID if available
<% if (typeof username !== 'undefined') { %>
// Get user ID from session (we'll need to pass this from server)
fetch('/api/user-info')
.then(response => response.json())
.then(data => {
if (data.userId) {
ws.send(JSON.stringify({
type: 'register',
userId: data.userId
}));
console.log('Registered WebSocket for user:', data.userId);
}
})
.catch(error => console.error('Error getting user info:', error));
<% } %>
};
ws.onmessage = function(event) {
const message = JSON.parse(event.data);
console.log('WebSocket message received:', message);
handleWebSocketMessage(message);
};
ws.onerror = function(error) {
console.error('WebSocket error:', error);
};
ws.onclose = function() {
console.log('WebSocket disconnected');
};
function handleWebSocketMessage(message) {
if (message.type === 'script_output') {
const logElement = document.getElementById(`log-${message.devbenchId}`);
if (logElement) {
logElement.textContent += message.data;
logElement.scrollTop = logElement.scrollHeight;
}
} else if (message.type === 'script_complete') {
console.log('Script completed with exit code:', message.exitCode);
setTimeout(() => {
location.reload();
}, 3000);
} else if (message.type === 'status_update') {
const statusElement = document.getElementById(`status-${message.devbenchId}`);
if (statusElement) {
statusElement.className = `badge bg-${message.status === 'active' ? 'success' : 'danger'}`;
statusElement.textContent = message.status;
}
}
}
</script>
</body>
</html>