Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Aeronix Test Case Generator</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| padding: 20px; | |
| } | |
| .container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| } | |
| .header { | |
| text-align: center; | |
| margin-bottom: 40px; | |
| color: white; | |
| } | |
| .header h1 { | |
| font-size: 3rem; | |
| font-weight: 700; | |
| text-shadow: 2px 2px 4px rgba(0,0,0,0.3); | |
| margin-bottom: 10px; | |
| } | |
| .header p { | |
| font-size: 1.2rem; | |
| opacity: 0.9; | |
| } | |
| .main-card { | |
| background: white; | |
| border-radius: 20px; | |
| box-shadow: 0 20px 40px rgba(0,0,0,0.1); | |
| overflow: hidden; | |
| backdrop-filter: blur(10px); | |
| } | |
| .form-section { | |
| padding: 40px; | |
| background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); | |
| } | |
| .form-title { | |
| font-size: 1.5rem; | |
| color: #333; | |
| margin-bottom: 30px; | |
| text-align: center; | |
| font-weight: 600; | |
| } | |
| .file-upload-container { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 30px; | |
| margin-bottom: 30px; | |
| } | |
| .file-upload-box input[type="file"] { | |
| position: absolute; | |
| opacity: 0; | |
| width: 100%; | |
| height: 100%; | |
| cursor: pointer; | |
| pointer-events: none; /* This prevents double triggering */ | |
| } | |
| .file-upload-box:hover { | |
| border-color: #764ba2; | |
| transform: translateY(-2px); | |
| box-shadow: 0 10px 25px rgba(0,0,0,0.1); | |
| } | |
| .file-upload-box.dragover { | |
| border-color: #764ba2; | |
| background-color: #f8f9ff; | |
| } | |
| .file-upload-box input[type="file"] { | |
| position: absolute; | |
| opacity: 0; | |
| width: 100%; | |
| height: 100%; | |
| cursor: pointer; | |
| } | |
| .upload-icon { | |
| font-size: 3rem; | |
| color: #667eea; | |
| margin-bottom: 15px; | |
| } | |
| .upload-text { | |
| color: #666; | |
| font-size: 1rem; | |
| margin-bottom: 5px; | |
| } | |
| .file-name { | |
| color: #333; | |
| font-weight: 600; | |
| font-size: 0.9rem; | |
| margin-top: 10px; | |
| padding: 8px 12px; | |
| background: #e8f2ff; | |
| border-radius: 8px; | |
| display: none; | |
| } | |
| .generate-btn { | |
| width: 100%; | |
| padding: 15px; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| border: none; | |
| border-radius: 12px; | |
| font-size: 1.1rem; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .generate-btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3); | |
| } | |
| .generate-btn:disabled { | |
| opacity: 0.6; | |
| cursor: not-allowed; | |
| transform: none; | |
| } | |
| .loading-spinner { | |
| display: none; | |
| width: 20px; | |
| height: 20px; | |
| border: 2px solid #ffffff; | |
| border-top: 2px solid transparent; | |
| border-radius: 50%; | |
| animation: spin 1s linear infinite; | |
| margin-right: 10px; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| .result-section { | |
| padding: 40px; | |
| background: #f8f9fa; | |
| display: none; | |
| } | |
| .result-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 20px; | |
| } | |
| .result-title { | |
| font-size: 1.5rem; | |
| color: #333; | |
| font-weight: 600; | |
| } | |
| .download-btn { | |
| padding: 10px 20px; | |
| background: #28a745; | |
| color: white; | |
| border: none; | |
| border-radius: 8px; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| } | |
| .download-btn:hover { | |
| background: #218838; | |
| transform: translateY(-1px); | |
| } | |
| .test-case-content { | |
| background: white; | |
| border-radius: 12px; | |
| padding: 30px; | |
| box-shadow: 0 5px 15px rgba(0,0,0,0.08); | |
| white-space: pre-wrap; | |
| font-family: 'Courier New', monospace; | |
| font-size: 0.9rem; | |
| line-height: 1.6; | |
| color: #333; | |
| max-height: 600px; | |
| overflow-y: auto; | |
| border: 1px solid #e9ecef; | |
| } | |
| .error-message { | |
| background: #f8d7da; | |
| color: #721c24; | |
| padding: 15px; | |
| border-radius: 8px; | |
| margin: 20px 0; | |
| border: 1px solid #f5c6cb; | |
| display: none; | |
| } | |
| .success-message { | |
| background: #d4edda; | |
| color: #155724; | |
| padding: 15px; | |
| border-radius: 8px; | |
| margin: 20px 0; | |
| border: 1px solid #c3e6cb; | |
| display: none; | |
| } | |
| @media (max-width: 768px) { | |
| .header h1 { | |
| font-size: 2rem; | |
| } | |
| .file-upload-container { | |
| grid-template-columns: 1fr; | |
| gap: 20px; | |
| } | |
| .form-section, | |
| .result-section { | |
| padding: 20px; | |
| } | |
| .result-header { | |
| flex-direction: column; | |
| gap: 15px; | |
| align-items: stretch; | |
| } | |
| } | |
| .pulse { | |
| animation: pulse 2s infinite; | |
| } | |
| @keyframes pulse { | |
| 0% { | |
| transform: scale(1); | |
| } | |
| 50% { | |
| transform: scale(1.02); | |
| } | |
| 100% { | |
| transform: scale(1); | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="header"> | |
| <h1>🚀 Aeronix</h1> | |
| <p>Advanced PCB Test Case Generator</p> | |
| </div> | |
| <div class="main-card"> | |
| <div class="form-section"> | |
| <h2 class="form-title">Upload Your Files</h2> | |
| <form id="testCaseForm"> | |
| <div class="file-upload-container"> | |
| <div class="file-upload-box" id="manualUpload"> | |
| <input type="file" id="manualFile" accept=".docx,.pdf,.doc" required> | |
| <div class="upload-icon">📋</div> | |
| <div class="upload-text">Manual Document</div> | |
| <div class="upload-text" style="font-size: 0.8rem; opacity: 0.7;">DOCX, PDF, DOC</div> | |
| <div class="file-name" id="manualFileName"></div> | |
| </div> | |
| <div class="file-upload-box" id="ipcUpload"> | |
| <input type="file" id="ipcFile" accept=".ipc" required> | |
| <div class="upload-icon">🔌</div> | |
| <div class="upload-text">IPC File</div> | |
| <div class="upload-text" style="font-size: 0.8rem; opacity: 0.7;">IPC format</div> | |
| <div class="file-name" id="ipcFileName"></div> | |
| </div> | |
| </div> | |
| <button type="submit" class="generate-btn" id="generateBtn"> | |
| <span class="loading-spinner" id="loadingSpinner"></span> | |
| <span id="btnText">Generate Test Cases</span> | |
| </button> | |
| </form> | |
| <div class="error-message" id="errorMessage"></div> | |
| <div class="success-message" id="successMessage"></div> | |
| </div> | |
| <div class="result-section" id="resultSection"> | |
| <div class="result-header"> | |
| <h3 class="result-title">Generated Test Cases</h3> | |
| <button class="download-btn" id="downloadBtn">💾 Download</button> | |
| </div> | |
| <div class="test-case-content" id="testCaseContent"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| const API_BASE_URL = 'http://localhost:8000'; | |
| // File upload handling | |
| function setupFileUpload(uploadBoxId, fileInputId, fileNameId) { | |
| const uploadBox = document.getElementById(uploadBoxId); | |
| const fileInput = document.getElementById(fileInputId); | |
| const fileName = document.getElementById(fileNameId); | |
| uploadBox.addEventListener('click', () => fileInput.click()); | |
| fileInput.addEventListener('change', (e) => { | |
| const file = e.target.files[0]; | |
| if (file) { | |
| fileName.textContent = file.name; | |
| fileName.style.display = 'block'; | |
| uploadBox.classList.add('pulse'); | |
| setTimeout(() => uploadBox.classList.remove('pulse'), 1000); | |
| } | |
| }); | |
| // Drag and drop | |
| uploadBox.addEventListener('dragover', (e) => { | |
| e.preventDefault(); | |
| uploadBox.classList.add('dragover'); | |
| }); | |
| uploadBox.addEventListener('dragleave', () => { | |
| uploadBox.classList.remove('dragover'); | |
| }); | |
| uploadBox.addEventListener('drop', (e) => { | |
| e.preventDefault(); | |
| uploadBox.classList.remove('dragover'); | |
| const files = e.dataTransfer.files; | |
| if (files.length > 0) { | |
| fileInput.files = files; | |
| const file = files[0]; | |
| fileName.textContent = file.name; | |
| fileName.style.display = 'block'; | |
| } | |
| }); | |
| } | |
| // Initialize file uploads | |
| setupFileUpload('manualUpload', 'manualFile', 'manualFileName'); | |
| setupFileUpload('ipcUpload', 'ipcFile', 'ipcFileName'); | |
| // Form submission | |
| document.getElementById('testCaseForm').addEventListener('submit', async (e) => { | |
| e.preventDefault(); | |
| const manualFile = document.getElementById('manualFile').files[0]; | |
| const ipcFile = document.getElementById('ipcFile').files[0]; | |
| if (!manualFile || !ipcFile) { | |
| showError('Please select both files before generating test cases.'); | |
| return; | |
| } | |
| await generateTestCases(manualFile, ipcFile); | |
| }); | |
| async function generateTestCases(manualFile, ipcFile) { | |
| const generateBtn = document.getElementById('generateBtn'); | |
| const loadingSpinner = document.getElementById('loadingSpinner'); | |
| const btnText = document.getElementById('btnText'); | |
| const resultSection = document.getElementById('resultSection'); | |
| const testCaseContent = document.getElementById('testCaseContent'); | |
| // Show loading state | |
| generateBtn.disabled = true; | |
| loadingSpinner.style.display = 'inline-block'; | |
| btnText.textContent = 'Generating...'; | |
| hideMessages(); | |
| try { | |
| const formData = new FormData(); | |
| formData.append('manual_file', manualFile); | |
| formData.append('ipc_file', ipcFile); | |
| const response = await fetch('/generate-test-case', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| console.log('Response status:', response.status); | |
| console.log('Response headers:', response.headers.get('content-type')); | |
| // Check if response is ok | |
| if (!response.ok) { | |
| let errorMessage = `HTTP error! status: ${response.status}`; | |
| // Try to get error details | |
| try { | |
| const contentType = response.headers.get('content-type'); | |
| if (contentType && contentType.includes('application/json')) { | |
| const errorData = await response.json(); | |
| errorMessage = errorData.detail || errorMessage; | |
| } else { | |
| const errorText = await response.text(); | |
| console.log('Error response text:', errorText); | |
| errorMessage = `Server error: ${response.status}`; | |
| } | |
| } catch (e) { | |
| console.log('Could not parse error response:', e); | |
| } | |
| throw new Error(errorMessage); | |
| } | |
| // Check content type before parsing | |
| const contentType = response.headers.get('content-type'); | |
| console.log('Success response content-type:', contentType); | |
| let data; | |
| if (contentType && contentType.includes('application/json')) { | |
| // Parse as JSON | |
| data = await response.json(); | |
| console.log('Parsed JSON data:', data); | |
| if (!data.success) { | |
| throw new Error(data.message || 'Failed to generate test cases'); | |
| } | |
| // Check if HTML file is available | |
| if (data.html_url) { | |
| console.log('HTML file available at:', data.html_url); | |
| // Redirect to the generated HTML page | |
| showSuccess(`Test cases generated successfully! Redirecting to HTML report...`); | |
| // Wait 1 second then redirect | |
| setTimeout(() => { | |
| window.location.href = data.html_url; | |
| }, 1000); | |
| return; // Exit function after redirect | |
| } | |
| // If no HTML, show text content (fallback) | |
| const testCases = data.content; | |
| window.currentTestCases = testCases; | |
| window.currentFileName = data.filename || 'test_cases.txt'; | |
| // Display results on webpage | |
| testCaseContent.textContent = testCases; | |
| resultSection.style.display = 'block'; | |
| resultSection.scrollIntoView({ behavior: 'smooth' }); | |
| } else if (contentType && contentType.includes('text/plain')) { | |
| // Handle as plain text (fallback) | |
| const testCases = await response.text(); | |
| console.log('Plain text response received'); | |
| data = { | |
| content: testCases, | |
| filename: response.headers.get('X-Generated-File') || 'test_cases.txt' | |
| }; | |
| window.currentTestCases = testCases; | |
| window.currentFileName = data.filename; | |
| // Display results on webpage | |
| testCaseContent.textContent = data.content; | |
| resultSection.style.display = 'block'; | |
| resultSection.scrollIntoView({ behavior: 'smooth' }); | |
| } else { | |
| // Unknown content type | |
| const responseText = await response.text(); | |
| console.log('Unknown content type. Response:', responseText); | |
| throw new Error('Unexpected response format from server'); | |
| } | |
| showSuccess(`Test cases generated successfully! File: ${data.filename || 'Generated'}`); | |
| } catch (error) { | |
| console.error('Detailed error:', error); | |
| showError(error.message || 'An error occurred while generating test cases.'); | |
| } finally { | |
| // Reset button state | |
| generateBtn.disabled = false; | |
| loadingSpinner.style.display = 'none'; | |
| btnText.textContent = 'Generate Test Cases'; | |
| } | |
| } | |
| function showError(message) { | |
| const errorDiv = document.getElementById('errorMessage'); | |
| errorDiv.textContent = message; | |
| errorDiv.style.display = 'block'; | |
| setTimeout(() => { | |
| errorDiv.style.display = 'none'; | |
| }, 5000); | |
| } | |
| function showSuccess(message) { | |
| const successDiv = document.getElementById('successMessage'); | |
| successDiv.textContent = message; | |
| successDiv.style.display = 'block'; | |
| setTimeout(() => { | |
| successDiv.style.display = 'none'; | |
| }, 3000); | |
| } | |
| function hideMessages() { | |
| document.getElementById('errorMessage').style.display = 'none'; | |
| document.getElementById('successMessage').style.display = 'none'; | |
| } | |
| </script> | |
| </body> | |
| </html> |