Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>AI CV Screener</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script> | |
| tailwind.config = { | |
| theme: { | |
| extend: { | |
| fontFamily: { | |
| sans: ['Inter', 'system-ui', 'sans-serif'], | |
| }, | |
| colors: { | |
| slate: { | |
| 50: '#f8fafc', | |
| 100: '#f1f5f9', | |
| 200: '#e2e8f0', | |
| 300: '#cbd5e1', | |
| 400: '#94a3b8', | |
| 500: '#64748b', | |
| 600: '#475569', | |
| 700: '#334155', | |
| 800: '#1e293b', | |
| 900: '#0f172a', | |
| }, | |
| indigo: { | |
| 50: '#eef2ff', | |
| 100: '#e0e7ff', | |
| 200: '#c7d2fe', | |
| 300: '#a5b4fc', | |
| 400: '#818cf8', | |
| 500: '#6366f1', | |
| 600: '#4f46e5', | |
| 700: '#4338ca', | |
| 800: '#3730a3', | |
| 900: '#312e81', | |
| } | |
| } | |
| } | |
| } | |
| } | |
| </script> | |
| </head> | |
| <body class="font-sans bg-slate-50 min-h-screen"> | |
| <!-- Header --> | |
| <header class="bg-white shadow-sm border-b border-slate-200"> | |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
| <div class="flex justify-between items-center py-4"> | |
| <h1 class="text-2xl font-bold text-slate-800">AI CV Screener</h1> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- Main Container --> | |
| <main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> | |
| <!-- Application Sections --> | |
| <div class="space-y-8"> | |
| <!-- Section 1: Enter Job Details --> | |
| <div class="bg-white rounded-lg shadow-sm border border-slate-200 p-6"> | |
| <h2 class="text-xl font-semibold text-slate-800 mb-4">1. Enter Job Details</h2> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-sm font-medium text-slate-700 mb-2">Job Title</label> | |
| <input type="text" id="job-title" class="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500" placeholder="e.g., Senior Software Engineer"> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-slate-700 mb-2">Seniority Level</label> | |
| <select id="seniority-level" class="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"> | |
| <option value="">Select seniority level</option> | |
| <option value="internship">Internship</option> | |
| <option value="junior">Junior</option> | |
| <option value="mid-level">Mid-Level</option> | |
| <option value="senior">Senior</option> | |
| <option value="lead">Lead</option> | |
| <option value="manager">Manager</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-slate-700 mb-2">Minimum Years of Experience</label> | |
| <input type="number" id="min-experience" class="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500" placeholder="e.g., 5" min="0"> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-slate-700 mb-2">Job Description</label> | |
| <textarea id="job-description" class="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 h-32" placeholder="Describe the role, responsibilities, and requirements..."></textarea> | |
| </div> | |
| <div> | |
| <button id="extract-skills-btn" class="bg-indigo-600 text-white px-6 py-2 rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition-colors"> | |
| Extract Key Skills & Responsibilities | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Section 2: Define Screening Criteria --> | |
| <div id="screening-criteria" class="bg-white rounded-lg shadow-sm border border-slate-200 p-6 hidden"> | |
| <h2 class="text-xl font-semibold text-slate-800 mb-4">2. Define Screening Criteria</h2> | |
| <div class="space-y-6"> | |
| <!-- Must-Have Skills --> | |
| <div> | |
| <h3 class="text-lg font-medium text-slate-800 mb-3">Must-Have Skills</h3> | |
| <div id="must-have-skills" class="flex flex-wrap gap-2 min-h-12 p-2 border border-slate-200 rounded-md"> | |
| <div class="text-slate-500 italic text-sm">Extracted skills will appear here</div> | |
| </div> | |
| </div> | |
| <!-- Nice-to-Have Skills --> | |
| <div> | |
| <h3 class="text-lg font-medium text-slate-800 mb-3">Nice-to-Have Skills</h3> | |
| <div id="nice-to-have-skills" class="flex flex-wrap gap-2 min-h-12 p-2 border border-slate-200 rounded-md"> | |
| <div class="text-slate-500 italic text-sm">Extracted skills will appear here</div> | |
| </div> | |
| </div> | |
| <!-- Key Responsibilities & Phrases --> | |
| <div> | |
| <label class="block text-sm font-medium text-slate-700 mb-2">Key Responsibilities & Phrases</label> | |
| <textarea id="key-responsibilities" class="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 h-24" placeholder="Key responsibilities and phrases to look for..."></textarea> | |
| </div> | |
| <!-- Negative Keywords --> | |
| <div> | |
| <label class="block text-sm font-medium text-slate-700 mb-2">Negative Keywords</label> | |
| <input type="text" id="negative-keywords" class="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500" placeholder="Keywords that would disqualify candidates..."> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Section 3: Set Education & Upload CVs --> | |
| <div class="bg-white rounded-lg shadow-sm border border-slate-200 p-6"> | |
| <h2 class="text-xl font-semibold text-slate-800 mb-4">3. Set Education & Upload CVs</h2> | |
| <div class="space-y-4"> | |
| <div class="text-slate-500 italic"> | |
| Education requirements and CV upload functionality will appear here. | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Section 4: Screening Results --> | |
| <div id="screening-results" class="bg-white rounded-lg shadow-sm border border-slate-200 p-6 hidden"> | |
| <h2 class="text-xl font-semibold text-slate-800 mb-4">4. Screening Results: Top Candidates</h2> | |
| <div class="space-y-4"> | |
| <div class="text-slate-500 italic"> | |
| Screening results will appear here after CVs are processed. | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <script> | |
| // Skill extraction function | |
| function extractSkillsFromDescription(text) { | |
| const commonSkills = [ | |
| 'JavaScript', 'React', 'Python', 'Java', 'HTML', 'CSS', 'Node.js', 'TypeScript', | |
| 'SQL', 'MongoDB', 'Git', 'AWS', 'Docker', 'Agile', 'Scrum', 'REST', 'API', | |
| 'Machine Learning', 'AI', 'Data Analysis', 'Project Management', 'Leadership', | |
| 'Communication', 'Problem Solving', 'Teamwork', 'Testing', 'Debugging' | |
| ]; | |
| const extracted = []; | |
| const words = text.toLowerCase().split(/\W+/); | |
| commonSkills.forEach(skill => { | |
| if (words.includes(skill.toLowerCase()) || text.toLowerCase().includes(skill.toLowerCase())) { | |
| extracted.push(skill); | |
| } | |
| }); | |
| // Add some random skills if not enough were found | |
| if (extracted.length < 3) { | |
| const additionalSkills = commonSkills.filter(skill => !extracted.includes(skill)); | |
| const randomSkills = additionalSkills.sort(() => 0.5 - Math.random()).slice(0, 3); | |
| extracted.push(...randomSkills); | |
| } | |
| return extracted.slice(0, 8); // Limit to 8 skills | |
| } | |
| // Function to create skill tag element | |
| function createSkillTag(skill) { | |
| const tag = document.createElement('div'); | |
| tag.className = 'inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-indigo-100 text-indigo-800 cursor-pointer hover:bg-indigo-200 transition-colors'; | |
| tag.innerHTML = ` | |
| ${skill} | |
| <span class="ml-2 text-indigo-600 hover:text-indigo-800">Γ</span> | |
| `; | |
| tag.addEventListener('click', function(e) { | |
| if (e.target.tagName === 'SPAN') { | |
| tag.remove(); | |
| } | |
| }); | |
| return tag; | |
| } | |
| // Event listener for extract skills button | |
| document.getElementById('extract-skills-btn').addEventListener('click', function() { | |
| const jobDescription = document.getElementById('job-description').value; | |
| const button = this; | |
| const originalText = button.textContent; | |
| if (!jobDescription.trim()) { | |
| alert('Please enter a job description first.'); | |
| return; | |
| } | |
| // Show loading state | |
| button.disabled = true; | |
| button.textContent = 'Extracting skills...'; | |
| button.classList.add('opacity-50'); | |
| // Show loading indicator | |
| const loadingText = document.createElement('div'); | |
| loadingText.className = 'text-sm text-slate-500 mt-2'; | |
| loadingText.textContent = 'Analyzing job description...'; | |
| button.parentNode.appendChild(loadingText); | |
| // Simulate API call with timeout | |
| setTimeout(() => { | |
| // Hide loading indicator | |
| loadingText.remove(); | |
| // Reset button | |
| button.disabled = false; | |
| button.textContent = originalText; | |
| button.classList.remove('opacity-50'); | |
| // Extract skills | |
| const skills = extractSkillsFromDescription(jobDescription); | |
| // Clear existing skills | |
| const mustHaveDiv = document.getElementById('must-have-skills'); | |
| const niceToHaveDiv = document.getElementById('nice-to-have-skills'); | |
| mustHaveDiv.innerHTML = ''; | |
| niceToHaveDiv.innerHTML = ''; | |
| // Split skills between must-have and nice-to-have | |
| const mustHaveCount = Math.min(Math.ceil(skills.length * 0.6), 5); // 60% as must-have, max 5 | |
| skills.forEach((skill, index) => { | |
| const tag = createSkillTag(skill); | |
| if (index < mustHaveCount) { | |
| mustHaveDiv.appendChild(tag); | |
| } else { | |
| niceToHaveDiv.appendChild(tag); | |
| } | |
| }); | |
| // Show the screening criteria section | |
| document.getElementById('screening-criteria').classList.remove('hidden'); | |
| }, 1000); | |
| }); | |
| </script> | |
| </body> | |
| </html> |