| <!DOCTYPE html>
|
| <html lang="en">
|
| <head>
|
| <meta charset="UTF-8">
|
| <meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| <title>AEGIS BIO DIGITAL LAB 10 - AR Portal</title>
|
| <meta name="description" content="AEGIS Bio Digital Lab 10 Augmented Reality Portal Dashboard">
|
|
|
|
|
| <script src="https://unpkg.com/lucide@latest/dist/umd/lucide.js"></script>
|
|
|
| <style>
|
| * {
|
| margin: 0;
|
| padding: 0;
|
| box-sizing: border-box;
|
| }
|
|
|
| body {
|
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
| background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
|
| color: white;
|
| min-height: 100vh;
|
| overflow-x: hidden;
|
| }
|
|
|
| .header {
|
| background: rgba(0, 0, 0, 0.4);
|
| padding: 1.5rem 2rem;
|
| backdrop-filter: blur(15px);
|
| border-bottom: 1px solid rgba(0, 255, 255, 0.3);
|
| position: sticky;
|
| top: 0;
|
| z-index: 100;
|
| }
|
|
|
| .header h1 {
|
| font-size: 2.5rem;
|
| margin-bottom: 0.5rem;
|
| background: linear-gradient(45deg, #00FFFF, #0080FF);
|
| -webkit-background-clip: text;
|
| -webkit-text-fill-color: transparent;
|
| background-clip: text;
|
| text-align: center;
|
| animation: pulse 2s infinite;
|
| }
|
|
|
| .header .subtitle {
|
| font-size: 1.2rem;
|
| text-align: center;
|
| opacity: 0.9;
|
| color: #E3F2FD;
|
| }
|
|
|
| .status-bar {
|
| display: flex;
|
| justify-content: center;
|
| align-items: center;
|
| gap: 2rem;
|
| margin-top: 1rem;
|
| flex-wrap: wrap;
|
| }
|
|
|
| .status-item {
|
| display: flex;
|
| align-items: center;
|
| gap: 0.5rem;
|
| font-size: 0.9rem;
|
| opacity: 0.8;
|
| }
|
|
|
| .status-online {
|
| color: #4CAF50;
|
| }
|
|
|
| .status-offline {
|
| color: #f44336;
|
| }
|
|
|
| .main-content {
|
| padding: 2rem;
|
| max-width: 1400px;
|
| margin: 0 auto;
|
| }
|
|
|
| .hero-section {
|
| text-align: center;
|
| margin-bottom: 3rem;
|
| }
|
|
|
| .hero-section h2 {
|
| font-size: 2rem;
|
| margin-bottom: 1rem;
|
| color: #00FFFF;
|
| }
|
|
|
| .hero-section p {
|
| font-size: 1.1rem;
|
| opacity: 0.9;
|
| max-width: 600px;
|
| margin: 0 auto;
|
| line-height: 1.6;
|
| }
|
|
|
| .portals-grid {
|
| display: grid;
|
| grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
| gap: 1.5rem;
|
| margin-bottom: 3rem;
|
| }
|
|
|
| .portal-card {
|
| background: rgba(0, 0, 0, 0.4);
|
| border: 1px solid rgba(0, 255, 255, 0.3);
|
| border-radius: 15px;
|
| padding: 1.5rem;
|
| text-align: center;
|
| backdrop-filter: blur(10px);
|
| transition: all 0.3s ease;
|
| cursor: pointer;
|
| position: relative;
|
| overflow: hidden;
|
| }
|
|
|
| .portal-card::before {
|
| content: '';
|
| position: absolute;
|
| top: 0;
|
| left: -100%;
|
| width: 100%;
|
| height: 100%;
|
| background: linear-gradient(90deg, transparent, rgba(0, 255, 255, 0.1), transparent);
|
| transition: left 0.5s;
|
| }
|
|
|
| .portal-card:hover::before {
|
| left: 100%;
|
| }
|
|
|
| .portal-card:hover {
|
| transform: translateY(-5px);
|
| border-color: rgba(0, 255, 255, 0.6);
|
| box-shadow: 0 10px 30px rgba(0, 255, 255, 0.2);
|
| }
|
|
|
| .portal-icon {
|
| width: 60px;
|
| height: 60px;
|
| margin: 0 auto 1rem;
|
| border-radius: 12px;
|
| display: flex;
|
| align-items: center;
|
| justify-content: center;
|
| position: relative;
|
| z-index: 1;
|
| }
|
|
|
| .portal-card h3 {
|
| font-size: 1.2rem;
|
| margin-bottom: 0.5rem;
|
| color: white;
|
| position: relative;
|
| z-index: 1;
|
| }
|
|
|
| .portal-card p {
|
| font-size: 0.9rem;
|
| opacity: 0.8;
|
| margin-bottom: 1rem;
|
| position: relative;
|
| z-index: 1;
|
| }
|
|
|
| .launch-btn {
|
| display: inline-flex;
|
| align-items: center;
|
| gap: 0.5rem;
|
| padding: 0.5rem 1rem;
|
| background: rgba(0, 255, 255, 0.2);
|
| border: 1px solid rgba(0, 255, 255, 0.4);
|
| border-radius: 25px;
|
| color: #00FFFF;
|
| text-decoration: none;
|
| font-size: 0.9rem;
|
| font-weight: 500;
|
| transition: all 0.3s ease;
|
| position: relative;
|
| z-index: 1;
|
| }
|
|
|
| .launch-btn:hover {
|
| background: rgba(0, 255, 255, 0.3);
|
| color: white;
|
| }
|
|
|
|
|
| .color-blue { background: linear-gradient(135deg, #2196F3, #1976D2); }
|
| .color-green { background: linear-gradient(135deg, #4CAF50, #388E3C); }
|
| .color-purple { background: linear-gradient(135deg, #9C27B0, #7B1FA2); }
|
| .color-yellow { background: linear-gradient(135deg, #FF9800, #F57C00); }
|
| .color-red { background: linear-gradient(135deg, #f44336, #D32F2F); }
|
| .color-orange { background: linear-gradient(135deg, #FF5722, #E64A19); }
|
| .color-indigo { background: linear-gradient(135deg, #3F51B5, #303F9F); }
|
| .color-pink { background: linear-gradient(135deg, #E91E63, #C2185B); }
|
| .color-teal { background: linear-gradient(135deg, #009688, #00796B); }
|
| .color-cyan { background: linear-gradient(135deg, #00BCD4, #0097A7); }
|
| .color-emerald { background: linear-gradient(135deg, #4CAF50, #2E7D32); }
|
|
|
| .qr-section {
|
| background: rgba(0, 0, 0, 0.3);
|
| border: 1px solid rgba(0, 255, 255, 0.3);
|
| border-radius: 15px;
|
| padding: 2rem;
|
| text-align: center;
|
| margin-bottom: 2rem;
|
| }
|
|
|
| .qr-section h3 {
|
| color: #00FFFF;
|
| margin-bottom: 1rem;
|
| font-size: 1.5rem;
|
| }
|
|
|
| .qr-code-container {
|
| margin: 1rem 0;
|
| }
|
|
|
| .qr-code-container img {
|
| max-width: 200px;
|
| border-radius: 10px;
|
| border: 2px solid rgba(0, 255, 255, 0.3);
|
| }
|
|
|
| .api-section {
|
| background: rgba(0, 0, 0, 0.3);
|
| border: 1px solid rgba(0, 255, 255, 0.3);
|
| border-radius: 15px;
|
| padding: 2rem;
|
| margin-bottom: 2rem;
|
| }
|
|
|
| .api-section h3 {
|
| color: #00FFFF;
|
| margin-bottom: 1rem;
|
| font-size: 1.5rem;
|
| }
|
|
|
| .api-endpoints {
|
| display: grid;
|
| gap: 0.5rem;
|
| font-family: 'Courier New', monospace;
|
| font-size: 0.9rem;
|
| }
|
|
|
| .api-endpoint {
|
| background: rgba(0, 0, 0, 0.4);
|
| padding: 0.75rem;
|
| border-radius: 8px;
|
| border-left: 3px solid #00FFFF;
|
| }
|
|
|
| .footer {
|
| background: rgba(0, 0, 0, 0.4);
|
| padding: 2rem;
|
| text-align: center;
|
| border-top: 1px solid rgba(0, 255, 255, 0.3);
|
| margin-top: 2rem;
|
| }
|
|
|
| .footer p {
|
| opacity: 0.7;
|
| margin-bottom: 0.5rem;
|
| }
|
|
|
| @keyframes pulse {
|
| 0%, 100% { opacity: 1; }
|
| 50% { opacity: 0.8; }
|
| }
|
|
|
| @keyframes spin {
|
| from { transform: rotate(0deg); }
|
| to { transform: rotate(360deg); }
|
| }
|
|
|
| .animate-spin {
|
| animation: spin 1s linear infinite;
|
| }
|
|
|
| .error-notification {
|
| animation: slideIn 0.3s ease-out;
|
| }
|
|
|
| @keyframes slideIn {
|
| from {
|
| transform: translateX(100%);
|
| opacity: 0;
|
| }
|
| to {
|
| transform: translateX(0);
|
| opacity: 1;
|
| }
|
| }
|
|
|
| .portal-card {
|
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
| }
|
|
|
| .portal-card:active {
|
| transform: translateY(-2px) scale(0.98);
|
| }
|
|
|
| @media (max-width: 768px) {
|
| .header h1 {
|
| font-size: 1.8rem;
|
| }
|
|
|
| .portals-grid {
|
| grid-template-columns: 1fr;
|
| gap: 1rem;
|
| }
|
|
|
| .status-bar {
|
| flex-direction: column;
|
| gap: 1rem;
|
| }
|
| }
|
| </style>
|
| </head>
|
| <body>
|
| <header class="header">
|
| <h1>AEGIS BIO DIGITAL LAB 10</h1>
|
| <div class="subtitle">AUGMENTED REALITY PORTAL DASHBOARD</div>
|
|
|
| <div class="status-bar">
|
| <div class="status-item">
|
| <i data-lucide="globe"></i>
|
| <span>10 Active Portals</span>
|
| </div>
|
| <div class="status-item" id="connection-status">
|
| <i data-lucide="wifi" id="status-icon"></i>
|
| <span id="status-text">Checking Connection...</span>
|
| </div>
|
| <div class="status-item">
|
| <i data-lucide="clock"></i>
|
| <span id="current-time"></span>
|
| </div>
|
| </div>
|
| </header>
|
|
|
| <main class="main-content">
|
| <section class="hero-section">
|
| <h2>🚀 Portal Access Dashboard</h2>
|
| <p>
|
| Welcome to the AEGIS Bio Digital Lab 10 AR Portal.
|
| Access all system components through this unified interface.
|
| Each portal provides specialized functionality for biological research and analysis.
|
| </p>
|
| </section>
|
|
|
| <section class="portals-grid" id="portals-container">
|
|
|
| </section>
|
|
|
| <section class="api-section">
|
| <h3>🔧 API Endpoints</h3>
|
| <div class="api-endpoints">
|
| <div class="api-endpoint">POST /api/update-urls - Update portal URLs</div>
|
| <div class="api-endpoint">GET /api/status - System status</div>
|
| <div class="api-endpoint">GET /health - Health check</div>
|
| </div>
|
| </section>
|
| </main>
|
|
|
| <footer class="footer">
|
| <p>🔬 Advanced Biological Research Platform</p>
|
| <p>🚀 Powered by AEGIS Technology</p>
|
| <p>© 2025 AEGIS Systems - All Rights Reserved</p>
|
| </footer>
|
|
|
| <script>
|
|
|
| window.portalsData = null;
|
| </script>
|
|
|
| <script>
|
|
|
| lucide.createIcons();
|
|
|
|
|
| const defaultPortals = [
|
| { name: 'Portal Control', url: '#', icon: 'settings', color: 'blue', description: 'Main control center' },
|
| { name: 'SciBERT Analysis', url: '#', icon: 'bar-chart-3', color: 'green', description: 'Scientific analysis tools' },
|
| { name: 'Conductor', url: '#', icon: 'cpu', color: 'purple', description: 'System orchestration' },
|
| { name: 'Economic Modal', url: '#', icon: 'dollar-sign', color: 'yellow', description: 'Economic modeling' },
|
| { name: 'War Modal', url: '#', icon: 'shield', color: 'red', description: 'Strategic analysis' },
|
| { name: 'Disease Modal', url: '#', icon: 'bug', color: 'orange', description: 'Disease tracking' },
|
| { name: 'DeepSeek', url: '#', icon: 'brain', color: 'indigo', description: 'AI deep analysis' },
|
| { name: 'Image Modal', url: '#', icon: 'image', color: 'pink', description: 'Image processing' },
|
| { name: 'Drug Development', url: '#', icon: 'pill', color: 'teal', description: 'Pharmaceutical research' },
|
| { name: 'Visual Kinetic', url: '#', icon: 'eye', color: 'cyan', description: 'Visual analytics' }
|
| ];
|
|
|
|
|
| function updateTime() {
|
| const now = new Date();
|
| document.getElementById('current-time').textContent = now.toLocaleString();
|
| }
|
|
|
|
|
| async function loadPortals() {
|
| const container = document.getElementById('portals-container');
|
|
|
|
|
| container.innerHTML = `
|
| <div style="grid-column: 1 / -1; text-align: center; padding: 2rem;">
|
| <i data-lucide="loader-2" class="animate-spin" style="width: 48px; height: 48px; margin-bottom: 1rem;"></i>
|
| <p>Loading portals...</p>
|
| </div>
|
| `;
|
| lucide.createIcons();
|
|
|
| let portalsData = null;
|
|
|
|
|
| try {
|
| const response = await fetch('/api/portals');
|
| if (response.ok) {
|
| const data = await response.json();
|
| portalsData = data.portals;
|
| }
|
| } catch (error) {
|
| console.warn('Failed to load portals from API:', error);
|
| }
|
|
|
|
|
| if (!portalsData && typeof window.portalsData !== 'undefined') {
|
| portalsData = window.portalsData;
|
| }
|
|
|
|
|
| const portalsToUse = portalsData || defaultPortals;
|
|
|
| container.innerHTML = portalsToUse.map(portal => `
|
| <div class="portal-card" onclick="openPortal('${portal.url}', '${portal.name}')">
|
| <div class="portal-icon color-${portal.color}">
|
| <i data-lucide="${portal.icon}"></i>
|
| </div>
|
| <h3>${portal.name}</h3>
|
| <p>${portal.description}</p>
|
| <a href="${portal.url}" class="launch-btn" onclick="event.stopPropagation(); openPortal('${portal.url}', '${portal.name}')">
|
| <span>Launch Portal</span>
|
| <i data-lucide="external-link"></i>
|
| </a>
|
| </div>
|
| `).join('');
|
|
|
| lucide.createIcons();
|
| }
|
|
|
|
|
| function openPortal(url) {
|
| if (url && url !== '#') {
|
| window.open(url, '_blank', 'noopener,noreferrer');
|
| }
|
| }
|
|
|
|
|
| async function checkStatus() {
|
| try {
|
| const response = await fetch('/api/status');
|
| const data = await response.json();
|
|
|
| const statusIcon = document.getElementById('status-icon');
|
| const statusText = document.getElementById('status-text');
|
| const connectionStatus = document.getElementById('connection-status');
|
|
|
| if (data.status === 'online') {
|
| statusIcon.setAttribute('data-lucide', 'wifi');
|
| statusText.textContent = 'System Online';
|
| connectionStatus.className = 'status-item status-online';
|
| } else {
|
| statusIcon.setAttribute('data-lucide', 'wifi-off');
|
| statusText.textContent = 'System Offline';
|
| connectionStatus.className = 'status-item status-offline';
|
| }
|
|
|
| lucide.createIcons();
|
| } catch (error) {
|
| const statusIcon = document.getElementById('status-icon');
|
| const statusText = document.getElementById('status-text');
|
| const connectionStatus = document.getElementById('connection-status');
|
|
|
| statusIcon.setAttribute('data-lucide', 'wifi-off');
|
| statusText.textContent = 'Connection Error';
|
| connectionStatus.className = 'status-item status-offline';
|
|
|
| lucide.createIcons();
|
| }
|
| }
|
|
|
|
|
| async function updatePortalUrls(newUrls) {
|
| try {
|
| const response = await fetch('/api/update-urls', {
|
| method: 'POST',
|
| headers: {
|
| 'Content-Type': 'application/json',
|
| },
|
| body: JSON.stringify({ urls: newUrls })
|
| });
|
|
|
| const data = await response.json();
|
|
|
| if (data.success) {
|
| loadPortals();
|
| return true;
|
| } else {
|
| console.error('Failed to update URLs:', data.error);
|
| return false;
|
| }
|
| } catch (error) {
|
| console.error('Error updating URLs:', error);
|
| return false;
|
| }
|
| }
|
|
|
|
|
| function initDashboard() {
|
| updateTime();
|
| loadPortals();
|
| checkStatus();
|
|
|
|
|
| setInterval(updateTime, 1000);
|
|
|
|
|
| setInterval(checkStatus, 30000);
|
| }
|
|
|
|
|
| document.addEventListener('keydown', function(event) {
|
|
|
| if ((event.ctrlKey || event.metaKey) && event.key === 'r') {
|
| event.preventDefault();
|
| loadPortals();
|
| checkStatus();
|
| }
|
| });
|
|
|
|
|
| window.addEventListener('focus', function() {
|
| checkStatus();
|
| });
|
|
|
|
|
| document.addEventListener('DOMContentLoaded', initDashboard);
|
|
|
|
|
| document.addEventListener('mouseover', function(event) {
|
| if (event.target.closest('.portal-card')) {
|
| event.target.closest('.portal-card').style.transform = 'translateY(-5px) scale(1.02)';
|
| }
|
| });
|
|
|
| document.addEventListener('mouseout', function(event) {
|
| if (event.target.closest('.portal-card')) {
|
| event.target.closest('.portal-card').style.transform = 'translateY(0) scale(1)';
|
| }
|
| });
|
|
|
|
|
| function showLoading(elementId) {
|
| const element = document.getElementById(elementId);
|
| if (element) {
|
| element.innerHTML = '<i data-lucide="loader-2" class="animate-spin"></i> Loading...';
|
| lucide.createIcons();
|
| }
|
| }
|
|
|
| function hideLoading(elementId, originalContent) {
|
| const element = document.getElementById(elementId);
|
| if (element) {
|
| element.innerHTML = originalContent;
|
| lucide.createIcons();
|
| }
|
| }
|
|
|
|
|
| function handlePortalError(portalName, error) {
|
| console.error(`Error launching ${portalName}:`, error);
|
|
|
|
|
| const errorDiv = document.createElement('div');
|
| errorDiv.className = 'error-notification';
|
| errorDiv.style.cssText = `
|
| position: fixed;
|
| top: 20px;
|
| right: 20px;
|
| background: rgba(244, 67, 54, 0.9);
|
| color: white;
|
| padding: 1rem;
|
| border-radius: 8px;
|
| z-index: 1000;
|
| max-width: 300px;
|
| backdrop-filter: blur(10px);
|
| `;
|
| errorDiv.innerHTML = `
|
| <div style="display: flex; align-items: center; gap: 0.5rem;">
|
| <i data-lucide="alert-circle"></i>
|
| <span>Failed to launch ${portalName}</span>
|
| </div>
|
| `;
|
|
|
| document.body.appendChild(errorDiv);
|
| lucide.createIcons();
|
|
|
|
|
| setTimeout(() => {
|
| if (errorDiv.parentNode) {
|
| errorDiv.parentNode.removeChild(errorDiv);
|
| }
|
| }, 5000);
|
| }
|
|
|
|
|
| function openPortalSafe(url, name) {
|
| try {
|
| if (url && url !== '#') {
|
| const newWindow = window.open(url, '_blank', 'noopener,noreferrer');
|
|
|
|
|
| if (!newWindow || newWindow.closed || typeof newWindow.closed === 'undefined') {
|
| throw new Error('Popup blocked or failed to open');
|
| }
|
| } else {
|
| throw new Error('Invalid URL');
|
| }
|
| } catch (error) {
|
| handlePortalError(name, error);
|
| }
|
| }
|
|
|
|
|
| function openPortal(url, name = 'Portal') {
|
| openPortalSafe(url, name);
|
| }
|
|
|
|
|
| if ('serviceWorker' in navigator) {
|
| window.addEventListener('load', function() {
|
| navigator.serviceWorker.register('/sw.js')
|
| .then(function(registration) {
|
| console.log('ServiceWorker registration successful');
|
| })
|
| .catch(function(error) {
|
| console.log('ServiceWorker registration failed');
|
| });
|
| });
|
| }
|
| </script>
|
| </body>
|
| </html>
|
|
|