mirror of
https://github.com/unraid/api.git
synced 2025-12-30 13:09:52 -06:00
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Standalone web bundle with auto-mount utilities and a self-contained test page. * New responsive modal components for consistent mobile/desktop dialogs. * Header actions to copy OS/API versions. * **Improvements** * Refreshed UI styles (muted borders), accessibility and animation refinements. * Theming updates and Tailwind v4–aligned, component-scoped styles. * Runtime GraphQL endpoint override and CSRF header support. * **Bug Fixes** * Safer network fetching and improved manifest/asset loading with duplicate protection. * **Tests/Chores** * Parallel plugin tests, new extractor test suite, and updated build/test scripts. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
327 lines
12 KiB
HTML
327 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Standalone Vue Apps Test Page</title>
|
|
<style>
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
|
padding: 20px;
|
|
background: #f5f5f5;
|
|
}
|
|
.container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
}
|
|
.test-section {
|
|
background: white;
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
margin-bottom: 20px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
h1 {
|
|
color: #333;
|
|
margin-bottom: 10px;
|
|
}
|
|
h2 {
|
|
color: #666;
|
|
margin-top: 0;
|
|
margin-bottom: 15px;
|
|
font-size: 18px;
|
|
}
|
|
.status {
|
|
padding: 10px;
|
|
border-radius: 4px;
|
|
margin-bottom: 10px;
|
|
font-family: monospace;
|
|
font-size: 14px;
|
|
}
|
|
.status.loading {
|
|
background: #fff3cd;
|
|
color: #856404;
|
|
}
|
|
.status.success {
|
|
background: #d4edda;
|
|
color: #155724;
|
|
}
|
|
.status.error {
|
|
background: #f8d7da;
|
|
color: #721c24;
|
|
}
|
|
.mount-target {
|
|
padding: 20px;
|
|
background: #fafafa;
|
|
border: 2px dashed #ddd;
|
|
border-radius: 4px;
|
|
min-height: 100px;
|
|
position: relative;
|
|
}
|
|
.mount-target::before {
|
|
content: attr(data-label);
|
|
position: absolute;
|
|
top: -10px;
|
|
left: 10px;
|
|
background: white;
|
|
padding: 0 5px;
|
|
color: #999;
|
|
font-size: 12px;
|
|
}
|
|
.debug-info {
|
|
margin-top: 20px;
|
|
padding: 15px;
|
|
background: #f8f9fa;
|
|
border-radius: 4px;
|
|
font-family: monospace;
|
|
font-size: 12px;
|
|
white-space: pre-wrap;
|
|
}
|
|
.multiple-mounts {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
gap: 20px;
|
|
margin-top: 20px;
|
|
}
|
|
.test-button {
|
|
background: #007bff;
|
|
color: white;
|
|
border: none;
|
|
padding: 10px 20px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
margin-right: 10px;
|
|
}
|
|
.test-button:hover {
|
|
background: #0056b3;
|
|
}
|
|
.test-button:disabled {
|
|
background: #ccc;
|
|
cursor: not-allowed;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- Teleport target for dropdowns and modals -->
|
|
<div id="teleports"></div>
|
|
|
|
<!-- Mount point for Modals component -->
|
|
<unraid-modals></unraid-modals>
|
|
|
|
<div class="container">
|
|
<h1>🧪 Standalone Vue Apps Test Page</h1>
|
|
<div id="status" class="status loading">Loading...</div>
|
|
|
|
<!-- Test Section 1: Single Mount -->
|
|
<div class="test-section">
|
|
<h2>Test 1: Single Component Mount</h2>
|
|
<p>Testing single instance of HeaderOsVersion component</p>
|
|
<div class="mount-target" data-label="HeaderOsVersion Mount">
|
|
<unraid-header-os-version></unraid-header-os-version>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Test Section 2: Multiple Mounts -->
|
|
<div class="test-section">
|
|
<h2>Test 2: Multiple Component Mounts (Shared Pinia Store)</h2>
|
|
<p>Testing that multiple instances share the same Pinia store</p>
|
|
<div class="multiple-mounts">
|
|
<div class="mount-target" data-label="Instance 1">
|
|
<unraid-header-os-version></unraid-header-os-version>
|
|
</div>
|
|
<div class="mount-target" data-label="Instance 2">
|
|
<unraid-header-os-version></unraid-header-os-version>
|
|
</div>
|
|
<div class="mount-target" data-label="Instance 3">
|
|
<unraid-header-os-version></unraid-header-os-version>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Test Section 3: Dynamic Mount -->
|
|
<div class="test-section">
|
|
<h2>Test 3: Dynamic Component Creation</h2>
|
|
<p>Test dynamically adding components after page load</p>
|
|
<button class="test-button" id="addComponent">Add New Component</button>
|
|
<button class="test-button" id="removeComponent">Remove Last Component</button>
|
|
<button class="test-button" id="remountAll">Remount All</button>
|
|
<div id="dynamicContainer" style="margin-top: 20px;">
|
|
<!-- Dynamic components will be added here -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Test Section 4: Modal Testing -->
|
|
<div class="test-section">
|
|
<h2>Test 4: Modal Components</h2>
|
|
<p>Test modal functionality</p>
|
|
<button class="test-button" onclick="testTrialModal()">Open Trial Modal</button>
|
|
<button class="test-button" onclick="testUpdateModal()">Open Update Modal</button>
|
|
<button class="test-button" onclick="testApiKeyModal()">Open API Key Modal</button>
|
|
<div style="margin-top: 10px;">
|
|
<small>Note: Modals require proper store state to display</small>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Debug Info -->
|
|
<div class="test-section">
|
|
<h2>Debug Information</h2>
|
|
<div class="debug-info" id="debugInfo">
|
|
Waiting for initialization...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Mock configurations for local testing -->
|
|
<script>
|
|
// Set GraphQL endpoint directly to API server
|
|
// Change this to match your API server port
|
|
window.GRAPHQL_ENDPOINT = 'http://localhost:3001/graphql';
|
|
|
|
// Mock webGui path for images
|
|
window.__WEBGUI_PATH__ = '';
|
|
|
|
// Add some debug logging
|
|
window.addEventListener('DOMContentLoaded', () => {
|
|
const status = document.getElementById('status');
|
|
const debugInfo = document.getElementById('debugInfo');
|
|
|
|
// Log when scripts are loaded
|
|
const observer = new MutationObserver((mutations) => {
|
|
mutations.forEach((mutation) => {
|
|
if (mutation.type === 'childList') {
|
|
mutation.addedNodes.forEach((node) => {
|
|
if (node.nodeName === 'SCRIPT') {
|
|
console.log('Script loaded:', node.src || 'inline');
|
|
}
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
observer.observe(document.head, { childList: true });
|
|
observer.observe(document.body, { childList: true });
|
|
|
|
// Check for Vue app mounting
|
|
let checkInterval = setInterval(() => {
|
|
const mountedElements = document.querySelectorAll('unraid-header-os-version');
|
|
let mountedCount = 0;
|
|
|
|
mountedElements.forEach(el => {
|
|
if (el.innerHTML.trim() !== '') {
|
|
mountedCount++;
|
|
}
|
|
});
|
|
|
|
if (mountedCount > 0) {
|
|
status.className = 'status success';
|
|
status.textContent = `✅ Successfully mounted ${mountedCount} component(s)`;
|
|
|
|
// Update debug info
|
|
debugInfo.textContent = `
|
|
Components Found: ${mountedElements.length}
|
|
Components Mounted: ${mountedCount}
|
|
Vue Apps: ${window.mountedApps ? Object.keys(window.mountedApps).length : 0}
|
|
Pinia Store: ${window.globalPinia ? 'Initialized' : 'Not found'}
|
|
GraphQL Endpoint: ${window.GRAPHQL_ENDPOINT || 'Not configured'}
|
|
`.trim();
|
|
|
|
clearInterval(checkInterval);
|
|
}
|
|
}, 500);
|
|
|
|
// Timeout after 10 seconds
|
|
setTimeout(() => {
|
|
if (checkInterval) {
|
|
clearInterval(checkInterval);
|
|
if (status.className === 'status loading') {
|
|
status.className = 'status error';
|
|
status.textContent = '❌ Failed to mount components (timeout)';
|
|
}
|
|
}
|
|
}, 10000);
|
|
});
|
|
|
|
// Dynamic component controls
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
let dynamicCount = 0;
|
|
const dynamicContainer = document.getElementById('dynamicContainer');
|
|
|
|
document.getElementById('addComponent').addEventListener('click', () => {
|
|
dynamicCount++;
|
|
const wrapper = document.createElement('div');
|
|
wrapper.className = 'mount-target';
|
|
wrapper.setAttribute('data-label', `Dynamic Instance ${dynamicCount}`);
|
|
wrapper.style.marginBottom = '10px';
|
|
wrapper.innerHTML = '<unraid-header-os-version></unraid-header-os-version>';
|
|
dynamicContainer.appendChild(wrapper);
|
|
|
|
// Trigger mount if app is already loaded
|
|
if (window.mountVueApp) {
|
|
window.mountVueApp({
|
|
component: window.HeaderOsVersion,
|
|
selector: 'unraid-header-os-version',
|
|
appId: `dynamic-${dynamicCount}`,
|
|
});
|
|
}
|
|
});
|
|
|
|
document.getElementById('removeComponent').addEventListener('click', () => {
|
|
const lastChild = dynamicContainer.lastElementChild;
|
|
if (lastChild) {
|
|
dynamicContainer.removeChild(lastChild);
|
|
dynamicCount = Math.max(0, dynamicCount - 1);
|
|
}
|
|
});
|
|
|
|
document.getElementById('remountAll').addEventListener('click', () => {
|
|
// This would require the mount function to be exposed globally
|
|
console.log('Remounting all components...');
|
|
location.reload();
|
|
});
|
|
});
|
|
|
|
// Modal test functions
|
|
window.testTrialModal = function() {
|
|
console.log('Testing trial modal...');
|
|
if (window.globalPinia) {
|
|
const trialStore = window.globalPinia._s.get('trial');
|
|
if (trialStore) {
|
|
trialStore.trialModalVisible = true;
|
|
console.log('Trial modal triggered');
|
|
} else {
|
|
console.error('Trial store not found');
|
|
}
|
|
}
|
|
};
|
|
|
|
window.testUpdateModal = function() {
|
|
console.log('Testing update modal...');
|
|
if (window.globalPinia) {
|
|
const updateStore = window.globalPinia._s.get('updateOs');
|
|
if (updateStore) {
|
|
updateStore.updateOsModalVisible = true;
|
|
console.log('Update modal triggered');
|
|
} else {
|
|
console.error('Update store not found');
|
|
}
|
|
}
|
|
};
|
|
|
|
window.testApiKeyModal = function() {
|
|
console.log('Testing API key modal...');
|
|
if (window.globalPinia) {
|
|
const apiKeyStore = window.globalPinia._s.get('apiKey');
|
|
if (apiKeyStore) {
|
|
apiKeyStore.showCreateModal = true;
|
|
console.log('API key modal triggered');
|
|
} else {
|
|
console.error('API key store not found');
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<!-- Load the standalone app -->
|
|
<script type="module" src=".nuxt/standalone-apps/standalone-apps.js"></script>
|
|
</body>
|
|
</html> |