ynov-cv / index.html
RaphaelC76's picture
Add 1 files
083f100 verified
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ynov CV Analyzer - Ycampers</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.gradient-bg {
background: linear-gradient(135deg, #4bb7a9 0%, #3a9a8d 100%);
}
.progress-bar {
height: 20px;
border-radius: 10px;
background-color: #e5e7eb;
overflow: hidden;
}
.progress-fill {
height: 100%;
transition: width 0.5s ease-in-out;
}
.pulse {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(75, 183, 169, 0.7); }
70% { box-shadow: 0 0 0 10px rgba(75, 183, 169, 0); }
100% { box-shadow: 0 0 0 0 rgba(75, 183, 169, 0); }
}
.floating {
animation: floating 3s ease-in-out infinite;
}
@keyframes floating {
0% { transform: translateY(0px); }
50% { transform: translateY(-10px); }
100% { transform: translateY(0px); }
}
</style>
</head>
<body class="bg-gray-50 font-sans">
<!-- Header -->
<header class="gradient-bg text-white shadow-lg">
<div class="container mx-auto px-4 py-6 flex justify-between items-center">
<div class="flex items-center space-x-4">
<img src="https://privacy.ynov.com/img/logo_ynov_campus.svg" alt="Ynov Campus" class="h-12">
<h1 class="text-2xl font-bold">Ynov CV Analyzer</h1>
</div>
<div class="hidden md:block">
<span class="bg-white text-[#4bb7a9] px-4 py-2 rounded-full font-semibold">Ycampers 2025</span>
</div>
</div>
</header>
<!-- Main Content -->
<main class="container mx-auto px-4 py-8">
<div class="max-w-4xl mx-auto">
<!-- Upload Section -->
<section class="bg-white rounded-xl shadow-md p-6 mb-8">
<h2 class="text-2xl font-bold text-gray-800 mb-4">Analyse ton CV</h2>
<p class="text-gray-600 mb-6">Télécharge ton CV pour obtenir une analyse détaillée de son contenu et de sa forme, avec des conseils pour l'améliorer.</p>
<div id="upload-container" class="border-2 border-dashed border-[#4bb7a9] rounded-xl p-8 text-center cursor-pointer hover:bg-gray-50 transition">
<div class="floating text-[#4bb7a9] mb-4">
<i class="fas fa-file-upload text-5xl"></i>
</div>
<p class="font-semibold text-gray-700">Glisse-dépose ton CV ici ou clique pour sélectionner</p>
<p class="text-sm text-gray-500 mt-2">Formats supportés: PDF, DOC, DOCX (max 5MB)</p>
<input type="file" id="cv-upload" class="hidden" accept=".pdf,.doc,.docx">
</div>
<div id="uploading" class="hidden text-center py-8">
<div class="inline-block animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-[#4bb7a9] mb-4"></div>
<p class="text-gray-700">Analyse en cours avec Deepseek AI...</p>
</div>
</section>
<!-- Results Section -->
<section id="results-section" class="hidden bg-white rounded-xl shadow-md p-6 mb-8">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold text-gray-800">Résultats de l'analyse</h2>
<div id="score-display" class="flex items-center">
<div class="text-3xl font-bold mr-2">0%</div>
<div class="text-sm text-gray-500">Score global</div>
</div>
</div>
<!-- Score Card -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
<div class="bg-gray-50 rounded-lg p-6">
<h3 class="font-semibold text-gray-700 mb-4">Détail du score</h3>
<div class="space-y-4">
<div>
<div class="flex justify-between mb-1">
<span class="text-sm font-medium text-gray-600">Contenu</span>
<span class="text-sm font-medium text-gray-600" id="content-score">0%</span>
</div>
<div class="progress-bar">
<div id="content-fill" class="progress-fill bg-red-500" style="width: 0%"></div>
</div>
</div>
<div>
<div class="flex justify-between mb-1">
<span class="text-sm font-medium text-gray-600">Forme</span>
<span class="text-sm font-medium text-gray-600" id="form-score">0%</span>
</div>
<div class="progress-bar">
<div id="form-fill" class="progress-fill bg-red-500" style="width: 0%"></div>
</div>
</div>
<div>
<div class="flex justify-between mb-1">
<span class="text-sm font-medium text-gray-600">Clarté</span>
<span class="text-sm font-medium text-gray-600" id="clarity-score">0%</span>
</div>
<div class="progress-bar">
<div id="clarity-fill" class="progress-fill bg-red-500" style="width: 0%"></div>
</div>
</div>
<div>
<div class="flex justify-between mb-1">
<span class="text-sm font-medium text-gray-600">Pertinence</span>
<span class="text-sm font-medium text-gray-600" id="relevance-score">0%</span>
</div>
<div class="progress-bar">
<div id="relevance-fill" class="progress-fill bg-red-500" style="width: 0%"></div>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 rounded-lg p-6">
<h3 class="font-semibold text-gray-700 mb-4">Visualisation du score</h3>
<canvas id="score-chart" height="200"></canvas>
</div>
</div>
<!-- Recommendations -->
<div class="bg-[#f8fafc] border border-[#e2e8f0] rounded-lg p-6 mb-6">
<h3 class="font-semibold text-gray-700 mb-4 flex items-center">
<i class="fas fa-lightbulb text-[#4bb7a9] mr-2"></i>
Recommandations pour améliorer ton CV
</h3>
<div id="recommendations" class="space-y-3">
<!-- Dynamically filled by JS -->
</div>
</div>
<!-- New CV Button -->
<div class="text-center">
<button id="new-cv-btn" class="bg-[#4bb7a9] hover:bg-[#3a9a8d] text-white font-medium py-2 px-6 rounded-lg transition duration-300 flex items-center mx-auto">
<i class="fas fa-redo mr-2"></i>
Analyser un nouveau CV
</button>
</div>
</section>
<!-- AI Analysis -->
<section id="ai-analysis" class="hidden bg-white rounded-xl shadow-md p-6">
<h2 class="text-2xl font-bold text-gray-800 mb-4">Analyse approfondie par Deepseek AI</h2>
<div class="bg-[#4bb7a9] bg-opacity-10 border-l-4 border-[#4bb7a9] p-4 mb-6">
<div class="flex">
<div class="flex-shrink-0">
<i class="fas fa-robot text-[#4bb7a9]"></i>
</div>
<div class="ml-3">
<p id="ai-feedback" class="text-sm text-gray-700">
<!-- AI feedback will be inserted here -->
</p>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<h3 class="font-semibold text-gray-700 mb-3">Points forts</h3>
<ul id="strengths" class="space-y-2">
<!-- Strengths will be inserted here -->
</ul>
</div>
<div>
<h3 class="font-semibold text-gray-700 mb-3">Points à améliorer</h3>
<ul id="weaknesses" class="space-y-2">
<!-- Weaknesses will be inserted here -->
</ul>
</div>
</div>
</section>
</div>
</main>
<!-- Footer -->
<footer class="gradient-bg text-white py-6">
<div class="container mx-auto px-4">
<div class="flex flex-col md:flex-row justify-between items-center">
<div class="mb-4 md:mb-0">
<img src="https://privacy.ynov.com/img/logo_ynov_campus.svg" alt="Ynov Campus" class="h-8">
</div>
<div class="text-center md:text-right">
<p class="text-sm">© YNOV Campus 2025 - Tous droits réservés</p>
<p class="text-xs opacity-75 mt-1">Analyseur de CV pour Ycampers - Powered by Deepseek AI</p>
</div>
</div>
</div>
</footer>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Elements
const uploadContainer = document.getElementById('upload-container');
const fileInput = document.getElementById('cv-upload');
const uploadingDiv = document.getElementById('uploading');
const resultsSection = document.getElementById('results-section');
const aiAnalysisSection = document.getElementById('ai-analysis');
const newCvBtn = document.getElementById('new-cv-btn');
// Upload handling
uploadContainer.addEventListener('click', () => fileInput.click());
uploadContainer.addEventListener('dragover', (e) => {
e.preventDefault();
uploadContainer.classList.add('border-[#4bb7a9]', 'bg-gray-100');
});
uploadContainer.addEventListener('dragleave', () => {
uploadContainer.classList.remove('border-[#4bb7a9]', 'bg-gray-100');
});
uploadContainer.addEventListener('drop', (e) => {
e.preventDefault();
uploadContainer.classList.remove('border-[#4bb7a9]', 'bg-gray-100');
if (e.dataTransfer.files.length) {
fileInput.files = e.dataTransfer.files;
handleFileUpload();
}
});
fileInput.addEventListener('change', handleFileUpload);
// New CV button handler
newCvBtn.addEventListener('click', resetForm);
function resetForm() {
// Hide results and show upload form
resultsSection.classList.add('hidden');
aiAnalysisSection.classList.add('hidden');
uploadingDiv.classList.add('hidden');
uploadContainer.classList.remove('hidden');
// Reset file input
fileInput.value = '';
// Reset scores display
document.querySelectorAll('.progress-fill').forEach(el => {
el.style.width = '0%';
el.classList.remove('bg-yellow-500', 'bg-green-500');
el.classList.add('bg-red-500');
});
document.querySelectorAll('[id$="-score"]').forEach(el => {
el.textContent = '0%';
});
document.getElementById('score-display').querySelector('div:first-child').textContent = '0%';
document.getElementById('score-display').classList.remove('pulse');
// Reset recommendations and AI feedback
document.getElementById('recommendations').innerHTML = '';
document.getElementById('ai-feedback').textContent = '';
document.getElementById('strengths').innerHTML = '';
document.getElementById('weaknesses').innerHTML = '';
// Destroy previous chart if exists
const chartCanvas = document.getElementById('score-chart');
if (chartCanvas.chart) {
chartCanvas.chart.destroy();
}
}
function handleFileUpload() {
if (fileInput.files.length) {
const file = fileInput.files[0];
const validTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'];
if (!validTypes.includes(file.type) && !file.name.match(/\.(pdf|doc|docx)$/)) {
alert('Format de fichier non supporté. Veuillez uploader un PDF, DOC ou DOCX.');
return;
}
if (file.size > 5 * 1024 * 1024) {
alert('Le fichier est trop volumineux (max 5MB).');
return;
}
// Show uploading state
uploadContainer.classList.add('hidden');
uploadingDiv.classList.remove('hidden');
// Simulate API call to Deepseek AI
setTimeout(() => {
analyzeCV(file.name);
}, 2500);
}
}
function analyzeCV(filename) {
// Hide uploading and show results
uploadingDiv.classList.add('hidden');
resultsSection.classList.remove('hidden');
aiAnalysisSection.classList.remove('hidden');
// Generate random scores for demo purposes
const contentScore = Math.floor(Math.random() * 30) + 50; // 50-80
const formScore = Math.floor(Math.random() * 40) + 30; // 30-70
const clarityScore = Math.floor(Math.random() * 35) + 45; // 45-80
const relevanceScore = Math.floor(Math.random() * 25) + 60; // 60-85
const overallScore = Math.round((contentScore + formScore + clarityScore + relevanceScore) / 4);
// Update scores
updateScore('content', contentScore);
updateScore('form', formScore);
updateScore('clarity', clarityScore);
updateScore('relevance', relevanceScore);
updateOverallScore(overallScore);
// Create chart
createScoreChart(contentScore, formScore, clarityScore, relevanceScore);
// Generate recommendations based on scores
generateRecommendations(contentScore, formScore, clarityScore, relevanceScore);
// Generate AI feedback
generateAIFeedback(filename, overallScore, contentScore, formScore, clarityScore, relevanceScore);
}
function updateScore(type, score) {
const scoreElement = document.getElementById(`${type}-score`);
const fillElement = document.getElementById(`${type}-fill`);
scoreElement.textContent = `${score}%`;
fillElement.style.width = `${score}%`;
// Update color based on score
if (score <= 40) {
fillElement.classList.remove('bg-yellow-500', 'bg-green-500');
fillElement.classList.add('bg-red-500');
} else if (score <= 70) {
fillElement.classList.remove('bg-red-500', 'bg-green-500');
fillElement.classList.add('bg-yellow-500');
} else {
fillElement.classList.remove('bg-red-500', 'bg-yellow-500');
fillElement.classList.add('bg-green-500');
}
}
function updateOverallScore(score) {
const scoreDisplay = document.getElementById('score-display');
const percentageElement = scoreDisplay.querySelector('div:first-child');
percentageElement.textContent = `${score}%`;
// Pulse animation for good scores
if (score > 70) {
scoreDisplay.classList.add('pulse');
} else {
scoreDisplay.classList.remove('pulse');
}
}
function createScoreChart(content, form, clarity, relevance) {
const ctx = document.getElementById('score-chart').getContext('2d');
// Store chart instance on canvas element
ctx.canvas.chart = new Chart(ctx, {
type: 'radar',
data: {
labels: ['Contenu', 'Forme', 'Clarté', 'Pertinence'],
datasets: [{
label: 'Score',
data: [content, form, clarity, relevance],
backgroundColor: 'rgba(75, 183, 169, 0.2)',
borderColor: '#4bb7a9',
borderWidth: 2,
pointBackgroundColor: '#4bb7a9',
pointRadius: 4
}]
},
options: {
scales: {
r: {
angleLines: {
display: true,
color: 'rgba(0, 0, 0, 0.1)'
},
suggestedMin: 0,
suggestedMax: 100,
ticks: {
stepSize: 20,
backdropColor: 'transparent'
},
grid: {
color: 'rgba(0, 0, 0, 0.1)'
},
pointLabels: {
font: {
size: 12,
weight: 'bold'
}
}
}
},
plugins: {
legend: {
display: false
}
},
elements: {
line: {
tension: 0.1
}
}
}
});
}
function generateRecommendations(content, form, clarity, relevance) {
const recommendationsContainer = document.getElementById('recommendations');
recommendationsContainer.innerHTML = '';
const allRecommendations = [];
// Content recommendations
if (content < 50) {
allRecommendations.push({
icon: 'fa-file-alt',
text: 'Ajoute plus de détails sur tes expériences professionnelles et formations.'
});
allRecommendations.push({
icon: 'fa-tasks',
text: 'Structure mieux les sections de ton CV (expériences, formations, compétences).'
});
} else if (content < 70) {
allRecommendations.push({
icon: 'fa-file-alt',
text: 'Enrichis les descriptions de tes expériences avec des réalisations concrètes.'
});
}
// Form recommendations
if (form < 50) {
allRecommendations.push({
icon: 'fa-paint-brush',
text: 'Améliore la mise en page pour une meilleure lisibilité.'
});
allRecommendations.push({
icon: 'fa-font',
text: 'Utilise une police professionnelle et des tailles cohérentes.'
});
} else if (form < 70) {
allRecommendations.push({
icon: 'fa-align-left',
text: 'Soigne davantage l\'alignement et les espacements.'
});
}
// Clarity recommendations
if (clarity < 60) {
allRecommendations.push({
icon: 'fa-spell-check',
text: 'Corrige les fautes d\'orthographe et de grammaire.'
});
allRecommendations.push({
icon: 'fa-bullhorn',
text: 'Utilise un langage plus clair et concis.'
});
}
// Relevance recommendations
if (relevance < 60) {
allRecommendations.push({
icon: 'fa-bullseye',
text: 'Adapte ton CV au secteur d\'activité visé.'
});
allRecommendations.push({
icon: 'fa-filter',
text: 'Mets en avant les expériences les plus pertinentes.'
});
} else if (relevance < 80) {
allRecommendations.push({
icon: 'fa-star',
text: 'Mets davantage en valeur tes compétences clés.'
});
}
// Default recommendations if none generated
if (allRecommendations.length === 0) {
allRecommendations.push({
icon: 'fa-thumbs-up',
text: 'Ton CV est déjà bien construit! Pense juste à le personnaliser pour chaque candidature.'
});
}
// Add recommendations to DOM
allRecommendations.forEach(rec => {
const recElement = document.createElement('div');
recElement.className = 'flex items-start';
recElement.innerHTML = `
<i class="fas ${rec.icon} text-[#4bb7a9] mt-1 mr-3"></i>
<span class="text-gray-700">${rec.text}</span>
`;
recommendationsContainer.appendChild(recElement);
});
}
function generateAIFeedback(filename, overallScore, content, form, clarity, relevance) {
const feedbackElement = document.getElementById('ai-feedback');
const strengthsList = document.getElementById('strengths');
const weaknessesList = document.getElementById('weaknesses');
// Generate feedback based on scores
let feedback = '';
let strengths = [];
let weaknesses = [];
if (overallScore > 70) {
feedback = `L'analyse de ton CV "${filename}" montre un document globalement bien construit avec un score de ${overallScore}%. `;
} else if (overallScore > 50) {
feedback = `Ton CV "${filename}" obtient un score moyen de ${overallScore}% avec plusieurs points à améliorer. `;
} else {
feedback = `L'analyse de ton CV "${filename}" révèle un score faible de ${overallScore}% avec des améliorations importantes à apporter. `;
}
// Content feedback
if (content > 70) {
strengths.push('Contenu riche et détaillé');
} else if (content < 50) {
weaknesses.push('Contenu trop succinct ou incomplet');
}
// Form feedback
if (form > 70) {
strengths.push('Mise en page professionnelle');
} else if (form < 50) {
weaknesses.push('Problèmes de mise en page');
}
// Clarity feedback
if (clarity > 70) {
strengths.push('Expression claire et précise');
} else if (clarity < 50) {
weaknesses.push('Problèmes de clarté ou de langue');
}
// Relevance feedback
if (relevance > 70) {
strengths.push('Pertinence des informations par rapport au secteur');
} else if (relevance < 50) {
weaknesses.push('Manque de pertinence pour le secteur visé');
}
// Default if no strengths/weaknesses detected (shouldn't happen)
if (strengths.length === 0) strengths.push('Motivation et potentiel');
if (weaknesses.length === 0) weaknesses.push('Aucun point faible majeur détecté');
// Complete feedback
feedback += `Voici une analyse détaillée avec des pistes concrètes pour optimiser ton CV et maximiser tes chances.`;
// Update DOM
feedbackElement.textContent = feedback;
strengthsList.innerHTML = strengths.map(str =>
`<li class="flex items-start">
<i class="fas fa-check-circle text-green-500 mt-1 mr-2"></i>
<span>${str}</span>
</li>`
).join('');
weaknessesList.innerHTML = weaknesses.map(weak =>
`<li class="flex items-start">
<i class="fas fa-exclamation-circle text-red-500 mt-1 mr-2"></i>
<span>${weak}</span>
</li>`
).join('');
}
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=RaphaelC76/ynov-cv" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>