Add cancel shutdown feature and enhance system control access methods in dashboard
This commit is contained in:
+153
-12
@@ -58,10 +58,28 @@ class ResourceDashboard {
|
||||
this.executeSystemCommand('restart');
|
||||
});
|
||||
|
||||
// Hidden system control activation - triple click on title
|
||||
document.getElementById('cancelBtn').addEventListener('click', () => {
|
||||
this.cancelSystemCommand();
|
||||
});
|
||||
|
||||
// Hidden system control activation methods
|
||||
this.setupHiddenSystemControlAccess();
|
||||
|
||||
// Close modal when clicking outside
|
||||
document.getElementById('systemControlModal').addEventListener('click', (e) => {
|
||||
if (e.target.id === 'systemControlModal') {
|
||||
this.hideSystemControlModal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setupHiddenSystemControlAccess() {
|
||||
// Method 1: Triple click on title (works on mobile)
|
||||
let clickCount = 0;
|
||||
let clickTimer = null;
|
||||
document.querySelector('h1').addEventListener('click', () => {
|
||||
const titleElement = document.querySelector('h1');
|
||||
|
||||
titleElement.addEventListener('click', () => {
|
||||
clickCount++;
|
||||
if (clickCount === 1) {
|
||||
clickTimer = setTimeout(() => {
|
||||
@@ -74,14 +92,90 @@ class ResourceDashboard {
|
||||
}
|
||||
});
|
||||
|
||||
// Close modal when clicking outside
|
||||
document.getElementById('systemControlModal').addEventListener('click', (e) => {
|
||||
if (e.target.id === 'systemControlModal') {
|
||||
this.hideSystemControlModal();
|
||||
// Method 2: Long press on title (mobile-friendly)
|
||||
let pressTimer = null;
|
||||
let pressStartTime = 0;
|
||||
|
||||
titleElement.addEventListener('touchstart', (e) => {
|
||||
pressStartTime = Date.now();
|
||||
pressTimer = setTimeout(() => {
|
||||
// Vibrate if supported (mobile feedback)
|
||||
if (navigator.vibrate) {
|
||||
navigator.vibrate(200);
|
||||
}
|
||||
this.toggleSystemControlButton();
|
||||
}, 2000); // 2 second long press
|
||||
});
|
||||
|
||||
titleElement.addEventListener('touchend', () => {
|
||||
if (pressTimer) {
|
||||
clearTimeout(pressTimer);
|
||||
pressTimer = null;
|
||||
}
|
||||
});
|
||||
|
||||
titleElement.addEventListener('touchmove', () => {
|
||||
if (pressTimer) {
|
||||
clearTimeout(pressTimer);
|
||||
pressTimer = null;
|
||||
}
|
||||
});
|
||||
|
||||
// Keyboard shortcut: Ctrl+Shift+P to toggle system control
|
||||
// Method 3: Mouse long press (desktop fallback)
|
||||
titleElement.addEventListener('mousedown', (e) => {
|
||||
if (e.button === 0) { // Left mouse button
|
||||
pressStartTime = Date.now();
|
||||
pressTimer = setTimeout(() => {
|
||||
this.toggleSystemControlButton();
|
||||
}, 2000);
|
||||
}
|
||||
});
|
||||
|
||||
titleElement.addEventListener('mouseup', () => {
|
||||
if (pressTimer) {
|
||||
clearTimeout(pressTimer);
|
||||
pressTimer = null;
|
||||
}
|
||||
});
|
||||
|
||||
titleElement.addEventListener('mouseleave', () => {
|
||||
if (pressTimer) {
|
||||
clearTimeout(pressTimer);
|
||||
pressTimer = null;
|
||||
}
|
||||
});
|
||||
|
||||
// Method 4: Secret tap sequence on the favicon
|
||||
let tapSequence = [];
|
||||
const favicon = document.querySelector('nav .fas.fa-chart-line');
|
||||
|
||||
favicon.addEventListener('click', () => {
|
||||
tapSequence.push(Date.now());
|
||||
|
||||
// Keep only last 5 taps within 3 seconds
|
||||
const now = Date.now();
|
||||
tapSequence = tapSequence.filter(time => now - time < 3000);
|
||||
|
||||
// Check for specific pattern: 5 quick taps
|
||||
if (tapSequence.length >= 5) {
|
||||
const timeDiffs = [];
|
||||
for (let i = 1; i < tapSequence.length; i++) {
|
||||
timeDiffs.push(tapSequence[i] - tapSequence[i-1]);
|
||||
}
|
||||
|
||||
// All taps should be within 200ms of each other
|
||||
const isQuickSequence = timeDiffs.every(diff => diff < 500);
|
||||
if (isQuickSequence) {
|
||||
if (navigator.vibrate) {
|
||||
navigator.vibrate([100, 50, 100]);
|
||||
}
|
||||
this.toggleSystemControlButton();
|
||||
tapSequence = [];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Method 5: Keyboard shortcut (desktop)
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.ctrlKey && e.shiftKey && e.key === 'P') {
|
||||
e.preventDefault();
|
||||
@@ -728,16 +822,32 @@ class ResourceDashboard {
|
||||
const systemButton = document.getElementById('systemControl');
|
||||
if (systemButton.classList.contains('hidden')) {
|
||||
systemButton.classList.remove('hidden');
|
||||
this.showNotification('System control unlocked! Use with caution.', 'info');
|
||||
|
||||
// Add visual feedback with pulsing effect
|
||||
systemButton.style.animation = 'pulse 1s ease-in-out 3';
|
||||
|
||||
// Show mobile-friendly notification
|
||||
this.showNotification('🔓 System control unlocked! Tap the red System button to access shutdown/restart options.', 'info');
|
||||
|
||||
// Auto-hide after 30 seconds for security
|
||||
setTimeout(() => {
|
||||
if (!systemButton.classList.contains('hidden')) {
|
||||
systemButton.classList.add('hidden');
|
||||
this.hideSystemControlModal();
|
||||
this.showNotification('System control auto-locked for security', 'info');
|
||||
}
|
||||
}, 30000);
|
||||
} else {
|
||||
systemButton.classList.add('hidden');
|
||||
systemButton.style.animation = '';
|
||||
this.hideSystemControlModal();
|
||||
this.showNotification('System control locked', 'info');
|
||||
}
|
||||
}
|
||||
|
||||
showSystemControlModal() {
|
||||
document.getElementById('systemControlModal').classList.remove('hidden');
|
||||
document.getElementById('systemTimer').value = '';
|
||||
document.getElementById('systemTimer').value = '15'; // Default 15 seconds
|
||||
document.getElementById('forceShutdown').checked = true;
|
||||
}
|
||||
|
||||
@@ -749,8 +859,8 @@ class ResourceDashboard {
|
||||
const timer = document.getElementById('systemTimer').value;
|
||||
const force = document.getElementById('forceShutdown').checked;
|
||||
|
||||
// Validate timer input
|
||||
const timerSeconds = parseInt(timer) || 0;
|
||||
// Validate timer input - default to 15 if empty
|
||||
const timerSeconds = parseInt(timer) || 15;
|
||||
if (timerSeconds < 0 || timerSeconds > 86400) {
|
||||
this.showNotification('Timer must be between 0 and 86400 seconds (24 hours)', 'error');
|
||||
return;
|
||||
@@ -772,7 +882,7 @@ class ResourceDashboard {
|
||||
} else {
|
||||
confirmMessage += ' immediately';
|
||||
}
|
||||
confirmMessage += '?';
|
||||
confirmMessage += '?\n\nNote: You can cancel this action using the Cancel button.';
|
||||
|
||||
if (!confirm(confirmMessage)) {
|
||||
return;
|
||||
@@ -804,6 +914,11 @@ class ResourceDashboard {
|
||||
setTimeout(() => {
|
||||
this.showNotification(`System ${action} initiated! Connection will be lost.`, 'info');
|
||||
}, 1000);
|
||||
} else {
|
||||
// Show reminder about cancel option
|
||||
setTimeout(() => {
|
||||
this.showNotification(`Reminder: Use the Cancel button to abort the ${action} if needed.`, 'info');
|
||||
}, 2000);
|
||||
}
|
||||
} else {
|
||||
const error = await response.text();
|
||||
@@ -815,6 +930,32 @@ class ResourceDashboard {
|
||||
}
|
||||
}
|
||||
|
||||
async cancelSystemCommand() {
|
||||
if (!confirm('Are you sure you want to cancel any pending shutdown/restart?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/resource/cancel-shutdown', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
this.showNotification(result.message, 'success');
|
||||
} else {
|
||||
const error = await response.text();
|
||||
this.showNotification(`Failed to cancel shutdown: ${error}`, 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error canceling shutdown:', error);
|
||||
this.showNotification('Error canceling shutdown command', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
hideLoading() {
|
||||
document.getElementById('loadingOverlay').style.display = 'none';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user