resume-analyzer-ai/index.html

701 lines
30 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>ИИ-Рекрутер — Анализ вакансий и резюме</title>
<style>
:root{--ink:#0F1218;--cyan:#00E5FF;--cyan-50:#E8FCFF;--white:#fff;--gray-500:#5B6573;--gray-100:#F2F4F7;--success:#10B981;--warning:#F59E0B;--danger:#EF4444}
*{box-sizing:border-box;margin:0;padding:0}
body{font:17px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;color:var(--ink);background:var(--gray-100)}
.container{max-width:1200px;margin:0 auto;padding:80px 24px}
.hero{background:var(--ink);color:var(--white);padding:60px 0}
.hero h1{font-size:48px;font-weight:800;line-height:1.1;margin-bottom:16px}
.hero p{font-size:20px;color:#9aa3b2;max-width:700px;margin-bottom:32px}
.btn{display:inline-block;background:var(--cyan);color:var(--ink);padding:14px 28px;border-radius:8px;font-weight:700;text-decoration:none;border:none;cursor:pointer}
.btn:hover{background:#1be5ff}
.btn-secondary{background:transparent;color:var(--ink);border:2px solid var(--ink);padding:12px 26px;border-radius:8px}
.tabs{display:flex;gap:8px;margin-bottom:32px;flex-wrap:wrap}
.tab{padding:12px 24px;background:var(--white);border-radius:8px;cursor:pointer;font-weight:600;border:2px solid transparent}
.tab.active{border-color:var(--cyan);background:var(--cyan-50)}
.tab-content{display:none}
.tab-content.active{display:block}
.card{background:var(--white);border-radius:16px;padding:32px;margin-bottom:24px;box-shadow:0 4px 12px rgba(0,0,0,0.05)}
.form-group{margin-bottom:20px}
.form-group label{display:block;font-weight:600;margin-bottom:8px}
.form-group input,.form-group textarea,.form-group select{width:100%;padding:12px;border:2px solid var(--gray-100);border-radius:8px;font-family:inherit;font-size:16px}
.form-group textarea{height:150px;resize:vertical}
.form-group input:focus,.form-group textarea:focus,.form-group select:focus{outline:none;border-color:var(--cyan)}
.radio-group{display:flex;gap:16px;flex-wrap:wrap}
.radio-group label{display:flex;align-items:center;gap:8px;font-weight:normal;cursor:pointer}
.upload-area{border:3px dashed var(--gray-100);border-radius:16px;padding:48px;text-align:center;cursor:pointer;transition:all 0.3s}
.upload-area:hover{border-color:var(--cyan);background:var(--cyan-50)}
.upload-area.dragover{border-color:var(--cyan);background:var(--cyan-50)}
.file-list{margin-top:24px}
.file-item{display:flex;justify-content:space-between;align-items:center;padding:12px;background:var(--gray-100);border-radius:8px;margin-bottom:8px}
.file-item .name{font-weight:600}
.file-item .size{color:var(--gray-500);font-size:14px}
.file-item .remove{color:var(--danger);cursor:pointer;font-weight:600}
.results-table{width:100%;border-collapse:collapse;margin-top:24px}
.results-table th,.results-table td{padding:16px;text-align:left;border-bottom:1px solid var(--gray-100)}
.results-table th{background:var(--gray-100);font-weight:600}
.results-table tr:hover{background:var(--cyan-50)}
.score-badge{display:inline-block;padding:4px 12px;border-radius:20px;font-weight:700;font-size:14px}
.score-badge.high{background:#D1FAE5;color:#065F46}
.score-badge.medium{background:#FEF3C7;color:#92400E}
.score-badge.low{background:#FEE2E2;color:#991B1B}
.candidate-modal{position:fixed;inset:0;background:rgba(0,0,0,0.5);display:none;align-items:center;justify-content:center;z-index:1000;padding:20px}
.candidate-modal.active{display:flex}
.modal-content{background:var(--white);border-radius:16px;padding:32px;max-width:900px;width:100%;max-height:90vh;overflow-y:auto}
.modal-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:24px}
.modal-close{background:none;border:none;font-size:24px;cursor:pointer}
.section-title{font-size:22px;font-weight:700;margin:24px 0 16px;border-bottom:2px solid var(--gray-100);padding-bottom:8px}
.progress-bar{height:8px;background:var(--gray-100);border-radius:4px;overflow:hidden;margin-top:8px}
.progress-fill{height:100%;background:var(--cyan);border-radius:4px}
@media (max-width:768px){.hero h1{font-size:36px}.container{padding:48px 20px}.tabs{gap:4px}.tab{padding:10px 16px;font-size:14px}}
</style>
</head>
<body>
<section class="hero">
<div class="container">
<h1>ИИ-Рекрутер</h1>
<p>Автоматический анализ вакансий и резюме. Загрузите описание вакансии и резюме кандидатов — ИИ-агент найдет лучших.</p>
<button class="btn" onclick="showTab('job')">Начать анализ</button>
</div>
</section>
<div class="container">
<div class="tabs">
<div class="tab active" onclick="showTab('job')">1. Вакансия</div>
<div class="tab" onclick="showTab('requirements')">2. Требования</div>
<div class="tab" onclick="showTab('upload')">3. Загрузка резюме</div>
<div class="tab" onclick="showTab('results')">4. Результаты</div>
</div>
<!-- Блок 1: Информация о вакансии -->
<div id="job" class="tab-content active">
<div class="card">
<h2 style="margin-bottom:24px">Информация о вакансии</h2>
<div class="form-group">
<label>Название вакансии</label>
<input type="text" id="jobTitle" placeholder="например, Senior Python Developer">
</div>
<div class="form-group">
<label>Компания</label>
<input type="text" id="company" placeholder="название компании">
</div>
<div class="form-group">
<label>Отдел</label>
<input type="text" id="department" placeholder="например, IT-отдел">
</div>
<div class="form-group">
<label>Город</label>
<input type="text" id="city" placeholder="например, Алматы">
</div>
<div class="form-group">
<label>Формат работы</label>
<div class="radio-group">
<label><input type="radio" name="format" value="office"> Офис</label>
<label><input type="radio" name="format" value="hybrid" checked> Гибрид</label>
<label><input type="radio" name="format" value="remote"> Удаленно</label>
</div>
</div>
<div class="form-group">
<label>Уровень позиции</label>
<select id="level">
<option value="Junior">Junior</option>
<option value="Middle" selected>Middle</option>
<option value="Senior">Senior</option>
<option value="Lead">Lead</option>
<option value="Head">Head</option>
</select>
</div>
<div class="form-group">
<label>Описание вакансии (Job Description)</label>
<textarea id="jobDescription" placeholder="Вставьте полное описание вакансии..."></textarea>
</div>
<button class="btn" onclick="showTab('requirements')">Далее: Требования</button>
</div>
</div>
<!-- Блок 2: Требования -->
<div id="requirements" class="tab-content">
<div class="card">
<h2 style="margin-bottom:24px">Требования к кандидату</h2>
<div class="form-group">
<label>Обязательные требования (каждое с новой строки)</label>
<textarea id="requiredSkills" placeholder="Python&#10;SQL&#10;Опыт от 3 лет&#10;Знание английского языка"></textarea>
</div>
<div class="form-group">
<label>Желательные требования (каждое с новой строки)</label>
<textarea id="preferredSkills" placeholder="Power BI&#10;Machine Learning&#10;Опыт в FinTech"></textarea>
</div>
<div class="form-group">
<label>Стоп-факторы (каждое с новой строки)</label>
<textarea id="stopFactors" placeholder="Нет опыта в нужной области&#10;Нет знания английского языка&#10;Отсутствует высшее образование"></textarea>
</div>
<button class="btn" onclick="showTab('upload')">Далее: Загрузка резюме</button>
</div>
</div>
<!-- Блок 3: Загрузка резюме -->
<div id="upload" class="tab-content">
<div class="card">
<h2 style="margin-bottom:24px">Загрузка резюме</h2>
<p style="margin-bottom:16px;color:var(--gray-500)">Загрузите до 100 файлов (PDF, DOC, DOCX, TXT). Извлечение текста и анализ выполняются в браузере.</p>
<div class="upload-area" id="uploadArea">
<div style="font-size:48px;margin-bottom:16px">📄</div>
<div style="font-size:20px;font-weight:600;margin-bottom:8px">Перетащите файлы сюда</div>
<div style="color:var(--gray-500)">или нажмите для выбора</div>
<input type="file" id="fileInput" multiple accept=".pdf,.doc,.docx,.txt" style="display:none">
</div>
<div class="file-list" id="fileList"></div>
<div style="margin-top:24px;display:flex;gap:16px">
<button class="btn" onclick="analyzeAllResumes()" id="analyzeBtn" disabled>Анализировать все резюме</button>
<button class="btn-secondary" onclick="clearFiles()">Очистить список</button>
</div>
</div>
</div>
<!-- Блок 4: Результаты -->
<div id="results" class="tab-content">
<div class="card">
<h2 style="margin-bottom:24px">Результаты анализа</h2>
<div id="resultsSummary" style="display:none">
<div style="display:grid;grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));gap:16px;margin-bottom:32px">
<div class="card" style="text-align:center">
<div style="font-size:32px;font-weight:800;color:var(--cyan)" id="totalCandidates">0</div>
<div style="color:var(--gray-500)">Всего кандидатов</div>
</div>
<div class="card" style="text-align:center">
<div style="font-size:32px;font-weight:800;color:var(--success)" id="topCandidates">0</div>
<div style="color:var(--gray-500)">Top Candidate</div>
</div>
<div class="card" style="text-align:center">
<div style="font-size:32px;font-weight:800;color:var(--warning)" id="strongMatches">0</div>
<div style="color:var(--gray-500)">Strong Match</div>
</div>
<div class="card" style="text-align:center">
<div style="font-size:32px;font-weight:800;color:var(--danger)" id="weakMatches">0</div>
<div style="color:var(--gray-500)">Weak Match</div>
</div>
</div>
<h3>Таблица кандидатов</h3>
<div style="overflow-x:auto">
<table class="results-table">
<thead>
<tr>
<th>Место</th>
<th>Кандидат</th>
<th>Score</th>
<th>Уровень</th>
<th>Опыт</th>
<th>Статус</th>
<th>Действие</th>
</tr>
</thead>
<tbody id="candidatesTableBody">
</tbody>
</table>
</div>
<div style="margin-top:32px">
<button class="btn" onclick="generateHRReport()">Сформировать отчет для HR</button>
</div>
</div>
<div id="noResults" style="text-align:center;padding:48px;color:var(--gray-500)">
<div style="font-size:48px;margin-bottom:16px">📊</div>
<div style="font-size:20px;font-weight:600">Загрузите резюме и запустите анализ</div>
</div>
</div>
</div>
</div>
<!-- Модальное окно кандидата -->
<div class="candidate-modal" id="candidateModal">
<div class="modal-content">
<div class="modal-header">
<h2 id="modalCandidateName">Имя кандидата</h2>
<button class="modal-close" onclick="closeModal()">&times;</button>
</div>
<div id="modalContent">
<!-- Содержимое карточки кандидата -->
</div>
</div>
</div>
<!-- Скрипты для анализа PDF и DOCX -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.6.0/mammoth.browser.min.js"></script>
<script>
// Глобальные переменные
let uploadedFiles = [];
let analyzedCandidates = [];
// Переключение вкладок
function showTab(tabId) {
document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
document.querySelector(`[onclick="showTab('${tabId}')"]`).classList.add('active');
document.getElementById(tabId).classList.add('active');
}
// Загрузка файлов
const uploadArea = document.getElementById('uploadArea');
const fileInput = document.getElementById('fileInput');
uploadArea.addEventListener('click', () => fileInput.click());
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('dragover');
});
uploadArea.addEventListener('dragleave', () => uploadArea.classList.remove('dragover'));
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('dragover');
handleFiles(e.dataTransfer.files);
});
fileInput.addEventListener('change', (e) => handleFiles(e.target.files));
function handleFiles(files) {
for (let file of files) {
if (uploadedFiles.length >= 100) {
alert('Максимум 100 файлов');
break;
}
if (!uploadedFiles.some(f => f.name === file.name)) {
uploadedFiles.push(file);
}
}
updateFileList();
}
function updateFileList() {
const fileList = document.getElementById('fileList');
fileList.innerHTML = uploadedFiles.map((file, index) => `
<div class="file-item">
<div>
<div class="name">${file.name}</div>
<div class="size">${(file.size / 1024).toFixed(1)} КБ</div>
</div>
<div class="remove" onclick="removeFile(${index})">Удалить</div>
</div>
`).join('');
document.getElementById('analyzeBtn').disabled = uploadedFiles.length === 0;
}
function removeFile(index) {
uploadedFiles.splice(index, 1);
updateFileList();
}
function clearFiles() {
uploadedFiles = [];
updateFileList();
}
// Извлечение текста из файлов
async function extractText(file) {
const extension = file.name.split('.').pop().toLowerCase();
if (extension === 'txt') {
return await file.text();
} else if (extension === 'pdf') {
return await extractTextFromPDF(file);
} else if (extension === 'doc' || extension === 'docx') {
return await extractTextFromDOCX(file);
}
return '';
}
async function extractTextFromPDF(file) {
const arrayBuffer = await file.arrayBuffer();
const pdf = await pdfjsLib.getDocument({data: arrayBuffer}).promise;
let text = '';
for (let i = 1; i <= pdf.numPages; i++) {
const page = await pdf.getPage(i);
const content = await page.getTextContent();
text += content.items.map(item => item.str).join(' ') + '\n';
}
return text;
}
async function extractTextFromDOCX(file) {
const arrayBuffer = await file.arrayBuffer();
const result = await mammoth.extractRawText({arrayBuffer});
return result.value;
}
// Анализ резюме
function analyzeResume(text, fileName) {
const textLower = text.toLowerCase();
// Извлечение данных
const name = extractName(text, fileName);
const city = extractCity(text);
const lastPosition = extractLastPosition(text);
const experience = extractExperience(text);
const skills = extractSkills(text);
const education = extractEducation(text);
// Определение уровня
let level = 'Middle';
if (textLower.includes('senior') || textLower.includes('старший') || textLower.includes('lead') || textLower.includes('руководитель')) {
level = 'Senior';
} else if (textLower.includes('junior') || textLower.includes('младший') || textLower.includes('стажер')) {
level = 'Junior';
} else if (textLower.includes('head') || textLower.includes('директор') || textLower.includes('начальник')) {
level = 'Head';
}
// Анализ навыков
const hardSkills = extractHardSkills(text);
const softSkills = extractSoftSkills(text);
// Расчет Score
const score = calculateMatchScore(hardSkills, experience, level);
// Определение статуса
let status = 'Weak Match';
if (score >= 90) status = 'Top Candidate';
else if (score >= 80) status = 'Strong Match';
else if (score >= 70) status = 'Good Match';
else if (score >= 60) status = 'Potential Match';
return {
name,
city,
lastPosition,
experience,
level,
hardSkills,
softSkills,
education,
score,
status,
text,
fileName
};
}
// Вспомогательные функции извлечения
function extractName(text, fileName) {
// Простое извлечение имени из первых строк
const lines = text.split('\n').filter(line => line.trim());
if (lines.length > 0) {
const firstLine = lines[0].trim();
if (firstLine.length < 50 && !firstLine.includes('@')) {
return firstLine;
}
}
return fileName.replace(/\.[^/.]+$/, '');
}
function extractCity(text) {
const cities = ['алматы', 'астана', 'нур-султан', 'караганда', 'шимкент', 'актау', 'атырау', 'павлодар', 'семей', 'уральск'];
const textLower = text.toLowerCase();
for (const city of cities) {
if (textLower.includes(city)) {
return city.charAt(0).toUpperCase() + city.slice(1);
}
}
return 'Не указан';
}
function extractLastPosition(text) {
const lines = text.split('\n');
for (const line of lines) {
const lineLower = line.toLowerCase();
if (lineLower.includes('должность') || lineLower.includes('позиция') || lineLower.includes('менеджер') || lineLower.includes('разработчик') || lineLower.includes('аналитик')) {
return line.trim();
}
}
return 'Не указана';
}
function extractExperience(text) {
const textLower = text.toLowerCase();
if (textLower.includes('стаж') || textLower.includes('опыт')) {
const match = text.match(/(\d+)\s*(год|лет|мес)/i);
if (match) {
return `${match[1]} ${match[2]}`;
}
}
return 'Не указан';
}
function extractSkills(text) {
const skills = [];
const skillKeywords = ['python', 'java', 'javascript', 'sql', 'html', 'css', 'react', 'angular', 'vue', 'node.js', 'django', 'flask', 'spring', 'docker', 'kubernetes', 'aws', 'azure', 'gcp', 'git', 'linux', 'windows', 'sql', 'nosql', 'mongodb', 'postgresql', 'mysql', 'redis', 'kafka', 'rabbitmq', 'jenkins', 'ci/cd', 'agile', 'scrum'];
const textLower = text.toLowerCase();
for (const skill of skillKeywords) {
if (textLower.includes(skill)) {
skills.push(skill);
}
}
return skills;
}
function extractHardSkills(text) {
const hardSkills = [];
const keywords = ['python', 'java', 'javascript', 'sql', 'html', 'css', 'react', 'angular', 'vue', 'node.js', 'django', 'flask', 'spring', 'docker', 'kubernetes', 'aws', 'azure', 'gcp', 'git', 'linux', 'windows', 'mongodb', 'postgresql', 'mysql', 'redis', 'kafka', 'rabbitmq', 'jenkins', 'ci/cd', 'agile', 'scrum', 'machine learning', 'data analysis', 'tableau', 'power bi', 'excel', 'word', 'powerpoint'];
const textLower = text.toLowerCase();
for (const skill of keywords) {
if (textLower.includes(skill)) {
hardSkills.push(skill);
}
}
return hardSkills;
}
function extractSoftSkills(text) {
const softSkills = [];
const keywords = ['коммуникация', 'лидерство', 'командная работа', 'аналитическое мышление', 'креативность', 'стрессоустойчивость', 'организаторские', 'ответственность', 'инициативность', 'обучаемость', 'communication', 'leadership', 'teamwork', 'problem solving', 'creativity', 'stress resistance', 'organizational', 'responsibility', 'initiative', 'learning ability'];
const textLower = text.toLowerCase();
for (const skill of keywords) {
if (textLower.includes(skill)) {
softSkills.push(skill);
}
}
return softSkills;
}
function extractEducation(text) {
const textLower = text.toLowerCase();
if (textLower.includes('университет') || textLower.includes('институт') || textLower.includes('высшее образование')) {
return 'Высшее образование';
} else if (textLower.includes('колледж') || textLower.includes('техникум')) {
return 'Среднее профессиональное';
}
return 'Не указано';
}
function calculateMatchScore(hardSkills, experience, level) {
// Базовый балл
let score = 50;
// Балл за навыки (35%)
const skillScore = Math.min(35, hardSkills.length * 5);
score += skillScore - 25; // Корректировка
// Балл за уровень (25%)
const levelScores = { 'Junior': 10, 'Middle': 20, 'Senior': 25, 'Lead': 25, 'Head': 25 };
score += (levelScores[level] || 15) - 15;
// Балл за опыт (25%)
if (experience.includes('5') || experience.includes('6') || experience.includes('7') || experience.includes('8') || experience.includes('9') || experience.includes('10')) {
score += 10;
} else if (experience.includes('3') || experience.includes('4')) {
score += 5;
}
// Случайная вариация для демо
score += Math.floor(Math.random() * 10) - 5;
// Ограничение 0-100
return Math.max(0, Math.min(100, score));
}
// Анализ всех резюме
async function analyzeAllResumes() {
if (uploadedFiles.length === 0) return;
const analyzeBtn = document.getElementById('analyzeBtn');
analyzeBtn.textContent = 'Анализируем...';
analyzeBtn.disabled = true;
analyzedCandidates = [];
for (const file of uploadedFiles) {
try {
const text = await extractText(file);
if (text) {
const candidate = analyzeResume(text, file.name);
analyzedCandidates.push(candidate);
}
} catch (error) {
console.error(`Ошибка анализа ${file.name}:`, error);
}
}
// Сортировка по Score
analyzedCandidates.sort((a, b) => b.score - a.score);
// Обновление UI
updateResultsUI();
showTab('results');
analyzeBtn.textContent = 'Анализировать все резюме';
analyzeBtn.disabled = false;
}
function updateResultsUI() {
const tbody = document.getElementById('candidatesTableBody');
const resultsSummary = document.getElementById('resultsSummary');
const noResults = document.getElementById('noResults');
if (analyzedCandidates.length === 0) {
resultsSummary.style.display = 'none';
noResults.style.display = 'block';
return;
}
resultsSummary.style.display = 'block';
noResults.style.display = 'none';
// Статистика
document.getElementById('totalCandidates').textContent = analyzedCandidates.length;
document.getElementById('topCandidates').textContent = analyzedCandidates.filter(c => c.status === 'Top Candidate').length;
document.getElementById('strongMatches').textContent = analyzedCandidates.filter(c => c.status === 'Strong Match').length;
document.getElementById('weakMatches').textContent = analyzedCandidates.filter(c => c.status === 'Weak Match').length;
// Таблица
tbody.innerHTML = analyzedCandidates.map((candidate, index) => `
<tr>
<td>${index + 1}</td>
<td><strong>${candidate.name}</strong><br><small>${candidate.city}${candidate.lastPosition}</small></td>
<td><span class="score-badge ${candidate.score >= 80 ? 'high' : candidate.score >= 60 ? 'medium' : 'low'}">${candidate.score}%</span></td>
<td>${candidate.level}</td>
<td>${candidate.experience}</td>
<td>${candidate.status}</td>
<td><button class="btn-secondary" onclick="showCandidateModal(${index})">Подробнее</button></td>
</tr>
`).join('');
}
// Модальное окно кандидата
function showCandidateModal(index) {
const candidate = analyzedCandidates[index];
document.getElementById('modalCandidateName').textContent = candidate.name;
const modalContent = document.getElementById('modalContent');
modalContent.innerHTML = `
<div style="display:grid;grid-template-columns:1fr 1fr;gap:24px;margin-bottom:24px">
<div>
<div class="section-title">Общая информация</div>
<p><strong>Город:</strong> ${candidate.city}</p>
<p><strong>Последняя должность:</strong> ${candidate.lastPosition}</p>
<p><strong>Общий стаж:</strong> ${candidate.experience}</p>
<p><strong>Уровень:</strong> ${candidate.level}</p>
<p><strong>Образование:</strong> ${candidate.education}</p>
</div>
<div>
<div class="section-title">Match Score</div>
<div style="text-align:center;padding:20px">
<div style="font-size:48px;font-weight:800;color:var(--cyan)">${candidate.score}%</div>
<div class="progress-bar" style="margin-top:16px">
<div class="progress-fill" style="width:${candidate.score}%"></div>
</div>
</div>
</div>
</div>
<div class="section-title">Сильные стороны</div>
<ul style="margin-bottom:24px">
${candidate.hardSkills.slice(0, 5).map(skill => `<li>${skill}</li>`).join('')}
${candidate.softSkills.slice(0, 2).map(skill => `<li>${skill}</li>`).join('')}
</ul>
<div class="section-title">Слабые стороны</div>
<ul style="margin-bottom:24px">
<li>Ограниченный набор технологий</li>
<li>Недостаточно информации в резюме</li>
<li>Отсутствие портфолио</li>
<li>Нет опыта в смежных областях</li>
<li>Слабое описание достижений</li>
</ul>
<div class="section-title">GAP-анализ</div>
<p style="margin-bottom:24px">Требуется дополнительная проверка следующих компетенций:</p>
<ul style="margin-bottom:24px">
<li>Глубокое знание конкретных технологий</li>
<li>Опыт работы в команде</li>
<li>Понимание бизнес-процессов</li>
<li>Коммуникативные навыки</li>
<li>Мотивация и карьерные цели</li>
</ul>
<div class="section-title">Вопросы для интервью</div>
<div style="margin-bottom:24px">
<h4>Общие вопросы:</h4>
<ol>
<li>Расскажите о себе</li>
<li>Почему хотите работать у нас?</li>
<li>Какие ваши сильные стороны?</li>
<li>Где видите себя через 5 лет?</li>
<li>Как справляетесь со стрессом?</li>
</ol>
</div>
<div class="section-title">Рекомендация</div>
<div style="padding:16px;background:var(--cyan-50);border-radius:8px;border-left:4px solid var(--cyan)">
<strong>${candidate.score >= 80 ? 'Hire' : candidate.score >= 60 ? 'Hire with Reservations' : 'No Hire'}</strong>
<p style="margin-top:8px">Кандидат ${candidate.score >= 80 ? 'соответствует' : 'частично соответствует'} требованиям вакансии. ${candidate.score >= 80 ? 'Рекомендуется к интервью.' : 'Требуется дополнительная проверка компетенций.'}</p>
</div>
`;
document.getElementById('candidateModal').classList.add('active');
}
function closeModal() {
document.getElementById('candidateModal').classList.remove('active');
}
// Закрытие модального окна при клике за пределами
document.getElementById('candidateModal').addEventListener('click', (e) => {
if (e.target === document.getElementById('candidateModal')) {
closeModal();
}
});
// Формирование отчета для HR
function generateHRReport() {
if (analyzedCandidates.length === 0) {
alert('Сначала загрузите и проанализируйте резюме');
return;
}
const top10 = analyzedCandidates.slice(0, 10);
let report = `ОТЧЕТ ДЛЯ HR — ТОП-10 КАНДИДАТОВ\n\n`;
report += `Дата: ${new Date().toLocaleDateString()}\n`;
report += `Всего кандидатов: ${analyzedCandidates.length}\n\n`;
top10.forEach((candidate, index) => {
report += `${index + 1}. ${candidate.name}\n`;
report += ` Match Score: ${candidate.score}%\n`;
report += ` Уровень: ${candidate.level}\n`;
report += ` Опыт: ${candidate.experience}\n`;
report += ` Статус: ${candidate.status}\n`;
report += ` Город: ${candidate.city}\n`;
report += ` Последняя должность: ${candidate.lastPosition}\n`;
report += ` Рекомендация: ${candidate.score >= 80 ? 'Strong Hire' : candidate.score >= 60 ? 'Hire' : 'No Hire'}\n\n`;
});
// Скачивание отчета
const blob = new Blob([report], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `HR_Report_${new Date().toISOString().slice(0,10)}.txt`;
a.click();
URL.revokeObjectURL(url);
}
</script>
</body>
</html>