compliance-tv/script.js

353 lines
20 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.

var LAWS = [
{ id: 1, title: "Қазақстан Республикасының «Сыбайлас жемқорлыққа қарсы іс-қимыл туралы» Заңы", cat: "Заң", desc: "Сыбайлас жемқорлыққа қарсы іс-қимылдың негізгі қағидаттары, тыйымдар мен жауапкершілік." },
{ id: 2, title: "ҚР «Мемлекеттік қызмет туралы» Заңы", cat: "Заң", desc: "Мемлекеттік қызметшілердің этикасы мен шектеулері, мүдделер қақтығысын реттеу." },
{ id: 3, title: "«Жеке деректер және оларды қорғау туралы» Заң", cat: "Заң", desc: "Жеке деректерді жинау, өңдеу және сақтау талаптары." },
{ id: 4, title: "Қазақстан Республикасының Азаматтық кодексі (құпия ақпарат бөлімі)", cat: "Құқық", desc: "Коммерциялық құпия және құпия ақпарат режимі." },
{ id: 5, title: "«Ақпаратқа қол жеткізу туралы» Заң", cat: "Заң", desc: "Ақпаратқа қол жеткізу тәртібі және қызметтік ақпараттың құпиялылығы." },
{ id: 6, title: "Телеканалдың Ішкі еңбек тәртібі", cat: "Ішкі регламент", desc: "Еңбек тәртібі, қауіпсіздік және жұмыс орнындағы әдеп нормалары." },
{ id: 7, title: "Қаржылық бақылау жөніндегі нұсқаулық", cat: "Ішкі регламент", desc: "Қаржылық операцияларды бақылау, сыйлықтар мен төлемдерді декларациялау тәртібі." },
{ id: 8, title: "Ақпараттық қауіпсіздік саясаты", cat: "Ішкі регламент", desc: "Ақпараттық ресурстарды пайдалану, пароль және құпия сөздермен жұмыс ережелері." },
{ id: 9, title: "«Ақшаны жылыстатуға қарсы іс-қимыл туралы» Заң", cat: "Заң", desc: "Күдікті операциялар туралы хабарлау және қаржылық мониторинг талаптары." },
{ id: 10, title: "Комплаенс-бақылау жөніндегі ереже", cat: "Ішкі регламент", desc: "Комплаенс-тексерулер, өзін-өзі тексеру және есептілік тәртібі." }
];
var STRUCTURE = [
{ id: 1, name: "Айгүл Сейтова", role: "Бас директор", dept: "Әкімшілік", head: true, emoji: "👩‍💼" },
{ id: 2, name: "Ерлан Қасымов", role: "Директордың орынбасары", dept: "Әкімшілік", head: false, emoji: "👨‍💼" },
{ id: 3, name: "Асет Бисенов", role: "Комплаенс офицер", dept: "Комплаенс қызметі", head: false, emoji: "🛡️" },
{ id: 4, name: "Гүлназ Омарова", role: "Заңгер", dept: "Құқықтық департамент", head: false, emoji: "⚖️" },
{ id: 5, name: "Дәурен Нұрлан", role: "Бас бухгалтер", dept: "Қаржы департаменті", head: false, emoji: "💼" },
{ id: 6, name: "Майра Жұмабек", role: "HR директоры", dept: "Кадр департаменті", head: false, emoji: "🧑‍🏫" },
{ id: 7, name: "Бауыржан Серік", role: "IT директоры", dept: "IT департаменті", head: false, emoji: "💻" },
{ id: 8, name: "Ляззат Төлеген", role: "Бас редактор", dept: "Редакция", head: false, emoji: "📰" }
];
var VIDEOS = [
{ id: 1, title: "Комплаенс деген не?", category: "Кіріспе", duration: "4:32", desc: "Комплаенс ұғымы және телеканалдағы рөлі" },
{ id: 2, title: "Мүдделер қақтығысы: қалай анықтауға?", category: "Этика", duration: "3:45", desc: "Мүдделер қақтығысының белгілері мен шешімдері" },
{ id: 3, title: "Сыбайлас жемқорлыққа қарсы саясат", category: "Сыбайлас жемқорлық", duration: "5:18", desc: "Негізгі тыйымдар мен жауапкершілік" },
{ id: 4, title: "Сыйлықтар мен төлемдер: шегі қандай?", category: "Сыбайлас жемқорлық", duration: "3:20", desc: "Сыйлық қабылдау ережелері және декларациялау" },
{ id: 5, title: "Құпия ақпаратпен жұмыс", category: "Ақпарат", duration: "4:55", desc: "Құпия ақпаратты қорғау ережелері" },
{ id: 6, title: "Жеке деректерді қорғау", category: "Ақпарат", duration: "3:55", desc: "Жеке деректермен жұмыс істеу талаптары" },
{ id: 7, title: "Қысым мен кемсітушілік", category: "Этика", duration: "4:28", desc: "Жұмыс орнындағы әдеп және теңдік" },
{ id: 8, title: "Әлеуметтік желілердегі мінез-құлық", category: "Этика", duration: "3:38", desc: "Қызметкердің әлеуметтік желідегі әдебі" }
];
var QUESTIONS = [
{ q: "Сыйлықты декларациясыз қабылдауға болатын ең жоғары шекті мөлшері:", options: ["5 АЕК", "10 АЕК", "20 АЕК", "Кез келген"], correct: 0 },
{ q: "Мүдделер қақтығысы туындағанда не істеу керек?", options: ["Өзім шешемін", "Басшыға және комплаенс қызметіне хабарлау", "Елемеу", "Жұмыстан кету"], correct: 1 },
{ q: "Комплаенс-тестілеуден қаншалықты жиі өту керек?", options: ["3 жылда бір", "Қалауымша", "Жыл сайын", "Тек жұмысқа қабылданғанда"], correct: 2 },
{ q: "Серіктестерден ақшалай сыйлық қабылдауға бола ма?", options: ["10 000 теңгеге дейін", "Басшының рұқсатымен", "Кез келген мөлшерде тыйым салынады", "Болады, егер декларацияласа"], correct: 2 },
{ q: "5 АЕК-тен қымбат сыйлықпен не істеу керек?", options: ["Өзіме қалдыру", "Әріптестерге беру", "Декларациялап, ұйымға тапсыру", "Қайтару"], correct: 2 },
{ q: "Құпия ақпаратқа не жатады?", options: ["Тек қаржылық есептер", "Тек жеке деректер", "Қаржылық деректер, ЖД, коммерциялық құпия", "Тек келісімшарттар"], correct: 2 },
{ q: "Сырттай қоса атқару:", options: ["Толық тыйым салынады", "Шектеусіз рұқсат", "Басшының келісімімен рұқсат", "Тек демалыс күндері"], correct: 2 },
{ q: "Сыбайлас жемқорлық туралы хабарлау арналары:", options: ["Тек жеке басшыға", "Сенім телефоны, email, басшы", "Тек анонимді", "Хабарлауға болмайды"], correct: 1 },
{ q: "Сыбайлас жемқорлыққа қарсы оқыту міндетті:", options: ["Тек басшыларға", "Тек жаңа қызметкерлерге", "Барлық қызметкерлерге", "Тек бухгалтерияға"], correct: 2 },
{ q: "Комплаенс ережесін бұзу неге әкеледі?", options: ["Ауызша ескерту", "Тәртіптік жауапкершілік, жұмыстан босатуға дейін", "Ешқандай салдарға", "Тек айыппұлға"], correct: 1 }
];
var currentQuestion = 0;
var score = 0;
var testTaken = false;
var userName = "";
var userDept = "";
var mockResults = [
{ name: "Айгүл Сейтова", dept: "Әкімшілік", score: 10, total: 10, date: "2026-08-10" },
{ name: "Дәурен Нұрлан", dept: "Қаржы", score: 9, total: 10, date: "2026-08-10" },
{ name: "Майра Жұмабек", dept: "Кадр", score: 8, total: 10, date: "2026-08-09" },
{ name: "Бауыржан Серік", dept: "IT", score: 7, total: 10, date: "2026-08-09" },
{ name: "Ляззат Төлеген", dept: "Редакция", score: 6, total: 10, date: "2026-08-08" }
];
var results = [];
var resultsBackendReady = false;
function getResults() {
return resultsBackendReady ? results : mockResults;
}
function renderLaws() {
var query = (document.getElementById("law-search").value || "").toLowerCase();
var activeCat = document.querySelector(".chip.active");
var cat = activeCat ? activeCat.dataset.cat : "";
var list = document.getElementById("law-list");
var filtered = LAWS.filter(function (l) {
var matchQ = l.title.toLowerCase().indexOf(query) !== -1 || l.desc.toLowerCase().indexOf(query) !== -1;
var matchC = !cat || l.cat === cat;
return matchQ && matchC;
});
if (!filtered.length) {
list.innerHTML = '<div class="empty">Ештеңе табылмады</div>';
return;
}
list.innerHTML = filtered.map(function (l) {
return '<div class="law-item">' +
'<div class="law-title">' + l.title + '</div>' +
'<div class="law-meta"><span class="law-cat">' + l.cat + '</span>' + l.id + '</div>' +
(l.desc ? '<div class="law-desc">' + l.desc + '</div>' : '') +
'</div>';
}).join("");
}
function renderLawChips() {
var cats = {};
LAWS.forEach(function (l) { cats[l.cat] = true; });
var chips = document.getElementById("law-chips");
chips.innerHTML = '<button class="chip active" data-cat="">Барлығы</button>' +
Object.keys(cats).map(function (c) {
return '<button class="chip" data-cat="' + c + '">' + c + '</button>';
}).join("");
chips.querySelectorAll(".chip").forEach(function (chip) {
chip.addEventListener("click", function () {
chips.querySelectorAll(".chip").forEach(function (c) { c.classList.remove("active"); });
chip.classList.add("active");
renderLaws();
});
});
}
function renderStructure() {
var grid = document.getElementById("structure-grid");
grid.innerHTML = STRUCTURE.map(function (s) {
return '<div class="structure-card">' +
'<div class="structure-avatar">' + s.emoji + '</div>' +
'<div class="structure-name">' + s.name + '</div>' +
'<div class="structure-role">' + s.role + '</div>' +
'<div class="structure-dept">' + s.dept + '</div>' +
(s.head ? '<div class="structure-head">👑 Басшылық</div>' : '') +
'</div>';
}).join("");
}
function renderVideos() {
var grid = document.getElementById("video-grid");
grid.innerHTML = VIDEOS.map(function (v) {
return '<div class="video-card" onclick="openVideo(' + v.id + ')">' +
'<div class="video-thumbnail">' + v.desc + '</div>' +
'<div class="video-info">' +
'<div class="video-title">' + v.title + '</div>' +
'<div class="video-meta"><span>' + v.category + '</span><span class="video-duration">' + v.duration + '</span></div>' +
'</div></div>';
}).join("");
}
function openVideo(id) {
var v = VIDEOS.find(function (x) { return x.id === id; });
if (v) alert("🎬 «" + v.title + "»\n\nРеалды нұсқада мұнда видео ойнайды (файлды офицер жүктеген кезде).");
}
function initTest() {
currentQuestion = 0;
score = 0;
testTaken = false;
userName = document.getElementById("test-name").value.trim();
userDept = document.getElementById("test-dept").value.trim();
if (!userName || !userDept) {
alert("Аты-жөніңізді және бөлімді толтырыңыз");
return;
}
document.getElementById("test-start").style.display = "none";
document.getElementById("test-quiz").style.display = "block";
document.getElementById("test-result").style.display = "none";
showQuestion();
}
function showQuestion() {
var q = QUESTIONS[currentQuestion];
document.getElementById("q-current").textContent = currentQuestion + 1;
document.getElementById("q-total").textContent = QUESTIONS.length;
document.getElementById("question-text").textContent = q.q;
document.getElementById("test-feedback").textContent = "";
document.getElementById("test-options").innerHTML = q.options.map(function (opt, i) {
return '<button class="test-option" onclick="answer(' + i + ')">' + opt + '</button>';
}).join("");
}
function answer(selectedIndex) {
if (testTaken) return;
testTaken = true;
var q = QUESTIONS[currentQuestion];
var options = document.querySelectorAll(".test-option");
var feedback = document.getElementById("test-feedback");
if (selectedIndex === q.correct) {
options[selectedIndex].classList.add("correct");
feedback.textContent = "✅ Дұрыс!";
feedback.style.color = "#48bb78";
score++;
} else {
options[selectedIndex].classList.add("wrong");
options[q.correct].classList.add("correct");
feedback.textContent = "❌ Қате";
feedback.style.color = "#f56565";
}
setTimeout(function () {
currentQuestion++;
if (currentQuestion < QUESTIONS.length) {
testTaken = false;
showQuestion();
} else {
finishTest();
}
}, 2000);
}
function finishTest() {
var percent = Math.round((score / QUESTIONS.length) * 100);
document.getElementById("test-quiz").style.display = "none";
document.getElementById("test-result").style.display = "block";
document.getElementById("result-score").textContent = score + " / " + QUESTIONS.length;
document.getElementById("result-percent").textContent = percent + "%";
var msg = percent >= 90
? "🎉 Тамаша! Сіз комплаенс саясатын жақсы білесіз."
: percent >= 70
? "👍 Жақсы нәтиже. Жеке тақырыптарды қайталау ұсынылады."
: "⚠️ Оқуды қайта өтіп, материалдарды зерделеуді ұсынамыз.";
document.getElementById("result-message").textContent = msg;
saveResult(userName, userDept, score, QUESTIONS.length);
}
function saveResult(name, dept, sc, total) {
var rec = { name: name, dept: dept, score: sc, total: total, date: new Date().toISOString().slice(0, 10) };
if (resultsBackendReady) {
fetch("/api/results", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(rec)
}).catch(function () {});
} else {
results.push(rec);
renderDashboard();
}
}
function renderStats() {
var rows = getResults();
var total = rows.length;
var passed = rows.filter(function (r) { return r.score / r.total >= 0.7; }).length;
var avg = total ? Math.round(rows.reduce(function (s, r) { return s + r.score / r.total * 100; }, 0) / total) : 0;
var stats = document.getElementById("stats-grid");
stats.innerHTML = [
{ icon: "👥", val: total, label: "Тест тапсырған" },
{ icon: "✅", val: passed, label: "70%+ нәтижемен" },
{ icon: "🎯", val: avg + "%", label: "Орташа нәтиже" },
{ icon: "📈", val: topPerformer(rows), label: "Үздік қызметкер" }
].map(function (s) {
return '<div class="stat-card"><span class="stat-icon">' + s.icon + '</span>' +
'<span class="stat-value">' + s.val + '</span><span class="stat-label">' + s.label + '</span></div>';
}).join("");
}
function topPerformer(rows) {
if (!rows.length) return "—";
var best = rows.reduce(function (a, b) { return (a.score / a.total) >= (b.score / b.total) ? a : b; });
return best.name;
}
function renderResultsTable() {
var query = (document.getElementById("dash-search").value || "").toLowerCase();
var rows = getResults().filter(function (r) {
return r.name.toLowerCase().indexOf(query) !== -1 || r.dept.toLowerCase().indexOf(query) !== -1;
}).slice().reverse();
var tbody = document.querySelector("#results-table tbody");
if (!rows.length) {
tbody.innerHTML = '<tr><td colspan="4" class="empty">Нәтиже жоқ</td></tr>';
return;
}
tbody.innerHTML = rows.map(function (r) {
var pct = Math.round(r.score / r.total * 100);
var cls = pct >= 90 ? "score-good" : pct >= 70 ? "score-mid" : "score-low";
return '<tr><td>' + r.name + '</td><td>' + r.dept + '</td>' +
'<td><span class="score-badge ' + cls + '">' + pct + '% (' + r.score + '/' + r.total + ')</span></td>' +
'<td>' + r.date + '</td></tr>';
}).join("");
}
function renderDashboard() {
renderStats();
renderResultsTable();
}
function exportCsv() {
var rows = getResults();
var head = ["Қызметкер", "Бөлім", "Нәтиже", "Дата"];
var body = rows.map(function (r) {
return [r.name, r.dept, r.score + "/" + r.total, r.date]
.map(function (v) { return '"' + String(v).replace(/"/g, '""') + '"'; }).join(";");
});
var csv = "\uFEFF" + [head.join(";"), ...body].join("\n");
var a = document.createElement("a");
a.href = URL.createObjectURL(new Blob([csv], { type: "text/csv" }));
a.download = "test_results.csv";
a.click();
}
function tryBackend() {
fetch("/api/results").then(function (r) { return r.json(); }).then(function (data) {
if (data && Array.isArray(data)) {
results = data;
resultsBackendReady = true;
renderDashboard();
}
}).catch(function () {});
}
document.getElementById("law-search").addEventListener("input", renderLaws);
document.getElementById("dash-search").addEventListener("input", renderResultsTable);
document.getElementById("export-btn").addEventListener("click", exportCsv);
document.getElementById("start-test-btn").addEventListener("click", initTest);
document.getElementById("restart-test-btn").addEventListener("click", function () {
document.getElementById("test-start").style.display = "block";
document.getElementById("test-result").style.display = "none";
});
document.getElementById("complaint-form").addEventListener("submit", function (e) {
e.preventDefault();
var form = e.target;
var data = {
category: form.category.value,
text: form.text.value
};
if (resultsBackendReady) {
fetch("/api/complaints", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(data)
}).then(function () {
form.reset();
showFormSuccess();
}).catch(function () { form.reset(); showFormSuccess(); });
} else {
form.reset();
showFormSuccess();
}
});
function showFormSuccess() {
var note = document.querySelector(".form-note");
var old = note.textContent;
note.textContent = "✅ Өтініш жіберілді!";
setTimeout(function () { note.textContent = old; }, 4000);
}
document.getElementById("video-form").addEventListener("submit", function (e) {
e.preventDefault();
alert("📤 Бұл мүмкіндік сервер қосылғанда жұмыс істейді (Рецепт: видео жүктеу).");
});
document.querySelectorAll(".nav-link").forEach(function (link) {
link.addEventListener("click", function (e) {
e.preventDefault();
document.querySelectorAll(".nav-link").forEach(function (l) { l.classList.remove("active"); });
link.classList.add("active");
document.querySelector(link.getAttribute("href")).scrollIntoView({ behavior: "smooth" });
});
});
renderLawChips();
renderLaws();
renderStructure();
renderVideos();
renderDashboard();
tryBackend();