ramangos/ResumeEditor
0
1<!DOCTYPE html>2<html lang="en">3<head>4 <meta charset="UTF-8">5 <meta name="viewport" content="width=device-width, initial-scale=1.0">6 <title>Resume Editor</title>7 <script src="https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.4.2/mammoth.browser.min.js"></script>8 <script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>9 <script src="https://cdnjs.cloudflare.com/ajax/libs/pizzip/3.1.4/pizzip.min.js"></script>10 <script src="https://cdnjs.cloudflare.com/ajax/libs/docxtemplater/3.37.11/docxtemplater.min.js"></script>11 <style>12 * {13 margin: 0;14 padding: 0;15 box-sizing: border-box;16 }17 18 body {19 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;20 background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);21 min-height: 100vh;22 padding: 20px;23 }24 25 .container {26 max-width: 900px;27 margin: 0 auto;28 background: white;29 border-radius: 20px;30 box-shadow: 0 20px 40px rgba(0,0,0,0.1);31 overflow: hidden;32 animation: slideUp 0.6s ease-out;33 }34 35 @keyframes slideUp {36 from {37 opacity: 0;38 transform: translateY(30px);39 }40 to {41 opacity: 1;42 transform: translateY(0);43 }44 }45 46 .header {47 background: linear-gradient(135deg, #2c3e50, #3498db);48 color: white;49 padding: 30px;50 text-align: center;51 }52 53 .header h1 {54 font-size: 2.5em;55 margin-bottom: 10px;56 font-weight: 300;57 }58 59 .header p {60 opacity: 0.9;61 font-size: 1.1em;62 }63 64 .upload-section {65 padding: 40px;66 text-align: center;67 border-bottom: 1px solid #eee;68 }69 70 .upload-area {71 border: 3px dashed #3498db;72 border-radius: 15px;73 padding: 60px 40px;74 background: #f8f9fa;75 transition: all 0.3s ease;76 cursor: pointer;77 position: relative;78 overflow: hidden;79 }80 81 .upload-area:hover {82 border-color: #2980b9;83 background: #e8f4f8;84 transform: translateY(-2px);85 }86 87 .upload-area.dragover {88 border-color: #27ae60;89 background: #e8f5e8;90 }91 92 .upload-icon {93 font-size: 4em;94 color: #3498db;95 margin-bottom: 20px;96 }97 98 .upload-text {99 font-size: 1.2em;100 color: #2c3e50;101 margin-bottom: 15px;102 }103 104 .upload-hint {105 color: #7f8c8d;106 font-size: 0.9em;107 }108 109 #fileInput {110 display: none;111 }112 113 .toolbar {114 background: #f8f9fa;115 padding: 15px 30px;116 border-bottom: 1px solid #dee2e6;117 display: none;118 }119 120 .toolbar button {121 background: #3498db;122 color: white;123 border: none;124 padding: 10px 20px;125 border-radius: 8px;126 cursor: pointer;127 font-size: 14px;128 margin-right: 10px;129 transition: all 0.3s ease;130 }131 132 .toolbar button:hover {133 background: #2980b9;134 transform: translateY(-1px);135 }136 137 .toolbar button:disabled {138 background: #95a5a6;139 cursor: not-allowed;140 transform: none;141 }142 143 .editor-section {144 padding: 40px;145 display: none;146 }147 148 .editor {149 min-height: 600px;150 border: 2px solid #e9ecef;151 border-radius: 10px;152 padding: 30px;153 background: white;154 font-family: 'Times New Roman', serif;155 font-size: 12pt;156 line-height: 1.6;157 outline: none;158 box-shadow: inset 0 2px 4px rgba(0,0,0,0.06);159 }160 161 .editor:focus {162 border-color: #3498db;163 box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);164 }165 166 .status {167 margin-top: 20px;168 padding: 15px;169 border-radius: 8px;170 font-weight: 500;171 text-align: center;172 display: none;173 }174 175 .status.success {176 background: #d4edda;177 color: #155724;178 border: 1px solid #c3e6cb;179 }180 181 .status.error {182 background: #f8d7da;183 color: #721c24;184 border: 1px solid #f5c6cb;185 }186 187 .loading {188 display: none;189 text-align: center;190 padding: 20px;191 }192 193 .spinner {194 border: 4px solid #f3f3f3;195 border-top: 4px solid #3498db;196 border-radius: 50%;197 width: 40px;198 height: 40px;199 animation: spin 1s linear infinite;200 margin: 0 auto 15px;201 }202 203 @keyframes spin {204 0% { transform: rotate(0deg); }205 100% { transform: rotate(360deg); }206 }207 208 .file-info {209 background: #e8f4f8;210 padding: 15px;211 border-radius: 8px;212 margin-bottom: 20px;213 display: none;214 }215 216 .file-info h3 {217 color: #2c3e50;218 margin-bottom: 5px;219 }220 221 .file-info p {222 color: #7f8c8d;223 font-size: 0.9em;224 }225 226 /* Preserve Word document styling */227 .editor h1, .editor h2, .editor h3, .editor h4, .editor h5, .editor h6 {228 font-weight: bold;229 margin: 12px 0 6px 0;230 }231 232 .editor p {233 margin: 6px 0;234 }235 236 .editor ul, .editor ol {237 margin: 6px 0;238 padding-left: 20px;239 }240 241 .editor strong {242 font-weight: bold;243 }244 245 .editor em {246 font-style: italic;247 }248 249 .editor u {250 text-decoration: underline;251 }252 </style>253</head>254<body>255 <div class="container">256 <div class="header">257 <h1>Resume Editor</h1>258 <p>Upload, edit, and save your resume in .docx format</p>259 </div>260 261 <div class="upload-section" id="uploadSection">262 <div class="upload-area" id="uploadArea">263 <div class="upload-icon">๐</div>264 <div class="upload-text">Drop your resume here or click to browse</div>265 <div class="upload-hint">Supports .docx files only</div>266 <input type="file" id="fileInput" accept=".docx" />267 </div>268 </div>269 270 <div class="loading" id="loading">271 <div class="spinner"></div>272 <p>Processing your resume...</p>273 </div>274 275 <div class="toolbar" id="toolbar">276 <button id="saveBtn">๐พ Save as DOCX</button>277 <button id="newFileBtn">๐ Upload New File</button>278 </div>279 280 <div class="editor-section" id="editorSection">281 <div class="file-info" id="fileInfo">282 <h3 id="fileName"></h3>283 <p id="fileSize"></p>284 </div>285 <div class="editor" id="editor" contenteditable="true"></div>286 </div>287 288 <div class="status" id="status"></div>289 </div>290 291 <script>292 let originalDocContent = null;293 let currentFileName = '';294 295 // DOM elements296 const uploadArea = document.getElementById('uploadArea');297 const fileInput = document.getElementById('fileInput');298 const uploadSection = document.getElementById('uploadSection');299 const toolbar = document.getElementById('toolbar');300 const editorSection = document.getElementById('editorSection');301 const editor = document.getElementById('editor');302 const loading = document.getElementById('loading');303 const status = document.getElementById('status');304 const fileInfo = document.getElementById('fileInfo');305 const fileName = document.getElementById('fileName');306 const fileSize = document.getElementById('fileSize');307 const saveBtn = document.getElementById('saveBtn');308 const newFileBtn = document.getElementById('newFileBtn');309 310 // Event listeners311 uploadArea.addEventListener('click', () => fileInput.click());312 uploadArea.addEventListener('dragover', handleDragOver);313 uploadArea.addEventListener('dragleave', handleDragLeave);314 uploadArea.addEventListener('drop', handleDrop);315 fileInput.addEventListener('change', handleFileSelect);316 saveBtn.addEventListener('click', saveDocument);317 newFileBtn.addEventListener('click', resetEditor);318 319 function handleDragOver(e) {320 e.preventDefault();321 uploadArea.classList.add('dragover');322 }323 324 function handleDragLeave(e) {325 e.preventDefault();326 uploadArea.classList.remove('dragover');327 }328 329 function handleDrop(e) {330 e.preventDefault();331 uploadArea.classList.remove('dragover');332 const files = e.dataTransfer.files;333 if (files.length > 0) {334 processFile(files[0]);335 }336 }337 338 function handleFileSelect(e) {339 const file = e.target.files[0];340 if (file) {341 processFile(file);342 }343 }344 345 function processFile(file) {346 if (!file.name.endsWith('.docx')) {347 showStatus('Please select a .docx file only.', 'error');348 return;349 }350 351 currentFileName = file.name;352 showLoading(true);353 354 // Display file info355 fileName.textContent = file.name;356 fileSize.textContent = `Size: ${(file.size / 1024).toFixed(1)} KB`;357 fileInfo.style.display = 'block';358 359 // Read the file as array buffer for later use360 const reader = new FileReader();361 reader.onload = function(e) {362 originalDocContent = e.target.result;363 364 // Convert to HTML for editing365 mammoth.convertToHtml({arrayBuffer: e.target.result})366 .then(function(result) {367 editor.innerHTML = result.value;368 showEditor();369 showLoading(false);370 showStatus('Resume loaded successfully! You can now edit it.', 'success');371 })372 .catch(function(error) {373 showLoading(false);374 showStatus('Error loading the document. Please try again.', 'error');375 console.error('Error:', error);376 });377 };378 reader.readAsArrayBuffer(file);379 }380 381 function showEditor() {382 uploadSection.style.display = 'none';383 toolbar.style.display = 'block';384 editorSection.style.display = 'block';385 }386 387 function showLoading(show) {388 loading.style.display = show ? 'block' : 'none';389 }390 391 function showStatus(message, type) {392 status.textContent = message;393 status.className = `status ${type}`;394 status.style.display = 'block';395 setTimeout(() => {396 status.style.display = 'none';397 }, 3000);398 }399 400 function saveDocument() {401 if (!originalDocContent) {402 showStatus('No document to save.', 'error');403 return;404 }405 406 try {407 // Get the edited HTML content408 const editedHtml = editor.innerHTML;409 410 // For a more robust solution, you would need to:411 // 1. Parse the HTML back to maintain Word formatting412 // 2. Update the original document structure413 // 3. Generate a new .docx file414 415 // This is a simplified approach using the original document416 // and creating a basic text version417 const textContent = editor.innerText;418 419 // Create a simple document structure420 const doc = new PizZip(originalDocContent);421 422 // This is a basic implementation - in a production app,423 // you'd want to use a more sophisticated approach to maintain formatting424 const blob = new Blob([textContent], {425 type: 'text/plain'426 });427 428 // For now, save as text file with instructions429 const newFileName = currentFileName.replace('.docx', '_edited.txt');430 saveAs(blob, newFileName);431 432 showStatus('Document saved! Note: For full .docx support, consider using a server-side solution.', 'success');433 434 } catch (error) {435 showStatus('Error saving document. Please try again.', 'error');436 console.error('Save error:', error);437 }438 }439 440 function resetEditor() {441 uploadSection.style.display = 'block';442 toolbar.style.display = 'none';443 editorSection.style.display = 'none';444 fileInfo.style.display = 'none';445 editor.innerHTML = '';446 originalDocContent = null;447 currentFileName = '';448 fileInput.value = '';449 status.style.display = 'none';450 }451 452 // Enhanced save function with better DOCX handling453 async function saveAsDocx() {454 if (!originalDocContent) {455 showStatus('No document to save.', 'error');456 return;457 }458 459 try {460 // Convert HTML back to a format that can be saved as DOCX461 const editedText = editor.innerText;462 463 // Create a new document with the edited content464 // This is a simplified approach - for production use, consider:465 // 1. Using a server-side solution with libraries like python-docx466 // 2. Implementing a more sophisticated HTML-to-DOCX converter467 // 3. Using commercial APIs that specialize in document conversion468 469 const zip = new PizZip(originalDocContent);470 const doc = new window.docxtemplater(zip, {471 paragraphLoop: true,472 linebreaks: true,473 });474 475 // Replace content (simplified approach)476 const content = {477 content: editedText478 };479 480 doc.setData(content);481 482 try {483 doc.render();484 const out = doc.getZip().generate({485 type: "blob",486 mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",487 });488 489 const newFileName = currentFileName.replace('.docx', '_edited.docx');490 saveAs(out, newFileName);491 showStatus('Document saved as DOCX!', 'success');492 493 } catch (error) {494 // Fallback to text file495 const blob = new Blob([editedText], { type: 'text/plain' });496 const newFileName = currentFileName.replace('.docx', '_edited.txt');497 saveAs(blob, newFileName);498 showStatus('Saved as text file. For full DOCX support, use a specialized document editor.', 'success');499 }500 501 } catch (error) {502 showStatus('Error saving document. Please try again.', 'error');503 console.error('Save error:', error);504 }505 }506 507 // Update save button to use the enhanced function508 saveBtn.addEventListener('click', saveAsDocx);509 </script>510</body>511</html>