/* HR Candidate Matcher — UI Layer */ /* global window, document */ (function () { 'use strict'; var APP = window.HRMatcher = window.HRMatcher || {}; var $ = function (sel) { return document.querySelector(sel); }; var $$ = function (sel) { return Array.prototype.slice.call(document.querySelectorAll(sel)); }; var toastTimer = null; var confirmCallback = null; var currentFilter = 'all'; var criteriaEditState = []; APP.ui = { showToast: function (message, type) { var container = $('#toast-container'); if (!container) return; var toast = document.createElement('div'); toast.className = 'toast' + (type ? ' toast-' + type : ''); toast.textContent = message; container.appendChild(toast); clearTimeout(toastTimer); toastTimer = setTimeout(function () { toast.classList.add('toast-out'); setTimeout(function () { if (toast.parentNode) toast.parentNode.removeChild(toast); }, 200); }, 3000); }, showConfirm: function (message, onConfirm) { var modal = $('#modal-confirm'); var desc = $('#confirm-desc'); if (!modal || !desc) return; desc.textContent = message; modal.hidden = false; confirmCallback = onConfirm; $('#btn-confirm-ok').focus(); }, hideConfirm: function () { var modal = $('#modal-confirm'); if (modal) modal.hidden = true; confirmCallback = null; }, resolveConfirm: function () { if (confirmCallback) confirmCallback(); }, showError: function (fieldId, message) { var field = $('#field-' + fieldId); var error = $('#' + fieldId + '-error'); if (field) field.classList.add('has-error'); if (error) { error.textContent = message; error.hidden = false; } }, clearError: function (fieldId) { var field = $('#field-' + fieldId); var error = $('#' + fieldId + '-error'); if (field) field.classList.remove('has-error'); if (error) { error.textContent = ''; error.hidden = true; } }, clearAllErrors: function () { $$('.field').forEach(function (f) { f.classList.remove('has-error'); }); $$('.field-error').forEach(function (e) { e.textContent = ''; e.hidden = true; }); }, showScreen: function (screenName) { $$('.screen').forEach(function (s) { s.hidden = true; }); var screen = $('#screen-' + screenName); if (screen) { screen.hidden = false; screen.removeAttribute('hidden'); } var nav = $('#app-nav'); var clearBtn = $('#btn-clear-all'); if (screenName === 'welcome') { if (nav) nav.hidden = true; if (clearBtn) clearBtn.hidden = true; } else { if (nav) nav.hidden = false; if (clearBtn) clearBtn.hidden = false; } this.updateStepsIndicator(screenName); window.scrollTo(0, 0); }, updateStepsIndicator: function (current) { var steps = ['input', 'criteria', 'match', 'interview']; var currentIdx = steps.indexOf(current); $$('.step').forEach(function (el) { var stepName = el.getAttribute('data-step'); var stepIdx = steps.indexOf(stepName); el.classList.remove('step-active', 'step-done'); if (stepIdx === currentIdx) el.classList.add('step-active'); else if (stepIdx < currentIdx) el.classList.add('step-done'); }); }, renderCriteriaTable: function (criteria) { var tbody = $('#criteria-tbody'); var emptyMsg = $('#criteria-empty'); var tableWrap = $('.criteria-table-wrap'); if (!tbody) return; criteriaEditState = criteria.map(function (c) { return { id: c.id, label: c.label, required: c.required, weight: c.weight, synonyms: (c.synonyms || []).join(', ') }; }); if (criteria.length === 0) { tbody.innerHTML = ''; if (tableWrap) tableWrap.hidden = true; if (emptyMsg) emptyMsg.hidden = false; return; } if (tableWrap) tableWrap.hidden = false; if (emptyMsg) emptyMsg.hidden = true; var html = ''; for (var i = 0; i < criteriaEditState.length; i++) { var c = criteriaEditState[i]; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; } tbody.innerHTML = html; }, readCriteriaFromTable: function () { var rows = $$('#criteria-tbody tr'); var criteria = []; for (var i = 0; i < rows.length; i++) { var row = rows[i]; var id = row.getAttribute('data-id'); var label = row.querySelector('[data-field="label"]').value.trim(); var required = row.querySelector('[data-field="required"]').value === 'true'; var weight = parseInt(row.querySelector('[data-field="weight"]').value, 10); var synonymsStr = row.querySelector('[data-field="synonyms"]').value; var synonyms = synonymsStr.split(',').map(function (s) { return s.trim(); }).filter(Boolean); criteria.push({ id: id, label: label, required: required, weight: weight, synonyms: synonyms }); } return criteria; }, renderMatchTable: function (criteria, matchResults, filter) { var tbody = $('#match-tbody'); var emptyMsg = $('#match-empty'); var tableWrap = $('.match-table-wrap'); if (!tbody) return; var filtered = APP.logic.getFilteredResults(matchResults, filter); if (filtered.length === 0) { tbody.innerHTML = ''; if (tableWrap) tableWrap.hidden = true; if (emptyMsg) emptyMsg.hidden = false; return; } if (tableWrap) tableWrap.hidden = false; if (emptyMsg) emptyMsg.hidden = true; var html = ''; for (var i = 0; i < filtered.length; i++) { var r = filtered[i]; var c = APP.logic.getCriterionById(criteria, r.criterionId); if (!c) continue; var statusClass = 'status-' + r.status.replace('_', '-'); var statusIcon = ''; var statusText = ''; if (r.status === APP.STATUS.CONFIRMED) { statusIcon = ''; statusText = 'Подтверждено'; } else if (r.status === APP.STATUS.NOT_FOUND) { statusIcon = ''; statusText = 'Не найдено'; } else { statusIcon = ''; statusText = 'Уточнить'; } var evidenceHtml = ''; if (r.evidence && r.evidence.length > 0) { evidenceHtml = ''; } else { evidenceHtml = 'Фрагменты не найдены'; } html += ''; html += '' + escapeHtml(c.label) + ''; html += '' + evidenceHtml + ''; html += ''; html += ''; html += ''; html += ''; html += ''; } tbody.innerHTML = html; }, renderCoverage: function (coverage) { var bar = $('#coverage-bar-fill'); var value = $('#coverage-value'); if (bar) { bar.style.width = coverage + '%'; bar.setAttribute('aria-valuenow', coverage); } if (value) { value.textContent = coverage + '%'; } }, renderInterview: function (interviewData) { var container = $('#interview-questions'); var emptyMsg = $('#interview-empty'); if (!container) return; if (interviewData.length === 0) { container.innerHTML = ''; if (emptyMsg) emptyMsg.hidden = false; return; } if (emptyMsg) emptyMsg.hidden = true; var statusLabels = {}; statusLabels[APP.STATUS.NOT_FOUND] = 'Не найдено'; statusLabels[APP.STATUS.UNCLEAR] = 'Требует уточнения'; var html = ''; var globalNum = 1; for (var i = 0; i < interviewData.length; i++) { var section = interviewData[i]; html += '
'; html += '
' + escapeHtml(section.criterion.label); html += ' ' + (statusLabels[section.status] || section.status) + ''; html += '
'; html += '
'; html += '
'; if (section.criterion.required) html += 'Обязательное'; else html += 'Желательное'; html += ' · Вес ' + section.criterion.weight; html += '
'; html += '
    '; for (var q = 0; q < section.questions.length; q++) { html += '
  1. ' + globalNum + '. ' + escapeHtml(section.questions[q]) + '
  2. '; globalNum++; } html += '
'; html += '
'; html += '
'; } container.innerHTML = html; }, updateFilterChips: function (activeFilter) { $$('.chip').forEach(function (chip) { var f = chip.getAttribute('data-filter'); if (f === activeFilter) { chip.classList.add('chip-active'); } else { chip.classList.remove('chip-active'); } }); } }; function escapeHtml(str) { if (!str) return ''; return String(str) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } function escapeAttr(str) { if (!str) return ''; return String(str) .replace(/&/g, '&') .replace(/"/g, '"') .replace(//g, '>'); } })();