';
if (state.content && state.content.announcements.length) {
state.content.announcements.forEach(function (a) {
html += '
' + esc(a.title) + '
📅 ' + a.date + '
' + esc(a.text) + "
";
});
} else {
html += '
Хабарландыру жоқ
';
}
if (state.user.role === "admin") {
html += '
➕ Жаңа хабарландыру
';
html += '
';
html += '';
html += '';
html += '
';
html += '
';
}
el.innerHTML = html;
}
async function addAnnouncement() {
var title = document.getElementById("annTitle").value.trim();
var text = document.getElementById("annText").value.trim();
var date = document.getElementById("annDate").value || new Date().toISOString().slice(0, 10);
if (!title || !text) { alert("Тақырып пен мәтінді толтырыңыз"); return; }
if (state.offline) {
var list = state.content.announcements;
list.unshift({ id: Date.now(), title: title, text: text, date: date });
renderHome(document.getElementById("content"));
return;
}
var res = await apiPost("/api/announcements", { title: title, text: text, date: date });
if (res.ok) {
await loadServerData();
renderHome(document.getElementById("content"));
} else alert(res.error || "Қате");
}
/* ---------------- БАСШЫЛЫҚ ҚҰРЫЛЫМЫ ---------------- */
function renderOrg(el) {
var org = state.content ? state.content.org : [];
var html = pageTitle("🏛️ Басшылық құрылымы");
var levels = {};
org.forEach(function (o) {
if (!levels[o.level]) levels[o.level] = [];
levels[o.level].push(o);
});
Object.keys(levels).sort(function (a, b) { return a - b; }).forEach(function (lv) {
html += '
▼<\/div>\s*$/, "");
el.innerHTML = html;
}
/* ---------------- МЕМЛЕКЕТТІК ОРГАНДАР ---------------- */
function renderContacts(el) {
var list = state.content && state.content.contacts ? state.content.contacts : [];
var html = pageTitle("📞 Мемлекеттік органдар — Алматы қаласы");
html += '
Комплаенс мәселелері бойынша хабарласуға болатын мемлекеттік органдардың ресми байланыс деректері.
";
el.innerHTML = html;
}
/* ---------------- ВИДЕО ---------------- */
function renderVideo(el) {
var vids = state.content ? state.content.videos : [];
var html = pageTitle("🎬 Видео сабақтар");
html += '
';
vids.forEach(function (v) {
html += '
';
html += '
▶
';
html += "
" + esc(v.title) + '
' + esc(v.desc) + '
⏱ ' + v.dur + "
";
html += "
";
});
html += "
";
el.innerHTML = html;
}
function openVideo(id) {
var v = state.content.videos.find(function (x) { return x.id === id; });
if (!v) return;
var url = prompt("Видео сілтемесін қойыңыз (https://...)", v.link || "https://www.youtube.com/watch?v=dQw4w9WgXcQ");
if (url && state.offline) { v.link = url; }
if (url) window.open(url, "_blank");
}
/* ---------------- ЗАҢДАР ---------------- */
function renderLaws(el) {
var laws = state.content ? state.content.laws : [];
var cats = [];
laws.forEach(function (l) { if (cats.indexOf(l.cat) < 0) cats.push(l.cat); });
var html = pageTitle("⚖️ Заң нормалары мен комплаенс заңдар");
html += '
';
html += '';
cats.forEach(function (c) { html += '"; });
html += "
";
html += '';
el.innerHTML = html;
filterLaws("all");
}
function filterLaws(cat) {
var laws = state.content ? state.content.laws : [];
document.querySelectorAll(".tab").forEach(function (t) { t.classList.toggle("active", t.dataset.cat === cat); });
var list = cat === "all" ? laws : laws.filter(function (l) { return l.cat === cat; });
var html = "";
list.forEach(function (l) {
html += '
' + esc(l.title) + '
' + esc(l.cat) + "
" + esc(l.text) + "
";
});
document.getElementById("lawList").innerHTML = html || '
Табылмады
';
}
/* ---------------- ШАҒЫМ ЖӘШІГІ ---------------- */
function renderComplaints(el) {
var html = pageTitle("✉️ Шағым жәшігі");
html += '
Жаңа шағым / өтініш
';
html += '
';
html += '';
html += '';
html += '';
html += '';
html += '
';
html += '
Менің шағымдарым' + (state.user.role === "admin" ? " және барлық өтініштер" : "") + "
";
html += '';
el.innerHTML = html;
renderComplaintList();
}
function renderComplaintList() {
var list = state.complaints.slice().sort(function (a, b) { return b.id - a.id; });
if (state.user.role !== "admin") {
list = list.filter(function (c) { return c.authorLogin === state.user.login || c.anonymous; });
}
var html = "";
list.forEach(function (c) {
var st = { "new": "Жаңа", "work": "Қаралуда", "done": "Шешілді" }[c.status] || c.status;
html += '
";
if (state.user.role === "admin" && c.status !== "done") {
html += '
';
if (c.status !== "work") html += '';
html += '';
html += "
";
}
html += "
";
});
document.getElementById("compList").innerHTML = html || '
Әзірге шағым жоқ
';
}
async function sendComplaint() {
var category = document.getElementById("compCat").value;
var text = document.getElementById("compText").value.trim();
var anonymous = document.getElementById("compAnon").checked;
if (!text) { document.getElementById("compMsg").innerHTML = '
';
}
function startTest(id) {
state.curTest = id;
state.curTestAnswers = {};
renderTests(document.getElementById("content"));
}
function renderTestRun(el) {
var t = state.content.tests.find(function (x) { return x.id === state.curTest; });
if (!t) { state.curTest = null; renderTests(el); return; }
var html = '';
html += '
' + esc(t.title) + "
";
t.questions.forEach(function (q, qi) {
html += '
' + (qi + 1) + ". " + esc(q.q) + "
";
q.options.forEach(function (op, oi) {
html += '";
});
html += "
";
});
html += '';
el.innerHTML = html;
}
function pickAnswer(elm, qi, oi) {
state.curTestAnswers[qi] = oi;
document.querySelectorAll('.q-option[data-q="' + qi + '"]').forEach(function (o) {
o.classList.toggle("selected", o.dataset.o === String(oi));
});
}
async function submitTest() {
var t = state.content.tests.find(function (x) { return x.id === state.curTest; });
var total = t.questions.length;
var answered = Object.keys(state.curTestAnswers).length;
if (answered < total) { alert("Барлық сұрақтарға жауап беріңіз (" + answered + "/" + total + ")"); return; }
var score = 0;
t.questions.forEach(function (q, qi) { if (state.curTestAnswers[qi] === q.correct) score++; });
var pct = Math.round(score / total * 100);
var result = { testId: t.id, login: state.user.login, name: state.user.name, score: pct, date: new Date().toISOString().slice(0, 10) };
if (state.offline) {
state.testResults.push(result);
lsSet("testResults", state.testResults);
showTestResult(pct, score, total);
return;
}
var res = await apiPost("/api/test-results", { testId: t.id, score: pct });
if (res.ok) {
await loadServerData();
showTestResult(pct, score, total);
} else alert(res.error || "Қате");
}
function showTestResult(pct, score, total) {
state.curTest = null;
var el = document.getElementById("content");
var html = '
';
html += '
' + pct + "%
";
html += "
Дұрыс жауаптар: " + score + " / " + total + "
";
html += '
' + (pct >= 70 ? "✅ Тест сәтті өтілді!" : "Тест өтілмеді, қайта тапсыруға болады") + "
";
html += '';
html += "
";
el.innerHTML = html;
}
/* ---------------- ДАШБОРД (АДМИН) ---------------- */
async function renderStats(el) {
var html = pageTitle("📊 Дашборд — статистика");
var data;
if (state.offline) {
data = computeStats();
} else {
var r = await apiGet("/api/stats");
if (r.ok) data = r.data; else data = computeStats();
}
if (!data) { el.innerHTML = '
Деректер жоқ
'; return; }
html += '
';
html += '
' + data.users + "
Қызметкерлер
";
html += '
' + data.complaintsTotal + "
Шағымдар (барлығы)
";
html += '
' + data.complaintsOpen + "
Ашық шағымдар
";
html += '
' + data.testsTotal + "
Тест тапсырулар
";
html += "
";
html += '
';
html += '
Шағымдар санаты бойынша
';
html += '
Тест өту көрсеткіші
';
html += "
";
html += '
Тест нәтижелері бойынша орташа балл
';
html += '
Соңғы шағымдар
';
el.innerHTML = html;
var catData = data.byCategory;
var catMax = Math.max.apply(null, catData.map(function (c) { return c.count; }).concat([1]));
var catHtml = "";
catData.forEach(function (c) {
var w = Math.max(8, Math.round(c.count / catMax * 100));
catHtml += '
';
var passHtml = "";
["done", "work", "new"].forEach(function (s) {
var c = data.byStatus[s] || 0;
var w = data.complaintsTotal ? Math.max(8, Math.round(c / data.complaintsTotal * 100)) : 8;
var color = s === "done" ? "green" : s === "work" ? "orange" : "red";
var name = s === "done" ? "Шешілді" : s === "work" ? "Қаралуда" : "Жаңа";
passHtml += '