134 lines
5.5 KiB
Plaintext
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> |