264 lines
17 KiB
HTML
264 lines
17 KiB
HTML
<!DOCTYPE html>
|
||
<!-- no-design-system -->
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<title>HR Candidate Matcher</title>
|
||
<link rel="stylesheet" href="style.css">
|
||
</head>
|
||
<body>
|
||
<div id="app">
|
||
<header class="app-header" role="banner">
|
||
<div class="container app-header-inner">
|
||
<div class="app-logo">
|
||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/>
|
||
<circle cx="9" cy="7" r="4"/>
|
||
<path d="M22 21v-2a4 4 0 0 0-3-3.87"/>
|
||
<path d="M16 3.13a4 4 0 0 1 0 7.75"/>
|
||
</svg>
|
||
<span class="app-logo-text">HR Candidate Matcher</span>
|
||
</div>
|
||
<nav class="app-nav" id="app-nav" role="navigation" aria-label="Основная навигация" hidden>
|
||
<ol class="steps" id="steps-indicator">
|
||
<li class="step" data-step="input"><span class="step-num">1</span> Ввод</li>
|
||
<li class="step" data-step="criteria"><span class="step-num">2</span> Критерии</li>
|
||
<li class="step" data-step="match"><span class="step-num">3</span> Сопоставление</li>
|
||
<li class="step" data-step="interview"><span class="step-num">4</span> Интервью</li>
|
||
</ol>
|
||
</nav>
|
||
<button class="btn btn-ghost btn-sm" id="btn-clear-all" type="button" aria-label="Очистить все данные" hidden>
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
|
||
Очистить всё
|
||
</button>
|
||
</div>
|
||
</header>
|
||
|
||
<main id="main-content" class="main-content" role="main">
|
||
<section id="screen-welcome" class="screen" aria-label="Добро пожаловать">
|
||
<div class="container">
|
||
<div class="welcome-hero">
|
||
<h1 class="welcome-title">HR Candidate Matcher</h1>
|
||
<p class="welcome-desc">Сопоставьте резюме с требованиями вакансии по прозрачным критериям. Подготовьте вопросы для интервью. Все данные остаются в вашем браузере.</p>
|
||
<div class="welcome-actions">
|
||
<button class="btn btn-primary btn-lg" id="btn-start-empty" type="button">Начать с нуля</button>
|
||
<button class="btn btn-outline btn-lg" id="btn-start-demo" type="button">Попробовать пример</button>
|
||
</div>
|
||
<p class="welcome-privacy">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
|
||
Персональные данные не сохраняются без явного согласия и не покидают браузер.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="screen-input" class="screen" hidden aria-label="Ввод данных">
|
||
<div class="container">
|
||
<div class="screen-header">
|
||
<button class="btn btn-ghost btn-sm btn-back" id="btn-back-from-input" type="button" aria-label="Назад">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
|
||
Назад
|
||
</button>
|
||
<h2 class="screen-title">Ввод данных</h2>
|
||
</div>
|
||
<p class="screen-subtitle">Вставьте текст вакансии и обезличенное резюме. Данные обрабатываются локально.</p>
|
||
<form id="form-input" novalidate>
|
||
<div class="input-grid">
|
||
<div class="input-col">
|
||
<div class="field" id="field-vacancy">
|
||
<label for="input-vacancy" class="field-label">Текст вакансии <span class="field-required" aria-label="обязательное поле">*</span></label>
|
||
<textarea id="input-vacancy" class="textarea" rows="16" placeholder="Вставьте описание вакансии…" aria-describedby="vacancy-error" aria-required="true"></textarea>
|
||
<div class="field-error" id="vacancy-error" role="alert" aria-live="polite" hidden></div>
|
||
</div>
|
||
</div>
|
||
<div class="input-col">
|
||
<div class="field" id="field-resume">
|
||
<label for="input-resume" class="field-label">Текст резюме <span class="field-required" aria-label="обязательное поле">*</span></label>
|
||
<textarea id="input-resume" class="textarea" rows="16" placeholder="Вставьте обезличенное резюме…" aria-describedby="resume-error" aria-required="true"></textarea>
|
||
<div class="field-error" id="resume-error" role="alert" aria-live="polite" hidden></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="input-actions">
|
||
<button class="btn btn-primary btn-lg" id="btn-extract" type="submit">Извлечь требования</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="screen-criteria" class="screen" hidden aria-label="Критерии">
|
||
<div class="container">
|
||
<div class="screen-header">
|
||
<button class="btn btn-ghost btn-sm btn-back" id="btn-back-from-criteria" type="button" aria-label="Назад к вводу">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
|
||
Назад
|
||
</button>
|
||
<h2 class="screen-title">Критерии</h2>
|
||
</div>
|
||
<p class="screen-subtitle">Проверьте и отредактируйте извлечённые требования. Укажите важность каждого.</p>
|
||
<div class="criteria-table-wrap" role="region" aria-label="Список критериев" tabindex="0">
|
||
<table class="criteria-table" id="criteria-table">
|
||
<thead>
|
||
<tr>
|
||
<th scope="col" class="col-label">Требование</th>
|
||
<th scope="col" class="col-type">Тип</th>
|
||
<th scope="col" class="col-weight">Вес</th>
|
||
<th scope="col" class="col-synonyms">Синонимы</th>
|
||
<th scope="col" class="col-actions"><span class="sr-only">Действия</span></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="criteria-tbody"></tbody>
|
||
</table>
|
||
</div>
|
||
<div class="criteria-empty" id="criteria-empty" hidden>
|
||
<p>Критерии не добавлены. Добавьте требования вручную или вернитесь к вводу данных.</p>
|
||
</div>
|
||
<div class="criteria-actions">
|
||
<button class="btn btn-outline" id="btn-add-criterion" type="button">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
||
Добавить требование
|
||
</button>
|
||
<button class="btn btn-primary btn-lg" id="btn-to-match" type="button">Перейти к сопоставлению</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="screen-match" class="screen" hidden aria-label="Сопоставление">
|
||
<div class="container">
|
||
<div class="screen-header">
|
||
<button class="btn btn-ghost btn-sm btn-back" id="btn-back-from-match" type="button" aria-label="Назад к критериям">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
|
||
Назад
|
||
</button>
|
||
<h2 class="screen-title">Сопоставление</h2>
|
||
</div>
|
||
<p class="screen-subtitle">Результаты сопоставления требований с фрагментами резюме.</p>
|
||
<div class="match-filter-bar" id="match-filter-bar">
|
||
<button class="chip chip-active" data-filter="all" type="button">Все</button>
|
||
<button class="chip" data-filter="confirmed" type="button">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>
|
||
Подтверждено
|
||
</button>
|
||
<button class="chip" data-filter="not_found" type="button">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>
|
||
Не найдено
|
||
</button>
|
||
<button class="chip" data-filter="unclear" type="button">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
|
||
Уточнить
|
||
</button>
|
||
</div>
|
||
<div class="match-table-wrap" role="region" aria-label="Таблица сопоставления" tabindex="0">
|
||
<table class="match-table" id="match-table">
|
||
<thead>
|
||
<tr>
|
||
<th scope="col" class="col-criterion">Требование</th>
|
||
<th scope="col" class="col-evidence">Найденные фрагменты</th>
|
||
<th scope="col" class="col-status">Статус</th>
|
||
<th scope="col" class="col-note">Примечание</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="match-tbody"></tbody>
|
||
</table>
|
||
</div>
|
||
<div class="match-empty" id="match-empty" hidden>
|
||
<p>Нет результатов по выбранному фильтру.</p>
|
||
<button class="btn btn-outline btn-sm" id="btn-reset-filter" type="button">Сбросить фильтр</button>
|
||
</div>
|
||
<div class="match-coverage" id="match-coverage">
|
||
<div class="coverage-label">Покрытие указанных требований</div>
|
||
<div class="coverage-bar-track">
|
||
<div class="coverage-bar-fill" id="coverage-bar-fill" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" aria-label="Покрытие требований"></div>
|
||
</div>
|
||
<div class="coverage-value" id="coverage-value">0%</div>
|
||
</div>
|
||
<div class="match-actions">
|
||
<button class="btn btn-outline" id="btn-view-source" type="button">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
|
||
Исходные тексты
|
||
</button>
|
||
<button class="btn btn-primary btn-lg" id="btn-to-interview" type="button">Вопросы для интервью</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="modal-overlay" id="modal-source" hidden role="dialog" aria-modal="true" aria-label="Исходные тексты">
|
||
<div class="modal-panel">
|
||
<div class="modal-header">
|
||
<h3>Исходные тексты</h3>
|
||
<button class="btn btn-ghost btn-sm" id="btn-close-source" type="button" aria-label="Закрыть">
|
||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
|
||
</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="source-grid">
|
||
<div class="source-col">
|
||
<h4>Вакансия</h4>
|
||
<pre class="source-text" id="source-vacancy"></pre>
|
||
</div>
|
||
<div class="source-col">
|
||
<h4>Резюме</h4>
|
||
<pre class="source-text" id="source-resume"></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<section id="screen-interview" class="screen" hidden aria-label="Интервью">
|
||
<div class="container">
|
||
<div class="screen-header">
|
||
<button class="btn btn-ghost btn-sm btn-back" id="btn-back-from-interview" type="button" aria-label="Назад к сопоставлению">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="19" y1="12" x2="5" y2="12"/><polyline points="12 19 5 12 12 5"/></svg>
|
||
Назад
|
||
</button>
|
||
<h2 class="screen-title">Вопросы для интервью</h2>
|
||
</div>
|
||
<p class="screen-subtitle">Вопросы по неподтверждённым и требующим уточнения пунктам.</p>
|
||
<div class="demo-badge" role="status">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
|
||
Демо-режим: вопросы сгенерированы по шаблонам
|
||
</div>
|
||
<div id="interview-questions"></div>
|
||
<div class="interview-empty" id="interview-empty" hidden>
|
||
<p>Все требования подтверждены — вопросы для интервью не требуются.</p>
|
||
</div>
|
||
<div class="interview-actions">
|
||
<button class="btn btn-outline" id="btn-copy-questions" type="button">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
|
||
Копировать все вопросы
|
||
</button>
|
||
<button class="btn btn-outline" id="btn-export-summary" type="button">
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
||
Экспорт сводки
|
||
</button>
|
||
<button class="btn btn-outline" id="btn-start-over" type="button">Начать заново</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
|
||
<div class="toast-container" id="toast-container" aria-live="polite" aria-atomic="true"></div>
|
||
|
||
<div class="modal-overlay" id="modal-confirm" hidden role="alertdialog" aria-modal="true" aria-labelledby="confirm-title" aria-describedby="confirm-desc">
|
||
<div class="modal-panel modal-sm">
|
||
<h3 id="confirm-title">Подтверждение</h3>
|
||
<p id="confirm-desc"></p>
|
||
<div class="modal-actions">
|
||
<button class="btn btn-ghost" id="btn-confirm-cancel" type="button">Отмена</button>
|
||
<button class="btn btn-danger" id="btn-confirm-ok" type="button">Подтвердить</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="js/data.js"></script>
|
||
<script src="js/storage.js"></script>
|
||
<script src="js/aiAdapter.js"></script>
|
||
<script src="js/logic.js"></script>
|
||
<script src="js/ui.js"></script>
|
||
<script src="js/app.js"></script>
|
||
</body>
|
||
</html>
|