/** * CV Generator - JavaScript * Gère le formulaire multi-étapes et l'interaction avec l'API */ // Configuration const API_BASE_URL = window.location.origin + '/api/cv'; let currentStep = 1; const totalSteps = 5; // ======================================== // Navigation entre les étapes // ======================================== function updateProgress() { const progress = ((currentStep - 1) / (totalSteps - 1)) * 100; document.getElementById('progress').style.width = `${progress}%`; // Mettre à jour les indicateurs d'étapes document.querySelectorAll('.step').forEach((step, index) => { const stepNum = index + 1; step.classList.remove('active', 'completed'); if (stepNum < currentStep) { step.classList.add('completed'); } else if (stepNum === currentStep) { step.classList.add('active'); } }); } function showStep(step) { document.querySelectorAll('.form-step').forEach(el => { el.classList.remove('active'); }); document.getElementById(`step-${step}`).classList.add('active'); // Gérer les boutons de navigation document.getElementById('prev-btn').style.display = step === 1 ? 'none' : 'block'; document.getElementById('next-btn').style.display = step === totalSteps ? 'none' : 'block'; // Mettre à jour le preview à l'étape finale if (step === totalSteps) { updatePreview(); } updateProgress(); } function nextStep() { // Valider l'étape actuelle avant de passer à la suivante if (!validateCurrentStep()) { return; } if (currentStep < totalSteps) { currentStep++; showStep(currentStep); window.scrollTo({ top: 0, behavior: 'smooth' }); } } function prevStep() { if (currentStep > 1) { currentStep--; showStep(currentStep); window.scrollTo({ top: 0, behavior: 'smooth' }); } } // ======================================== // Validation // ======================================== function validateCurrentStep() { const currentStepEl = document.getElementById(`step-${currentStep}`); const requiredFields = currentStepEl.querySelectorAll('[required]'); let isValid = true; requiredFields.forEach(field => { if (!field.value.trim()) { field.style.borderColor = '#ef4444'; isValid = false; } else { field.style.borderColor = '#e2e8f0'; } }); if (!isValid) { alert('Veuillez remplir tous les champs obligatoires.'); } return isValid; } // ======================================== // Ajout dynamique d'entrées // ======================================== function addEducation() { const container = document.getElementById('education-container'); const newEntry = document.createElement('div'); newEntry.className = 'entry-card education-entry'; newEntry.innerHTML = `
`; container.appendChild(newEntry); } function addExperience() { const container = document.getElementById('experience-container'); const newEntry = document.createElement('div'); newEntry.className = 'entry-card experience-entry'; newEntry.innerHTML = `
`; container.appendChild(newEntry); } function addSkill() { const container = document.getElementById('skills-container'); const newEntry = document.createElement('div'); newEntry.className = 'skill-entry form-row'; newEntry.innerHTML = `
`; container.appendChild(newEntry); } function addLanguage() { const container = document.getElementById('languages-container'); const newEntry = document.createElement('div'); newEntry.className = 'language-entry form-row'; newEntry.innerHTML = `
`; container.appendChild(newEntry); } function removeEntry(button) { const entry = button.closest('.entry-card, .skill-entry, .language-entry'); entry.remove(); } // ======================================== // Collecte des données du formulaire // ======================================== function collectFormData() { // Informations personnelles const personalInfo = { full_name: document.getElementById('full_name').value, email: document.getElementById('email').value, phone: document.getElementById('phone').value, address: document.getElementById('address').value, linkedin: document.getElementById('linkedin').value || null, portfolio: document.getElementById('portfolio').value || null, summary: document.getElementById('summary').value || null }; // Formation const education = []; const eduDegrees = document.getElementsByName('edu_degree[]'); const eduInstitutions = document.getElementsByName('edu_institution[]'); const eduStarts = document.getElementsByName('edu_start[]'); const eduEnds = document.getElementsByName('edu_end[]'); const eduDescriptions = document.getElementsByName('edu_description[]'); for (let i = 0; i < eduDegrees.length; i++) { if (eduDegrees[i].value) { education.push({ degree: eduDegrees[i].value, institution: eduInstitutions[i].value, start_date: eduStarts[i].value, end_date: eduEnds[i].value, description: eduDescriptions[i].value || null }); } } // Expériences const experiences = []; const expTitles = document.getElementsByName('exp_title[]'); const expCompanies = document.getElementsByName('exp_company[]'); const expStarts = document.getElementsByName('exp_start[]'); const expEnds = document.getElementsByName('exp_end[]'); const expDescriptions = document.getElementsByName('exp_description[]'); for (let i = 0; i < expTitles.length; i++) { if (expTitles[i].value) { experiences.push({ job_title: expTitles[i].value, company: expCompanies[i].value, start_date: expStarts[i].value, end_date: expEnds[i].value, description: expDescriptions[i].value }); } } // Compétences const skills = []; const skillNames = document.getElementsByName('skill_name[]'); const skillLevels = document.getElementsByName('skill_level[]'); for (let i = 0; i < skillNames.length; i++) { if (skillNames[i].value) { skills.push({ name: skillNames[i].value, level: skillLevels[i].value || null }); } } // Langues const languages = []; const langNames = document.getElementsByName('lang_name[]'); const langLevels = document.getElementsByName('lang_level[]'); for (let i = 0; i < langNames.length; i++) { if (langNames[i].value) { languages.push({ name: langNames[i].value, level: langLevels[i].value }); } } // Hobbies const hobbiesInput = document.getElementById('hobbies').value; const hobbies = hobbiesInput ? hobbiesInput.split(',').map(h => h.trim()).filter(h => h) : null; return { personal_info: personalInfo, education: education, experiences: experiences, skills: skills, languages: languages, hobbies: hobbies }; } // ======================================== // Preview // ======================================== function updatePreview() { const data = collectFormData(); // Personal Info const personalHtml = `
Nom: ${data.personal_info.full_name}
Email: ${data.personal_info.email}
Téléphone: ${data.personal_info.phone}
Adresse: ${data.personal_info.address}
${data.personal_info.linkedin ? `
LinkedIn: ${data.personal_info.linkedin}
` : ''} ${data.personal_info.summary ? `
Résumé: ${data.personal_info.summary}
` : '
Le résumé sera généré par l\'IA
'} `; document.getElementById('preview-personal').innerHTML = personalHtml; // Education const educationHtml = data.education.map(edu => `
${edu.degree} - ${edu.institution}
${edu.start_date} - ${edu.end_date}
`).join(''); document.getElementById('preview-education').innerHTML = educationHtml || 'Aucune formation ajoutée'; // Experience const experienceHtml = data.experiences.map(exp => `
${exp.job_title} - ${exp.company}
${exp.start_date} - ${exp.end_date}
`).join(''); document.getElementById('preview-experience').innerHTML = experienceHtml || 'Aucune expérience ajoutée'; // Skills & Languages const skillsHtml = `
Compétences: ${data.skills.map(s => s.name).join(', ') || 'Aucune'}
Langues: ${data.languages.map(l => `${l.name} (${l.level})`).join(', ') || 'Aucune'}
${data.hobbies ? `
Centres d'intérêt: ${data.hobbies.join(', ')}
` : ''} `; document.getElementById('preview-skills').innerHTML = skillsHtml; } // ======================================== // IA Features // ======================================== async function generateSummary() { const btn = document.querySelector('.btn-ai'); btn.disabled = true; btn.textContent = '⏳ Génération...'; try { const data = collectFormData(); const response = await fetch(`${API_BASE_URL}/enhance-summary`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await response.json(); if (result.success) { document.getElementById('summary').value = result.summary; } else { alert('Erreur lors de la génération du résumé'); } } catch (error) { console.error('Erreur:', error); alert('Erreur de connexion au serveur'); } finally { btn.disabled = false; btn.textContent = '✨ Générer avec l\'IA'; } } async function enhanceExperience(button) { const entry = button.closest('.entry-card'); const textarea = entry.querySelector('textarea[name="exp_description[]"]'); const titleInput = entry.querySelector('input[name="exp_title[]"]'); if (!textarea.value.trim()) { alert('Veuillez d\'abord entrer une description à améliorer'); return; } button.disabled = true; button.textContent = '⏳ Amélioration...'; try { const response = await fetch(`${API_BASE_URL}/enhance-experience?description=${encodeURIComponent(textarea.value)}&job_title=${encodeURIComponent(titleInput.value)}`, { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (result.success) { textarea.value = result.enhanced_description; } else { alert('Erreur lors de l\'amélioration'); } } catch (error) { console.error('Erreur:', error); alert('Erreur de connexion au serveur'); } finally { button.disabled = false; button.textContent = '✨ Améliorer avec l\'IA'; } } // ======================================== // Génération du CV // ======================================== async function generateCV(event) { event.preventDefault(); const generateBtn = document.getElementById('generate-btn'); const loading = document.getElementById('loading'); generateBtn.disabled = true; generateBtn.querySelector('.btn-text').classList.add('hidden'); generateBtn.querySelector('.btn-loading').classList.remove('hidden'); loading.classList.remove('hidden'); try { const data = collectFormData(); const response = await fetch(`${API_BASE_URL}/generate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await response.json(); if (result.success) { showDownloadModal(result.message, result.file_path); } else { alert('Erreur: ' + (result.detail || result.message)); } } catch (error) { console.error('Erreur:', error); alert('Erreur de connexion au serveur. Vérifiez que le backend est lancé.'); } finally { generateBtn.disabled = false; generateBtn.querySelector('.btn-text').classList.remove('hidden'); generateBtn.querySelector('.btn-loading').classList.add('hidden'); loading.classList.add('hidden'); } } // ======================================== // Modal // ======================================== function showDownloadModal(message, filename) { const modal = document.getElementById('download-modal'); const messageEl = document.getElementById('modal-message'); const downloadLink = document.getElementById('download-link'); messageEl.textContent = message; downloadLink.href = `${API_BASE_URL}/download/${filename}`; downloadLink.download = filename; modal.classList.remove('hidden'); } function closeModal() { document.getElementById('download-modal').classList.add('hidden'); } // ======================================== // Initialisation // ======================================== document.addEventListener('DOMContentLoaded', function() { // Initialiser la première étape showStep(1); // Gérer la soumission du formulaire document.getElementById('cv-form').addEventListener('submit', generateCV); // Fermer le modal en cliquant à l'extérieur document.getElementById('download-modal').addEventListener('click', function(e) { if (e.target === this) { closeModal(); } }); });