Files
api/web/public/test-pages/shared-header.js
Eli Bosley af5ca11860 Feat/vue (#1655)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- New Features
- Introduced Docker management UI components: Overview, Logs, Console,
Preview, and Edit.
- Added responsive Card/Detail layouts with grouping, bulk actions, and
tabs.
  - New UnraidToaster component and global toaster configuration.
- Component auto-mounting improved with async loading and multi-selector
support.
- UI/UX
- Overhauled theme system (light/dark tokens, primary/orange accents)
and added theme variants.
  - Header OS version now includes integrated changelog modal.
- Registration displays warning states; multiple visual polish updates.
- API
  - CPU load now includes percentGuest and percentSteal metrics.
- Chores
  - Migrated web app to Vite; updated artifacts and manifests.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: mdatelle <mike@datelle.net>
Co-authored-by: Michael Datelle <mdatelle@icloud.com>
2025-09-08 10:04:49 -04:00

379 lines
12 KiB
JavaScript

// Shared header component for all test pages
// This provides consistent navigation and server state management
(function () {
'use strict';
// Function to inject the shared header HTML
window.injectSharedHeader = function () {
const headerHTML = `
<!-- Shared Test Header -->
<div id="shared-test-header" class="test-header">
<div class="test-header-main">
<div class="test-header-left">
<h1 class="test-header-title">
<a href="/test-pages/" style="color: inherit; text-decoration: none;">
🧪 Unraid Component Test
</a>
</h1>
<span class="test-header-subtitle" id="page-title"></span>
</div>
<div class="test-header-right">
<div class="server-state-selector">
<label>Server State:</label>
<select id="server-state-select">
<option value="default">Default (Pro)</option>
<option value="trial">Trial</option>
<option value="expired">Expired</option>
</select>
</div>
<button id="refresh-state" class="header-btn">↻ Refresh</button>
<button id="toggle-console" class="header-btn">📋 Console</button>
</div>
</div>
<!-- Navigation -->
<nav class="test-header-nav">
<a href="/test-pages/" class="nav-link">Home</a>
<a href="/test-pages/all-components.html" class="nav-link">All Components</a>
<a href="/test-pages/dashboard.html" class="nav-link">Dashboard</a>
<a href="/test-pages/settings.html" class="nav-link">Settings</a>
<a href="/test-pages/authentication.html" class="nav-link">Authentication</a>
<a href="/test-pages/os-management.html" class="nav-link">OS Management</a>
</nav>
<!-- Console (hidden by default) -->
<div id="test-console" class="test-console" style="display: none;">
<div class="console-header">
<span>Test Console</span>
<button id="clear-console" class="console-btn">Clear</button>
</div>
<div id="console-output" class="console-output">
> Console ready...
</div>
</div>
</div>
<style>
/* Global font size overrides to match Unraid's 10px base */
:root {
--text-xs: 0.75rem !important;
--text-sm: 0.875rem !important;
--text-base: 1rem !important;
--text-lg: 1.125rem !important;
--text-xl: 1.25rem !important;
--text-2xl: 1.5rem !important;
--text-3xl: 1.875rem !important;
--text-4xl: 2.25rem !important;
--text-5xl: 3rem !important;
--text-6xl: 3.75rem !important;
--text-7xl: 4.5rem !important;
--text-8xl: 6rem !important;
--text-9xl: 8rem !important;
}
/* Force Tailwind text size classes to use our values */
.text-xs { font-size: 0.75rem !important; line-height: 1rem !important; }
.text-sm { font-size: 0.875rem !important; line-height: 1.25rem !important; }
.text-base { font-size: 1rem !important; line-height: 1.5rem !important; }
.text-lg { font-size: 1.125rem !important; line-height: 1.75rem !important; }
.text-xl { font-size: 1.25rem !important; line-height: 1.75rem !important; }
.text-2xl { font-size: 1.5rem !important; line-height: 2rem !important; }
.text-3xl { font-size: 1.875rem !important; line-height: 2.25rem !important; }
.text-4xl { font-size: 2.25rem !important; line-height: 2.5rem !important; }
.text-5xl { font-size: 3rem !important; line-height: 1 !important; }
.text-6xl { font-size: 3.75rem !important; line-height: 1 !important; }
.text-7xl { font-size: 4.5rem !important; line-height: 1 !important; }
.text-8xl { font-size: 6rem !important; line-height: 1 !important; }
.text-9xl { font-size: 8rem !important; line-height: 1 !important; }
.test-header {
background: white;
border-bottom: 2px solid #e5e7eb;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
position: sticky;
top: 0;
z-index: 1000;
margin: -20px -20px 20px -20px;
}
.test-header-main {
padding: 15px 20px;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 15px;
}
.test-header-left {
display: flex;
align-items: center;
gap: 15px;
}
.test-header-title {
margin: 0;
font-size: 20px;
color: #1f2937;
}
.test-header-subtitle {
color: #6b7280;
font-size: 14px;
padding-left: 15px;
border-left: 2px solid #e5e7eb;
}
.test-header-right {
display: flex;
align-items: center;
gap: 10px;
}
.server-state-selector {
display: flex;
align-items: center;
gap: 8px;
padding-right: 15px;
border-right: 1px solid #e5e7eb;
}
.server-state-selector label {
color: #6b7280;
font-size: 14px;
}
.server-state-selector select {
padding: 5px 10px;
border: 1px solid #d1d5db;
border-radius: 4px;
background: white;
color: #1f2937;
font-size: 14px;
cursor: pointer;
}
.header-btn {
padding: 6px 12px;
background: #f3f4f6;
color: #4b5563;
border: 1px solid #d1d5db;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
transition: all 0.2s;
}
.header-btn:hover {
background: #e5e7eb;
color: #1f2937;
}
.test-header-nav {
background: #f9fafb;
padding: 0 20px;
display: flex;
gap: 0;
border-top: 1px solid #e5e7eb;
}
.nav-link {
padding: 10px 15px;
color: #6b7280;
text-decoration: none;
font-size: 14px;
border-bottom: 2px solid transparent;
transition: all 0.2s;
}
.nav-link:hover {
color: #1f2937;
background: #f3f4f6;
}
.nav-link.active {
color: #3b82f6;
border-bottom-color: #3b82f6;
}
.test-console {
background: #1f2937;
border-top: 1px solid #374151;
}
.console-header {
padding: 8px 20px;
background: #111827;
display: flex;
justify-content: space-between;
align-items: center;
color: white;
font-size: 14px;
}
.console-btn {
padding: 4px 10px;
background: #374151;
color: white;
border: none;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
}
.console-btn:hover {
background: #4b5563;
}
.console-output {
padding: 15px 20px;
color: #10b981;
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
font-size: 12px;
max-height: 200px;
overflow-y: auto;
white-space: pre-wrap;
}
@media (max-width: 768px) {
.test-header-main {
flex-direction: column;
align-items: flex-start;
}
.test-header-nav {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
}
</style>
`;
// Insert at the beginning of body
const bodyFirstChild = document.body.firstChild;
const headerContainer = document.createElement('div');
headerContainer.innerHTML = headerHTML;
document.body.insertBefore(headerContainer.firstChild, bodyFirstChild);
// Add padding to body to account for sticky header
document.body.style.paddingTop = '20px';
};
// Function to initialize header functionality
window.initializeSharedHeader = function (pageTitle) {
// Set page title
if (pageTitle) {
const titleElement = document.getElementById('page-title');
if (titleElement) {
titleElement.textContent = pageTitle;
}
}
// Mark active navigation link
const currentPath = window.location.pathname;
document.querySelectorAll('.nav-link').forEach((link) => {
if (link.getAttribute('href') === currentPath) {
link.classList.add('active');
}
});
// Console functionality
const consoleOutput = document.getElementById('console-output');
const testConsole = document.getElementById('test-console');
window.testLog = function (message, type = 'info') {
if (!consoleOutput) return;
const timestamp = new Date().toLocaleTimeString();
const prefix = type === 'error' ? '❌' : type === 'success' ? '✅' : '>';
const color = type === 'error' ? '#ef4444' : type === 'success' ? '#10b981' : '#10b981';
const logLine = document.createElement('div');
logLine.style.color = color;
logLine.textContent = `${prefix} [${timestamp}] ${message}`;
consoleOutput.appendChild(logLine);
consoleOutput.scrollTop = consoleOutput.scrollHeight;
};
// Toggle console
document.getElementById('toggle-console')?.addEventListener('click', function () {
if (testConsole) {
const isVisible = testConsole.style.display !== 'none';
testConsole.style.display = isVisible ? 'none' : 'block';
this.textContent = isVisible ? '📋 Console' : '📋 Hide';
}
});
// Clear console
document.getElementById('clear-console')?.addEventListener('click', function () {
if (consoleOutput) {
consoleOutput.innerHTML = '> Console cleared...';
}
});
// Server state selector
const stateSelect = document.getElementById('server-state-select');
if (stateSelect && window.applyServerStateToComponents) {
stateSelect.addEventListener('change', function () {
const selectedState = this.value;
window.applyServerStateToComponents(selectedState);
window.testLog(`Server state changed to: ${selectedState}`, 'success');
});
}
// Refresh state button
document.getElementById('refresh-state')?.addEventListener('click', function () {
const currentState = stateSelect?.value || 'default';
if (window.applyServerStateToComponents) {
window.applyServerStateToComponents(currentState);
window.testLog('Server state refreshed', 'success');
}
});
// Initial log
window.testLog('Test page initialized');
// Load and apply server state after a delay
setTimeout(function () {
if (window.applyServerStateToComponents) {
window.applyServerStateToComponents('default');
window.testLog('Server state applied to components', 'success');
}
}, 1000);
};
// Function to set base font size to match Unraid's 10px
window.setBaseFontSize = function () {
const baseFontStyle = document.createElement('style');
baseFontStyle.id = 'unraid-base-font';
baseFontStyle.innerHTML = `
/* Match Unraid's 10px base font size */
html {
font-size: 10px;
}
`;
document.head.appendChild(baseFontStyle);
};
// Auto-inject and initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () {
window.setBaseFontSize();
window.injectSharedHeader();
// Get page title from existing h1 or title tag
const existingTitle =
document.querySelector('h1')?.textContent ||
document.title.replace(' - Unraid Component Test', '');
window.initializeSharedHeader(existingTitle);
});
} else {
window.setBaseFontSize();
window.injectSharedHeader();
const existingTitle =
document.querySelector('h1')?.textContent ||
document.title.replace(' - Unraid Component Test', '');
window.initializeSharedHeader(existingTitle);
}
})();