| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>CV Parser</title> |
| <script src=https://cdn.jsdelivr.net/npm/pretty-print-json@3.0/dist/pretty-print-json.min.js></script> |
| <link rel=stylesheet href=https://cdn.jsdelivr.net/npm/pretty-print-json@3.0/dist/css/pretty-print-json.css> |
| <style> |
| body { |
| font-family: Arial, sans-serif; |
| max-width: 800px; |
| margin: 0 auto; |
| padding: 20px; |
| } |
| .container { |
| border: 1px solid #ddd; |
| padding: 20px; |
| border-radius: 5px; |
| } |
| .upload-area { |
| border: 2px dashed #ccc; |
| padding: 20px; |
| text-align: center; |
| margin-bottom: 20px; |
| cursor: pointer; |
| } |
| .upload-area:hover { |
| border-color: #aaa; |
| } |
| .upload-area.highlight { |
| border-color: #2196F3; |
| background-color: #f0f8ff; |
| } |
| .progress { |
| display: none; |
| margin-top: 10px; |
| } |
| button { |
| background-color: #4CAF50; |
| color: white; |
| padding: 10px 15px; |
| border: none; |
| border-radius: 4px; |
| cursor: pointer; |
| } |
| button:hover { |
| background-color: #45a049; |
| } |
| .error { |
| color: red; |
| margin-top: 10px; |
| } |
| |
| |
| .json-container { |
| background-color: #f8f9fa; |
| border-radius: 4px; |
| padding: 15px; |
| font-family: 'Consolas', 'Monaco', monospace; |
| font-size: 14px; |
| overflow: auto; |
| max-height: 600px; |
| border: 1px solid #e0e0e0; |
| position: relative; |
| } |
| |
| .json-key { |
| color: #0366d6; |
| font-weight: bold; |
| } |
| |
| .json-string { |
| color: #22863a; |
| } |
| |
| .json-number { |
| color: #005cc5; |
| } |
| |
| .json-boolean { |
| color: #d73a49; |
| } |
| |
| .json-null { |
| color: #6a737d; |
| } |
| |
| .collapsible, |
| .collapsible::before, |
| .collapsible.collapsed::before, |
| .json-toggle-all { |
| display: none; |
| } |
| |
| .json-copy-btn { |
| position: absolute; |
| top: 10px; |
| right: 10px; |
| background-color: #f0f0f0; |
| border: 1px solid #ddd; |
| border-radius: 3px; |
| padding: 3px 8px; |
| font-size: 12px; |
| cursor: pointer; |
| } |
| |
| .copied-message { |
| position: absolute; |
| top: 10px; |
| right: 55px; |
| background-color: #4CAF50; |
| color: white; |
| border-radius: 3px; |
| padding: 3px 8px; |
| font-size: 12px; |
| opacity: 0; |
| transition: opacity 0.3s; |
| } |
| |
| .copied-message.show { |
| opacity: 1; |
| } |
| |
| .results-container { |
| position: relative; |
| margin-top: 20px; |
| } |
| </style> |
| </head> |
| <body> |
| <h1>CV Parser</h1> |
| <div class="container"> |
| <div class="upload-area" id="drop-area"> |
| <p>Drag & drop your CV file here or click anywhere in this area to select</p> |
| <input type="file" id="fileInput" accept=".pdf,.docx,.jpg,.jpeg,.png" style="display: none;"> |
| <p>Supported formats: PDF, DOCX, JPG, PNG</p> |
| </div> |
| <div id="fileName"></div> |
| <div class="progress" id="uploadProgress">Uploading...</div> |
| <div class="error" id="errorMessage"></div> |
| <button id="uploadBtn" style="display: none;">Upload</button> |
| |
| <h2>Results</h2> |
| <div class="results-container"> |
| <pre id="results" class="json-container"></pre> |
| </div> |
| </div> |
|
|
| <script> |
| document.addEventListener('DOMContentLoaded', function() { |
| const dropArea = document.getElementById('drop-area'); |
| const fileInput = document.getElementById('fileInput'); |
| const uploadBtn = document.getElementById('uploadBtn'); |
| const fileNameDisplay = document.getElementById('fileName'); |
| const progressIndicator = document.getElementById('uploadProgress'); |
| const resultsContainer = document.getElementById('results'); |
| const errorMessage = document.getElementById('errorMessage'); |
| |
| |
| dropArea.addEventListener('click', () => fileInput.click()); |
| |
| |
| fileInput.addEventListener('change', handleFileSelection); |
| |
| |
| ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { |
| dropArea.addEventListener(eventName, preventDefaults, false); |
| }); |
| |
| function preventDefaults(e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| } |
| |
| ['dragenter', 'dragover'].forEach(eventName => { |
| dropArea.addEventListener(eventName, () => { |
| dropArea.classList.add('highlight'); |
| }, false); |
| }); |
| |
| ['dragleave', 'drop'].forEach(eventName => { |
| dropArea.addEventListener(eventName, () => { |
| dropArea.classList.remove('highlight'); |
| }, false); |
| }); |
| |
| dropArea.addEventListener('drop', (e) => { |
| const dt = e.dataTransfer; |
| const files = dt.files; |
| if (files.length) { |
| fileInput.files = files; |
| handleFileSelection(); |
| } |
| }, false); |
| |
| |
| uploadBtn.addEventListener('click', uploadFile); |
| |
| function handleFileSelection() { |
| if (fileInput.files.length > 0) { |
| const file = fileInput.files[0]; |
| fileNameDisplay.textContent = `Selected file: ${file.name}`; |
| uploadBtn.style.display = 'block'; |
| resultsContainer.style.display = 'none'; |
| errorMessage.textContent = ''; |
| } |
| } |
| |
| function uploadFile() { |
| if (!fileInput.files.length) return; |
| |
| const file = fileInput.files[0]; |
| const formData = new FormData(); |
| formData.append('file', file); |
| |
| |
| progressIndicator.style.display = 'block'; |
| uploadBtn.disabled = true; |
| |
| fetch('/upload/', { |
| method: 'POST', |
| body: formData |
| }) |
| .then(response => { |
| if (!response.ok) { |
| return response.json().then(data => { |
| throw new Error(data.detail || 'Error uploading file'); |
| }); |
| } |
| return response.json(); |
| }) |
| .then(data => { |
| |
| resultsContainer.innerHTML = ''; |
| resultsContainer.style.display = 'block'; |
| |
| |
| const jsonHtml = prettyPrintJson.toHtml(data, { |
| indent: 4, |
| lineNumbers: true, |
| linkUrls: true, |
| quoteKeys: true |
| }); |
| |
| resultsContainer.innerHTML = jsonHtml; |
| |
| |
| addCopyButton(); |
| |
| errorMessage.textContent = ''; |
| }) |
| .catch(error => { |
| errorMessage.textContent = error.message; |
| resultsContainer.style.display = 'none'; |
| }) |
| .finally(() => { |
| progressIndicator.style.display = 'none'; |
| uploadBtn.disabled = false; |
| }); |
| } |
| |
| |
| function addCopyButton() { |
| const parent = resultsContainer.parentNode; |
| |
| |
| const copyBtn = document.createElement('button'); |
| copyBtn.className = 'json-copy-btn'; |
| copyBtn.textContent = 'Copy to Clipboard'; |
| parent.appendChild(copyBtn); |
| |
| |
| const copiedMsg = document.createElement('span'); |
| copiedMsg.className = 'copied-message'; |
| copiedMsg.textContent = 'Copied!'; |
| parent.appendChild(copiedMsg); |
| |
| |
| copyBtn.addEventListener('click', () => { |
| try { |
| |
| const jsonText = JSON.stringify( |
| JSON.parse(resultsContainer.textContent), |
| null, |
| 2 |
| ); |
| |
| navigator.clipboard.writeText(jsonText).then(() => { |
| copiedMsg.classList.add('show'); |
| setTimeout(() => copiedMsg.classList.remove('show'), 2000); |
| }); |
| } catch (err) { |
| console.error('Failed to copy JSON:', err); |
| } |
| }); |
| } |
| }); |
| </script> |
| </body> |
| </html> |
|
|