mne-nuzhen-prostoy-udobnyy-i/discount-helper/app.js

1098 lines
35 KiB
JavaScript
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.

// === ПРИЛОЖЕНИЕ "ПОМОЩНИК ПО СКИДКАМ" ===
// Состояние приложения
let state = {
currentPage: 'home',
chatHistory: [],
testCurrent: null,
testScore: 0,
testQuestionIndex: 0,
testAnswers: []
};
// === Инициализация ===
function init() {
setupNavigation();
setupSearch();
setupUpload();
setupQuickSolutions();
renderLessons();
renderErrors();
renderInstructions();
renderFAQ();
renderTariffs();
setupCalculator();
setupAssistant();
setupTests();
setupAdmin();
loadState();
}
// === Навигация ===
function setupNavigation() {
// Клик по меню
document.querySelectorAll('.nav-item[data-page]').forEach(item => {
item.addEventListener('click', (e) => {
e.preventDefault();
const page = item.dataset.page;
navigateTo(page);
// Мобильное меню
if (window.innerWidth <= 1024) {
document.getElementById('sidebar').classList.remove('open');
}
});
});
// Клик по фичам на главной
document.querySelectorAll('.feature-card').forEach(card => {
card.addEventListener('click', () => {
const page = card.dataset.page;
if (page) navigateTo(page);
});
});
// Мобильное меню
document.getElementById('openSidebar').addEventListener('click', () => {
document.getElementById('sidebar').classList.add('open');
});
document.getElementById('closeSidebar').addEventListener('click', () => {
document.getElementById('sidebar').classList.remove('open');
});
}
function navigateTo(page) {
// Скрыть все страницы
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
// Показать нужную
document.getElementById(`page-${page}`).classList.add('active');
document.querySelector(`.nav-item[data-page="${page}"]`)?.classList.add('active');
state.currentPage = page;
saveState();
}
// === Поиск ===
function setupSearch() {
const searchHandler = () => {
const query = document.getElementById('searchInput').value.trim().toLowerCase();
if (!query) return;
performSearch(query);
};
document.getElementById('searchBtn').addEventListener('click', searchHandler);
document.getElementById('searchInput').addEventListener('keypress', (e) => {
if (e.key === 'Enter') searchHandler();
});
// Поиск с главной
document.getElementById('homeSearchBtn').addEventListener('click', () => {
const query = document.getElementById('homeSearchInput').value.trim();
if (query) {
navigateTo('search');
document.getElementById('searchInput').value = query;
performSearch(query.toLowerCase());
}
});
document.getElementById('homeSearchInput').addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
const query = e.target.value.trim();
if (query) {
navigateTo('search');
document.getElementById('searchInput').value = query;
performSearch(query.toLowerCase());
}
}
});
// Быстрые вопросы
document.querySelectorAll('.qq-btn').forEach(btn => {
btn.addEventListener('click', () => {
const q = btn.dataset.q;
navigateTo('assistant');
setTimeout(() => {
document.getElementById('chatInput').value = q;
sendChatMessage(q);
}, 300);
});
});
// Поиск ошибок
document.getElementById('errorSearchInput').addEventListener('input', (e) => {
const query = e.target.value.toLowerCase();
filterErrors(query);
});
}
function performSearch(query) {
const results = [];
// Поиск в ошибках
KB.errors.forEach(err => {
if (
err.code.toLowerCase().includes(query) ||
err.title.toLowerCase().includes(query) ||
err.meaning.toLowerCase().includes(query)
) {
results.push({
type: 'error',
title: `Ошибка ${err.code}: ${err.title}`,
preview: err.meaning,
data: err
});
}
});
// Поиск в уроках
KB.lessons.forEach(lesson => {
if (
lesson.title.toLowerCase().includes(query) ||
lesson.description.toLowerCase().includes(query)
) {
results.push({
type: 'lesson',
title: `Урок: ${lesson.title}`,
preview: lesson.description,
data: lesson
});
}
});
// Поиск в инструкциях
KB.instructions.forEach(inst => {
if (
inst.title.toLowerCase().includes(query) ||
inst.category.toLowerCase().includes(query)
) {
results.push({
type: 'instruction',
title: `Инструкция: ${inst.title}`,
preview: inst.steps.slice(0, 3).join(' → ') + '...',
data: inst
});
}
});
// Поиск в FAQ
KB.faq.forEach(f => {
if (
f.question.toLowerCase().includes(query) ||
f.answer.toLowerCase().includes(query)
) {
results.push({
type: 'faq',
title: `FAQ: ${f.question}`,
preview: f.answer.slice(0, 150) + '...',
data: f
});
}
});
// Отображение результатов
const container = document.getElementById('searchResults');
if (results.length === 0) {
container.innerHTML = `
<div class="card">
<p>По запросу "${escapeHtml(query)}" ничего не найдено.</p>
<p style="margin-top: 12px; color: var(--text-muted);">
Попробуйте:<br>
• Задать вопрос в AI-помощнике<br>
• Посмотреть раздел "Ошибки" или "FAQ"
</p>
</div>
`;
} else {
container.innerHTML = results.map(r => `
<div class="card search-result-item" data-type="${r.type}" data-id="${r.data.id}">
<h3>${escapeHtml(r.title)}</h3>
<p style="color: var(--text-muted); margin-top: 8px;">${escapeHtml(r.preview)}</p>
</div>
`).join('');
// Клик по результату
container.querySelectorAll('.search-result-item').forEach(item => {
item.addEventListener('click', () => {
const type = item.dataset.type;
const id = item.dataset.id;
if (type === 'error') showErrorModal(KB.errors.find(e => e.id === id));
if (type === 'lesson') showLessonModal(KB.lessons.find(l => l.id === id));
if (type === 'instruction') navigateTo('instructions');
if (type === 'faq') navigateTo('faq');
});
});
}
}
// === Загрузка скриншотов ===
function setupUpload() {
const dropzone = document.getElementById('uploadDropzone');
const input = document.getElementById('uploadInput');
const btn = document.getElementById('uploadBtn');
btn.addEventListener('click', () => input.click());
input.addEventListener('change', (e) => {
if (e.target.files.length > 0) handleUpload(e.target.files[0]);
});
// Drag & drop
dropzone.addEventListener('dragover', (e) => {
e.preventDefault();
dropzone.classList.add('dragover');
});
dropzone.addEventListener('dragleave', () => {
dropzone.classList.remove('dragover');
});
dropzone.addEventListener('drop', (e) => {
e.preventDefault();
dropzone.classList.remove('dragover');
if (e.dataTransfer.files.length > 0) {
handleUpload(e.dataTransfer.files[0]);
}
});
// Клик по всей карточке
dropzone.addEventListener('click', (e) => {
if (e.target !== btn && e.target !== input) input.click();
});
}
function handleUpload(file) {
if (!file.type.startsWith('image/')) {
alert('Пожалуйста, загрузите изображение (PNG, JPG, JPEG)');
return;
}
const modal = document.getElementById('uploadModal');
const body = document.getElementById('uploadModalBody');
modal.style.display = 'flex';
// Имитация анализа (в реальности здесь будет OCR API)
setTimeout(() => {
// Демонстрационный ответ
const demoErrors = [
{
detected: 'Ошибка 403: Нет прав на создание скидки',
confidence: 85,
solution: KB.errors[0]
},
{
detected: 'Ошибка TARIFF_NOT_FOUND',
confidence: 70,
solution: KB.errors[1]
}
];
const result = demoErrors[Math.floor(Math.random() * demoErrors.length)];
body.innerHTML = `
<div style="margin-bottom: 24px;">
<div style="display: flex; align-items: center; gap: 12px; margin-bottom: 16px;">
<span style="font-size: 32px;">🔍</span>
<div>
<h3 style="margin-bottom: 4px;">Анализ завершён</h3>
<p style="color: var(--text-muted); font-size: 14px;">
Уверенность распознавания: ${result.confidence}%
</p>
</div>
</div>
<div class="card" style="background: var(--primary-light); border-left: 4px solid var(--primary);">
<h4 style="color: var(--primary); margin-bottom: 8px;">📋 Я вижу ошибку:</h4>
<p style="font-size: 16px; font-weight: 500;">${result.detected}</p>
</div>
<div class="error-detail-section">
<h3>📖 Что это значит</h3>
<div class="error-detail-content">${result.solution.meaning}</div>
</div>
<div class="error-detail-section">
<h3>⚠️ Возможные причины</h3>
<div class="error-detail-content">
<ol style="margin: 0; padding-left: 20px;">
${result.solution.causes.map(c => `<li>${c}</li>`).join('')}
</ol>
</div>
</div>
<div class="error-detail-section">
<h3>✅ Что сделать</h3>
<div class="error-detail-content">
<ol class="error-steps">
${result.solution.solution.map(s => `<li>${s}</li>`).join('')}
</ol>
</div>
</div>
${result.solution.contact ? `
<div class="card" style="background: #fef3c7; border-left: 4px solid var(--warning);">
<h4 style="margin-bottom: 8px;">📞 Если не помогло</h4>
<p>${result.solution.contact}</p>
</div>
` : ''}
<div style="margin-top: 24px; text-align: center;">
<button class="btn btn-primary" onclick="document.getElementById('uploadModal').style.display='none'">
Понятно
</button>
</div>
</div>
`;
}, 2000);
}
// === Быстрые решения ===
function setupQuickSolutions() {
document.querySelectorAll('.solution-card').forEach(card => {
card.addEventListener('click', () => {
const key = card.dataset.solution;
const solution = KB.quickSolutions[key];
if (solution) showQuickSolution(solution);
});
});
}
function showQuickSolution(solution) {
const modal = document.getElementById('errorModal');
const title = document.getElementById('errorModalTitle');
const body = document.getElementById('errorModalBody');
title.textContent = solution.title;
body.innerHTML = `
<div class="error-detail-section">
<h3>📋 Алгоритм действий</h3>
<div class="error-detail-content">
<ol class="error-steps">
${solution.steps.map(s => `<li>${s}</li>`).join('')}
</ol>
</div>
</div>
${solution.contact ? `
<div class="card" style="background: #fef3c7; border-left: 4px solid var(--warning);">
<h4 style="margin-bottom: 8px;">📞 Контакты</h4>
<p>${solution.contact}</p>
</div>
` : ''}
`;
modal.style.display = 'flex';
}
// === Рендер уроков ===
function renderLessons() {
const container = document.getElementById('lessonsList');
container.innerHTML = KB.lessons.map(lesson => `
<div class="lesson-card" data-id="${lesson.id}">
<div class="lesson-header">
<div class="lesson-icon">${lesson.icon}</div>
<div>
<div class="lesson-title">${lesson.title}</div>
<div class="lesson-desc">${lesson.description}</div>
<div style="font-size: 13px; color: var(--text-muted); margin-top: 4px;">
⏱ ${lesson.duration}
</div>
</div>
</div>
</div>
`).join('');
container.querySelectorAll('.lesson-card').forEach(card => {
card.addEventListener('click', () => {
const lesson = KB.lessons.find(l => l.id === card.dataset.id);
showLessonModal(lesson);
});
});
}
function showLessonModal(lesson) {
const modal = document.getElementById('lessonModal');
const title = document.getElementById('lessonModalTitle');
const body = document.getElementById('lessonModalBody');
title.textContent = lesson.title;
body.innerHTML = `
<div style="line-height: 1.8;">
${lesson.content}
</div>
<div style="margin-top: 32px; text-align: center;">
<button class="btn btn-primary" onclick="document.getElementById('lessonModal').style.display='none'">
Завершить урок
</button>
</div>
`;
modal.style.display = 'flex';
}
// === Рендер ошибок ===
function renderErrors() {
renderErrorsGrid(KB.errors);
}
function renderErrorsGrid(errors) {
const container = document.getElementById('errorsGrid');
if (errors.length === 0) {
container.innerHTML = '<p style="text-align: center; color: var(--text-muted); grid-column: 1/-1;">Ошибки не найдены</p>';
return;
}
container.innerHTML = errors.map(err => `
<div class="error-card" data-id="${err.id}">
<div class="error-code">Код: ${err.code}</div>
<div class="error-title">${err.title}</div>
<div class="error-preview">${err.meaning}</div>
</div>
`).join('');
container.querySelectorAll('.error-card').forEach(card => {
card.addEventListener('click', () => {
const err = KB.errors.find(e => e.id === card.dataset.id);
showErrorModal(err);
});
});
}
function filterErrors(query) {
const filtered = KB.errors.filter(err =>
err.code.toLowerCase().includes(query) ||
err.title.toLowerCase().includes(query) ||
err.meaning.toLowerCase().includes(query)
);
renderErrorsGrid(filtered);
}
function showErrorModal(err) {
const modal = document.getElementById('errorModal');
const title = document.getElementById('errorModalTitle');
const body = document.getElementById('errorModalBody');
title.textContent = `Ошибка ${err.code}`;
body.innerHTML = `
<div class="error-detail-section">
<h3>📖 Что это значит</h3>
<div class="error-detail-content">${err.meaning}</div>
</div>
<div class="error-detail-section">
<h3>⚠️ Почему возникает</h3>
<div class="error-detail-content">
<ol style="margin: 0; padding-left: 20px;">
${err.causes.map(c => `<li>${c}</li>`).join('')}
</ol>
</div>
</div>
<div class="error-detail-section">
<h3>✅ Что сделать</h3>
<div class="error-detail-content">
<ol class="error-steps">
${err.solution.map(s => `<li>${s}</li>`).join('')}
</ol>
</div>
</div>
${err.contact ? `
<div class="card" style="background: #fef3c7; border-left: 4px solid var(--warning);">
<h4 style="margin-bottom: 8px;">📞 Если не помогло</h4>
<p>${err.contact}</p>
</div>
` : ''}
`;
modal.style.display = 'flex';
}
// === Рендер инструкций ===
function renderInstructions() {
const container = document.getElementById('instructionsList');
const categories = [...new Set(KB.instructions.map(i => i.category))];
container.innerHTML = categories.map(cat => `
<div style="margin-bottom: 32px;">
<h3 style="margin-bottom: 16px; color: var(--text-muted); font-size: 14px; text-transform: uppercase;">
${cat}
</h3>
<div style="display: flex; flex-direction: column; gap: 12px;">
${KB.instructions.filter(i => i.category === cat).map(inst => `
<div class="instruction-card" data-id="${inst.id}">
<div class="instruction-header">
<div class="instruction-icon">${inst.icon}</div>
<div>
<div class="instruction-title">${inst.title}</div>
</div>
</div>
</div>
`).join('')}
</div>
</div>
`).join('');
container.querySelectorAll('.instruction-card').forEach(card => {
card.addEventListener('click', () => {
const inst = KB.instructions.find(i => i.id === card.dataset.id);
showInstructionModal(inst);
});
});
}
function showInstructionModal(inst) {
const modal = document.getElementById('lessonModal');
const title = document.getElementById('lessonModalTitle');
const body = document.getElementById('lessonModalBody');
title.textContent = inst.title;
body.innerHTML = `
<div style="margin-bottom: 24px;">
<p style="color: var(--text-muted);">Категория: ${inst.category}</p>
</div>
<div>
<h3 style="margin-bottom: 20px;">Пошаговая инструкция</h3>
<ol class="error-steps" style="counter-reset: step-counter;">
${inst.steps.map((step, i) => `
<li style="margin-bottom: 16px; padding-bottom: 16px; border-bottom: 1px solid var(--border);">
${step}
</li>
`).join('')}
</ol>
</div>
<div style="margin-top: 32px; text-align: center;">
<button class="btn btn-primary" onclick="document.getElementById('lessonModal').style.display='none'">
Закрыть
</button>
</div>
`;
modal.style.display = 'flex';
}
// === Рендер FAQ ===
function renderFAQ() {
const container = document.getElementById('faqList');
container.innerHTML = KB.faq.map((f, i) => `
<div class="faq-card" data-index="${i}">
<div class="faq-question">
<span>${f.question}</span>
<span class="faq-icon">+</span>
</div>
<div class="faq-answer">
${f.answer}
</div>
</div>
`).join('');
container.querySelectorAll('.faq-card').forEach(card => {
card.addEventListener('click', () => {
card.classList.toggle('active');
});
});
}
// === Калькулятор ===
function renderTariffs() {
const select = document.getElementById('calcTariff');
select.innerHTML = KB.tariffs.map(t =>
`<option value="${t.id}">${t.name} — ${t.price.toLocaleString('ru-RU')} ₸</option>`
).join('');
}
function setupCalculator() {
document.getElementById('calcBtn').addEventListener('click', calculateDiscount);
}
function calculateDiscount() {
const tariffId = document.getElementById('calcTariff').value;
const type = document.getElementById('calcType').value;
const amount = parseFloat(document.getElementById('calcAmount').value);
const period = parseInt(document.getElementById('calcPeriod').value);
const reason = document.getElementById('calcReason').value;
// Валидация
if (!amount || amount <= 0) {
alert('Введите корректный размер скидки');
return;
}
if (!reason) {
alert('Выберите основание для скидки');
return;
}
const tariff = KB.tariffs.find(t => t.id === tariffId);
let discountAmount, finalPrice, totalSavings;
if (type === 'percent') {
if (amount > 100) {
alert('Процентная скидка не может быть больше 100%');
return;
}
discountAmount = (tariff.price * amount) / 100;
finalPrice = tariff.price - discountAmount;
totalSavings = discountAmount * period;
} else {
if (amount >= tariff.price) {
alert('Фиксированная скидка не может превышать абонентскую плату');
return;
}
discountAmount = amount;
finalPrice = tariff.price - discountAmount;
totalSavings = discountAmount * period;
}
const container = document.getElementById('calcResult');
container.style.display = 'block';
container.innerHTML = `
<h3>Результат расчета</h3>
<div class="result-row">
<span>Тариф:</span>
<span>${tariff.name}</span>
</div>
<div class="result-row">
<span>Абонентская плата:</span>
<span>${tariff.price.toLocaleString('ru-RU')} ₸</span>
</div>
<div class="result-row">
<span>Тип скидки:</span>
<span>${type === 'percent' ? `${amount}%` : `${amount.toLocaleString('ru-RU')} ₸`}</span>
</div>
<div class="result-row">
<span>Срок действия:</span>
<span>${period} мес.</span>
</div>
<div class="result-row">
<span>Итоговая плата в месяц:</span>
<span>${finalPrice.toLocaleString('ru-RU', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} ₸</span>
</div>
<div class="result-row">
<span>Экономия за период:</span>
<span>${totalSavings.toLocaleString('ru-RU', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} ₸</span>
</div>
`;
}
// === AI-помощник ===
function setupAssistant() {
const input = document.getElementById('chatInput');
const btn = document.getElementById('chatSendBtn');
btn.addEventListener('click', () => {
const message = input.value.trim();
if (message) {
sendChatMessage(message);
input.value = '';
}
});
input.addEventListener('keypress', (e) => {
if (e.key === 'Enter' && input.value.trim()) {
sendChatMessage(input.value.trim());
input.value = '';
}
});
// Обратная связь
document.getElementById('feedbackYes').addEventListener('click', () => {
showFeedbackResponse('Спасибо за оценку! Рад, что помог.');
});
document.getElementById('feedbackNo').addEventListener('click', () => {
showFeedbackResponse('Что можно улучшить? Вы можете задать вопрос подробнее или обратиться к руководителю.');
});
}
function sendChatMessage(message) {
const messagesContainer = document.getElementById('chatMessages');
const feedbackBlock = document.getElementById('feedbackBlock');
// Сообщение пользователя
messagesContainer.innerHTML += `
<div class="message user">
<div class="message-content">
<p>${escapeHtml(message)}</p>
</div>
</div>
`;
// Имитация ответа AI (в реальности здесь будет API)
const response = generateAIResponse(message);
setTimeout(() => {
messagesContainer.innerHTML += `
<div class="message bot">
<div class="message-avatar">🤖</div>
<div class="message-content">
${response}
</div>
</div>
`;
messagesContainer.scrollTop = messagesContainer.scrollHeight;
feedbackBlock.style.display = 'flex';
}, 800);
}
function generateAIResponse(message) {
const lowerMsg = message.toLowerCase();
// Поиск по базе знаний
let bestMatch = null;
let bestScore = 0;
// Проверка ошибок
KB.errors.forEach(err => {
const score = calculateSimilarity(lowerMsg, err.code.toLowerCase() + ' ' + err.title.toLowerCase());
if (score > bestScore) {
bestScore = score;
bestMatch = { type: 'error', data: err };
}
});
// Проверка FAQ
KB.faq.forEach(f => {
const score = calculateSimilarity(lowerMsg, f.question.toLowerCase());
if (score > bestScore) {
bestScore = score;
bestMatch = { type: 'faq', data: f };
}
});
if (bestMatch && bestScore > 0.3) {
if (bestMatch.type === 'error') {
const err = bestMatch.data;
return `
<p><strong>Это ошибка: ${err.code} — ${err.title}</strong></p>
<p><strong>Что это значит:</strong> ${err.meaning}</p>
<p><strong>Причины:</strong></p>
<ul>${err.causes.map(c => `<li>${c}</li>`).join('')}</ul>
<p><strong>Что сделать:</strong></p>
<ol>${err.solution.map(s => `<li>${s}</li>`).join('')}</ol>
${err.contact ? `<p><strong>Контакты:</strong> ${err.contact}</p>` : ''}
`;
} else if (bestMatch.type === 'faq') {
const f = bestMatch.data;
return `<p><strong>${f.question}</strong></p><p>${f.answer}</p>`;
}
}
// Ответ по умолчанию
return `
<p>Я нашёл несколько материалов, которые могут помочь:</p>
<ul>
<li>Раздел <strong>"Ошибки"</strong> — справочник кодов ошибок и решений</li>
<li>Раздел <strong>"Обучение"</strong> — пошаговые уроки по работе со скидками</li>
<li>Раздел <strong>"FAQ"</strong> — частые вопросы и ответы</li>
</ul>
<p>Вы можете уточнить вопрос или открыть один из этих разделов.</p>
`;
}
function calculateSimilarity(s1, s2) {
const words1 = s1.split(/\s+/);
const words2 = s2.split(/\s+/);
const matches = words1.filter(w => words2.some(w2 => w2.includes(w) || w.includes(w2)));
return matches.length / Math.max(words1.length, words2.length);
}
function showFeedbackResponse(text) {
const feedbackBlock = document.getElementById('feedbackBlock');
feedbackBlock.innerHTML = `<span style="color: var(--text-muted);">${text}</span>`;
setTimeout(() => {
feedbackBlock.style.display = 'none';
feedbackBlock.innerHTML = `
<span>Ответ помог?</span>
<button class="btn-icon feedback-btn" id="feedbackYes">👍</button>
<button class="btn-icon feedback-btn" id="feedbackNo">👎</button>
`;
setupAssistant();
}, 3000);
}
// === Тесты ===
function setupTests() {
document.getElementById('startTestBtn').addEventListener('click', startTest);
document.getElementById('nextQuestionBtn').addEventListener('click', nextQuestion);
document.getElementById('restartTestBtn').addEventListener('click', () => {
document.getElementById('testResult').style.display = 'none';
document.getElementById('testsIntro').style.display = 'block';
});
}
function startTest() {
const test = KB.tests[0];
state.testCurrent = test;
state.testScore = 0;
state.testQuestionIndex = 0;
state.testAnswers = [];
document.getElementById('testsIntro').style.display = 'none';
document.getElementById('testContainer').style.display = 'block';
showQuestion();
}
function showQuestion() {
const test = state.testCurrent;
const q = test.questions[state.testQuestionIndex];
document.getElementById('testCounter').textContent =
`${state.testQuestionIndex + 1}/${test.questions.length}`;
document.getElementById('testProgress').style.width =
`${((state.testQuestionIndex) / test.questions.length) * 100}%`;
document.getElementById('testQuestion').textContent = q.question;
document.getElementById('testAnswers').innerHTML = q.answers.map((a, i) => `
<div class="test-answer" data-index="${i}">${a}</div>
`).join('');
document.getElementById('testFeedback').className = 'test-feedback';
document.getElementById('testFeedback').style.display = 'none';
document.getElementById('nextQuestionBtn').style.display = 'none';
// Выбор ответа
document.querySelectorAll('.test-answer').forEach(ans => {
ans.addEventListener('click', () => {
document.querySelectorAll('.test-answer').forEach(a => a.classList.remove('selected'));
ans.classList.add('selected');
// Проверка
const selectedIndex = parseInt(ans.dataset.index);
checkAnswer(selectedIndex);
});
});
}
function checkAnswer(selectedIndex) {
const test = state.testCurrent;
const q = test.questions[state.testQuestionIndex];
const isCorrect = selectedIndex === q.correct;
if (isCorrect) {
state.testScore++;
}
state.testAnswers.push({
question: q.question,
selected: selectedIndex,
correct: q.correct,
isCorrect
});
// Показать результат
const feedback = document.getElementById('testFeedback');
feedback.textContent = isCorrect ? '✅ Правильно! ' + q.explanation : '❌ Неправильно. ' + q.explanation;
feedback.className = 'test-feedback ' + (isCorrect ? '' : 'incorrect');
feedback.style.display = 'block';
// Блокировка ответов
document.querySelectorAll('.test-answer').forEach((a, i) => {
if (i === q.correct) a.classList.add('correct');
if (i === selectedIndex && !isCorrect) a.classList.add('incorrect');
});
document.getElementById('nextQuestionBtn').style.display = 'inline-block';
}
function nextQuestion() {
state.testQuestionIndex++;
const test = state.testCurrent;
if (state.testQuestionIndex >= test.questions.length) {
showTestResult();
} else {
showQuestion();
}
}
function showTestResult() {
document.getElementById('testContainer').style.display = 'none';
document.getElementById('testResult').style.display = 'block';
const score = state.testScore;
const total = state.testCurrent.questions.length;
const percentage = (score / total) * 100;
document.getElementById('resultScore').textContent = `${score}/${total}`;
let message = '';
if (percentage >= 90) {
message = '🎉 Отлично! Вы полностью готовы к работе со скидками.';
} else if (percentage >= 70) {
message = '👍 Хорошо! Есть небольшие пробелы, но вы готовы к работе.';
} else if (percentage >= 50) {
message = '📚 Неплохо, но стоит повторить материал и пройти тест ещё раз.';
} else {
message = '⚠️ Рекомендуем пройти обучение ещё раз и внимательно изучить материалы.';
}
document.getElementById('resultMessage').textContent = message;
}
// === Админ-панель ===
function setupAdmin() {
document.querySelectorAll('.admin-tab').forEach(tab => {
tab.addEventListener('click', () => {
document.querySelectorAll('.admin-tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.admin-panel').forEach(p => p.classList.remove('active'));
tab.classList.add('active');
document.getElementById(`admin-${tab.dataset.tab}`).classList.add('active');
});
});
renderAdminPanel('errors');
}
function renderAdminPanel(type) {
const container = document.getElementById(`admin-${type}`);
if (type === 'errors') {
container.innerHTML = `
<button class="btn btn-primary admin-add-btn">+ Добавить ошибку</button>
${KB.errors.map(err => `
<div class="admin-item">
<div>
<strong>${err.code}</strong> — ${err.title}
</div>
<div class="admin-item-actions">
<button class="admin-btn admin-btn-edit">Изменить</button>
<button class="admin-btn admin-btn-delete">Удалить</button>
</div>
</div>
`).join('')}
`;
}
if (type === 'faq') {
container.innerHTML = `
<button class="btn btn-primary admin-add-btn">+ Добавить вопрос</button>
${KB.faq.map(f => `
<div class="admin-item">
<div style="flex: 1;">
<strong>${f.question}</strong>
</div>
<div class="admin-item-actions">
<button class="admin-btn admin-btn-edit">Изменить</button>
<button class="admin-btn admin-btn-delete">Удалить</button>
</div>
</div>
`).join('')}
`;
}
if (type === 'lessons') {
container.innerHTML = `
<button class="btn btn-primary admin-add-btn">+ Добавить урок</button>
${KB.lessons.map(l => `
<div class="admin-item">
<div>
<strong>${l.icon} ${l.title}</strong>
<div style="font-size: 13px; color: var(--text-muted);">${l.description}</div>
</div>
<div class="admin-item-actions">
<button class="admin-btn admin-btn-edit">Изменить</button>
<button class="admin-btn admin-btn-delete">Удалить</button>
</div>
</div>
`).join('')}
`;
}
if (type === 'instructions') {
container.innerHTML = `
<button class="btn btn-primary admin-add-btn">+ Добавить инструкцию</button>
${KB.instructions.map(i => `
<div class="admin-item">
<div>
<strong>${i.icon} ${i.title}</strong>
<div style="font-size: 13px; color: var(--text-muted);">${i.category}</div>
</div>
<div class="admin-item-actions">
<button class="admin-btn admin-btn-edit">Изменить</button>
<button class="admin-btn admin-btn-delete">Удалить</button>
</div>
</div>
`).join('')}
`;
}
if (type === 'tests') {
container.innerHTML = `
<button class="btn btn-primary admin-add-btn">+ Добавить тест</button>
${KB.tests.map(t => `
<div class="admin-item">
<div>
<strong>${t.title}</strong>
<div style="font-size: 13px; color: var(--text-muted);">${t.questions.length} вопросов</div>
</div>
<div class="admin-item-actions">
<button class="admin-btn admin-btn-edit">Изменить</button>
<button class="admin-btn admin-btn-delete">Удалить</button>
</div>
</div>
`).join('')}
`;
}
}
// === Сохранение состояния ===
function saveState() {
localStorage.setItem('discountHelper_state', JSON.stringify({
currentPage: state.currentPage
}));
}
function loadState() {
const saved = localStorage.getItem('discountHelper_state');
if (saved) {
try {
const data = JSON.parse(saved);
state.currentPage = data.currentPage || 'home';
} catch (e) {}
}
navigateTo(state.currentPage);
}
// === Утилиты ===
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// === Закрытие модальных окон ===
document.getElementById('closeErrorModal').addEventListener('click', () => {
document.getElementById('errorModal').style.display = 'none';
});
document.getElementById('closeUploadModal').addEventListener('click', () => {
document.getElementById('uploadModal').style.display = 'none';
});
document.getElementById('closeLessonModal').addEventListener('click', () => {
document.getElementById('lessonModal').style.display = 'none';
});
// Клик по overlay
document.querySelectorAll('.modal-overlay').forEach(overlay => {
overlay.addEventListener('click', (e) => {
if (e.target === overlay) {
overlay.style.display = 'none';
}
});
});
// Клавиша Escape
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
document.querySelectorAll('.modal-overlay').forEach(m => m.style.display = 'none');
}
});
// === Старт ===
init();