Resume/CV Outline Creator
Structured tool for drafting career, education, and skill sections.
1. Personal & Contact Information
2. Professional Experience
No work experience added yet.
3. Education
No education entries added yet.
4. Skills & Technical Proficiencies
No skills added yet.
No work experience added yet.
'; return; } experience.forEach((job, index) => { const li = document.createElement('div'); li.className = 'rvo-list-item'; li.innerHTML = `${job.title} at ${job.company} (${job.dates})
- ${job.details.split('\n').map(d => d.trim() ? `
- ${d.trim()} ` : '').join('')}
No education entries added yet.
'; return; } education.forEach((edu, index) => { const li = document.createElement('div'); li.className = 'rvo-list-item'; li.innerHTML = `${edu.degree} from ${edu.institution}
Graduated: ${edu.gradDate}
No skills added yet.
'; return; } // Display skills as chips const skillContainer = document.createElement('div'); skillContainer.className = 'flex flex-wrap gap-2'; skills.forEach((skill, index) => { const chip = document.createElement('span'); chip.className = 'inline-flex items-center text-sm font-medium bg-sky-200 text-sky-800 rounded-full px-3 py-1'; chip.innerHTML = `${skill} `; chip.querySelector('button').addEventListener('click', () => removeSkill(index)); skillContainer.appendChild(chip); }); rvoSkillsList.appendChild(skillContainer); } function addSkill() { const skillText = rvoSkillInput.value.trim(); if (skillText && !skills.includes(skillText)) { skills.push(skillText); rvoSkillInput.value = ''; renderSkills(); } } function removeSkill(index) { skills.splice(index, 1); renderSkills(); } // --- PDF Generation (Ensured functionality) --- function downloadPDF() { if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') { alert('Error: jsPDF library not loaded.'); return; } if (typeof window.jspdf.jsPDF.autoTable === 'undefined') { alert('Error: jsPDF-AutoTable library not loaded.'); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF(); const margin = 40; const pageWidth = doc.internal.pageSize.getWidth(); let currentY = margin; // --- Helper function to add structured text sections --- function addTextSection(title, content, startY, fontStyle = 'normal') { const estimatedHeight = 15 + (content.split('\n').length * 12) + 10; if (startY + estimatedHeight > doc.internal.pageSize.getHeight() - margin) { doc.addPage(); startY = margin; } doc.setFontSize(12); doc.setFont('helvetica', 'bold'); doc.setTextColor(14, 165, 233); // sky-500 doc.text(title, margin, startY); doc.setFontSize(10); doc.setFont('helvetica', fontStyle); doc.setTextColor(51, 65, 85); const lines = doc.splitTextToSize(content || 'N/A', pageWidth - margin * 2); doc.text(lines, margin, startY + 15); return startY + 15 + lines.length * 12 + 10; } // --- Header (Contact Block) --- doc.setFontSize(20); doc.setFont('helvetica', 'bold'); doc.setTextColor(3, 105, 161); // sky-700 doc.text(rvoNameInput.value || 'APPLICANT NAME', pageWidth / 2, currentY, { align: 'center' }); currentY += 15; doc.setFontSize(10); doc.setFont('helvetica', 'normal'); doc.setTextColor(71, 85, 105); const contactInfo = [ rvoPhoneInput.value, rvoEmailInput.value, rvoLinkedInInput.value ].filter(Boolean).join(' | '); doc.text(contactInfo || 'Contact details N/A', pageWidth / 2, currentY, { align: 'center' }); currentY += 20; // --- Professional Summary --- currentY = addTextSection("PROFESSIONAL SUMMARY", rvoSummaryTextarea.value, currentY); // --- Professional Experience --- doc.setFontSize(14); doc.setFont('helvetica', 'bold'); doc.setTextColor(14, 165, 233); doc.text("PROFESSIONAL EXPERIENCE", margin, currentY); currentY += 5; experience.forEach(job => { // Check page break const jobDetailLines = doc.splitTextToSize(job.details, pageWidth - margin * 2 - 10); const estimatedHeight = 30 + jobDetailLines.length * 10; if (currentY + estimatedHeight > doc.internal.pageSize.getHeight() - margin) { doc.addPage(); currentY = margin; } doc.setFontSize(10); doc.setFont('helvetica', 'bold'); doc.setTextColor(51, 65, 85); doc.text(job.title, margin, currentY); doc.text(job.dates, pageWidth - margin, currentY, { align: 'right' }); currentY += 12; doc.setFont('helvetica', 'normal'); doc.text(job.company, margin, currentY); currentY += 5; // Bullet points (using autoTable for spacing/consistency) const detailsArray = job.details.split('\n').filter(d => d.trim()); const bulletData = detailsArray.map(detail => [`• ${detail}`]); doc.autoTable({ startY: currentY, body: bulletData, theme: 'plain', styles: { cellPadding: 1, fontSize: 9, minCellHeight: 10, cellWidth: pageWidth - margin * 2 - 10, overflow: 'linebreak' }, margin: { left: margin + 10, right: margin } }); currentY = doc.autoTable.previous.finalY + 10; }); // --- Education --- doc.setFontSize(14); doc.setFont('helvetica', 'bold'); doc.setTextColor(14, 165, 233); doc.text("EDUCATION", margin, currentY); currentY += 5; education.forEach(edu => { const estimatedHeight = 30; if (currentY + estimatedHeight > doc.internal.pageSize.getHeight() - margin) { doc.addPage(); currentY = margin; } doc.setFontSize(10); doc.setFont('helvetica', 'bold'); doc.setTextColor(51, 65, 85); doc.text(edu.degree, margin, currentY); doc.text(edu.gradDate, pageWidth - margin, currentY, { align: 'right' }); currentY += 12; doc.setFont('helvetica', 'normal'); doc.text(edu.institution, margin, currentY); currentY += 15; }); // --- Skills --- if (skills.length > 0) { doc.setFontSize(14); doc.setFont('helvetica', 'bold'); doc.setTextColor(14, 165, 233); doc.text("SKILLS & PROFICIENCIES", margin, currentY); currentY += 15; doc.setFontSize(10); doc.setFont('helvetica', 'normal'); const skillsText = skills.join(' • '); currentY = addTextSection("", skillsText, currentY, 'bold'); } doc.save('resume-outline.pdf'); } // --- Event Listeners and Initial Load --- rvoAddJobBtn.addEventListener('click', addExperience); rvoAddEducationBtn.addEventListener('click', addEducation); rvoAddSkillBtn.addEventListener('click', addSkill); rvoSkillInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { e.preventDefault(); addSkill(); } }); rvoPdfBtn.addEventListener('click', downloadPDF); renderExperience(); renderEducation(); renderSkills(); });