// === ПРИЛОЖЕНИЕ "ПОМОЩНИК ПО СКИДКАМ" ===
// Состояние приложения
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 = `
По запросу "${escapeHtml(query)}" ничего не найдено.
Попробуйте:
• Задать вопрос в AI-помощнике
• Посмотреть раздел "Ошибки" или "FAQ"
`;
} else {
container.innerHTML = results.map(r => `
${escapeHtml(r.title)}
${escapeHtml(r.preview)}
`).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 = `
🔍
Анализ завершён
Уверенность распознавания: ${result.confidence}%
📋 Я вижу ошибку:
${result.detected}
📖 Что это значит
${result.solution.meaning}
⚠️ Возможные причины
${result.solution.causes.map(c => `${c} `).join('')}
✅ Что сделать
${result.solution.solution.map(s => `${s} `).join('')}
${result.solution.contact ? `
📞 Если не помогло
${result.solution.contact}
` : ''}
Понятно
`;
}, 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 = `
📋 Алгоритм действий
${solution.steps.map(s => `${s} `).join('')}
${solution.contact ? `
📞 Контакты
${solution.contact}
` : ''}
`;
modal.style.display = 'flex';
}
// === Рендер уроков ===
function renderLessons() {
const container = document.getElementById('lessonsList');
container.innerHTML = KB.lessons.map(lesson => `
`).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 = `
${lesson.content}
Завершить урок
`;
modal.style.display = 'flex';
}
// === Рендер ошибок ===
function renderErrors() {
renderErrorsGrid(KB.errors);
}
function renderErrorsGrid(errors) {
const container = document.getElementById('errorsGrid');
if (errors.length === 0) {
container.innerHTML = 'Ошибки не найдены
';
return;
}
container.innerHTML = errors.map(err => `
Код: ${err.code}
${err.title}
${err.meaning}
`).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 = `
📖 Что это значит
${err.meaning}
⚠️ Почему возникает
${err.causes.map(c => `${c} `).join('')}
✅ Что сделать
${err.solution.map(s => `${s} `).join('')}
${err.contact ? `
📞 Если не помогло
${err.contact}
` : ''}
`;
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 => `
${cat}
${KB.instructions.filter(i => i.category === cat).map(inst => `
`).join('')}
`).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 = `
Категория: ${inst.category}
Пошаговая инструкция
${inst.steps.map((step, i) => `
${step}
`).join('')}
Закрыть
`;
modal.style.display = 'flex';
}
// === Рендер FAQ ===
function renderFAQ() {
const container = document.getElementById('faqList');
container.innerHTML = KB.faq.map((f, i) => `
${f.question}
+
${f.answer}
`).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 =>
`${t.name} — ${t.price.toLocaleString('ru-RU')} ₸ `
).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 = `
Результат расчета
Тариф:
${tariff.name}
Абонентская плата:
${tariff.price.toLocaleString('ru-RU')} ₸
Тип скидки:
${type === 'percent' ? `${amount}%` : `${amount.toLocaleString('ru-RU')} ₸`}
Срок действия:
${period} мес.
Итоговая плата в месяц:
${finalPrice.toLocaleString('ru-RU', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} ₸
Экономия за период:
${totalSavings.toLocaleString('ru-RU', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} ₸
`;
}
// === 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 += `
`;
// Имитация ответа AI (в реальности здесь будет API)
const response = generateAIResponse(message);
setTimeout(() => {
messagesContainer.innerHTML += `
`;
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 `
Это ошибка: ${err.code} — ${err.title}
Что это значит: ${err.meaning}
Причины:
${err.causes.map(c => `${c} `).join('')}
Что сделать:
${err.solution.map(s => `${s} `).join('')}
${err.contact ? `Контакты: ${err.contact}
` : ''}
`;
} else if (bestMatch.type === 'faq') {
const f = bestMatch.data;
return `${f.question}
${f.answer}
`;
}
}
// Ответ по умолчанию
return `
Я нашёл несколько материалов, которые могут помочь:
Раздел "Ошибки" — справочник кодов ошибок и решений
Раздел "Обучение" — пошаговые уроки по работе со скидками
Раздел "FAQ" — частые вопросы и ответы
Вы можете уточнить вопрос или открыть один из этих разделов.
`;
}
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 = `${text} `;
setTimeout(() => {
feedbackBlock.style.display = 'none';
feedbackBlock.innerHTML = `
Ответ помог?
👍
👎
`;
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) => `
${a}
`).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 = `
+ Добавить ошибку
${KB.errors.map(err => `
${err.code} — ${err.title}
Изменить
Удалить
`).join('')}
`;
}
if (type === 'faq') {
container.innerHTML = `
+ Добавить вопрос
${KB.faq.map(f => `
${f.question}
Изменить
Удалить
`).join('')}
`;
}
if (type === 'lessons') {
container.innerHTML = `
+ Добавить урок
${KB.lessons.map(l => `
${l.icon} ${l.title}
${l.description}
Изменить
Удалить
`).join('')}
`;
}
if (type === 'instructions') {
container.innerHTML = `
+ Добавить инструкцию
${KB.instructions.map(i => `
${i.icon} ${i.title}
${i.category}
Изменить
Удалить
`).join('')}
`;
}
if (type === 'tests') {
container.innerHTML = `
+ Добавить тест
${KB.tests.map(t => `
${t.title}
${t.questions.length} вопросов
Изменить
Удалить
`).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();