Files
lms-it-oms/content/it_course_v2.html

1587 lines
106 KiB
HTML
Raw Permalink 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.
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Управление ИТ — Обучающий курс</title>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700;900&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
--bg: #0d1117;
--surface: #161b22;
--surface2: #21262d;
--accent: #00d4aa;
--accent2: #0095ff;
--warn: #ff6b6b;
--gold: #ffd700;
--text: #e6edf3;
--muted: #8b949e;
--border: #30363d;
--correct: #3fb950;
--wrong: #f85149;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: var(--bg);
color: var(--text);
font-family: 'Montserrat', sans-serif;
min-height: 100vh;
overflow-x: hidden;
}
body::before {
content: '';
position: fixed; inset: 0;
background-image:
linear-gradient(var(--border) 1px, transparent 1px),
linear-gradient(90deg, var(--border) 1px, transparent 1px);
background-size: 40px 40px;
opacity: 0.3;
pointer-events: none;
z-index: 0;
}
.app { position: relative; z-index: 1; max-width: 920px; margin: 0 auto; padding: 20px; }
.header {
text-align: center;
padding: 48px 0 32px;
animation: fadeDown 0.7s ease both;
}
.header .badge {
display: inline-block;
background: linear-gradient(135deg, var(--accent2), var(--accent));
color: #000;
font-size: 11px;
font-weight: 700;
letter-spacing: 2px;
text-transform: uppercase;
padding: 4px 14px;
border-radius: 20px;
margin-bottom: 16px;
}
.header h1 {
font-size: clamp(28px, 5vw, 48px);
font-weight: 900;
line-height: 1.1;
background: linear-gradient(135deg, #fff 30%, var(--accent));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 10px;
}
.header p { color: var(--muted); font-size: 15px; }
.progress-bar-wrap {
background: var(--surface2);
border: 1px solid var(--border);
border-radius: 12px;
padding: 16px 24px;
margin-bottom: 24px;
display: flex;
align-items: center;
gap: 16px;
animation: fadeUp 0.5s ease 0.2s both;
}
.progress-label { font-size: 13px; color: var(--muted); white-space: nowrap; }
.progress-track {
flex: 1;
height: 8px;
background: var(--bg);
border-radius: 4px;
overflow: hidden;
border: 1px solid var(--border);
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--accent2), var(--accent));
border-radius: 4px;
transition: width 0.5s ease;
}
.progress-count { font-family: 'JetBrains Mono', monospace; font-size: 13px; color: var(--accent); }
.screen { display: none; animation: fadeUp 0.5s ease both; }
.screen.active { display: block; }
.modules-grid { display: grid; gap: 16px; }
.module-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 16px;
padding: 24px;
cursor: pointer;
transition: all 0.25s ease;
position: relative;
overflow: hidden;
}
.module-card::before {
content: '';
position: absolute;
top: 0; left: 0; right: 0;
height: 3px;
background: linear-gradient(90deg, var(--accent2), var(--accent));
transform: scaleX(0);
transform-origin: left;
transition: transform 0.3s ease;
}
.module-card:hover { border-color: var(--accent); transform: translateY(-2px); box-shadow: 0 8px 30px rgba(0,212,170,0.1); }
.module-card:hover::before { transform: scaleX(1); }
.module-card.done { border-color: var(--correct); opacity: 0.85; }
.module-card.done::before { transform: scaleX(1); background: var(--correct); }
.module-header { display: flex; align-items: center; gap: 14px; margin-bottom: 10px; }
.module-icon {
width: 44px; height: 44px;
background: linear-gradient(135deg, rgba(0,149,255,0.2), rgba(0,212,170,0.2));
border: 1px solid var(--border);
border-radius: 10px;
display: flex; align-items: center; justify-content: center;
font-size: 20px;
flex-shrink: 0;
}
.module-title { font-size: 16px; font-weight: 700; }
.module-sub { font-size: 12px; color: var(--muted); margin-top: 2px; }
.module-desc { font-size: 13px; color: var(--muted); line-height: 1.6; }
.module-footer { display: flex; align-items: center; justify-content: space-between; margin-top: 14px; }
.module-topics { display: flex; gap: 6px; flex-wrap: wrap; }
.topic-tag {
background: rgba(0,149,255,0.1);
border: 1px solid rgba(0,149,255,0.3);
color: var(--accent2);
font-size: 10px;
font-weight: 600;
letter-spacing: 0.5px;
padding: 2px 8px;
border-radius: 4px;
font-family: 'JetBrains Mono', monospace;
}
.module-status { font-size: 12px; color: var(--muted); }
.module-card.done .module-status { color: var(--correct); }
.lesson-nav {
display: flex; align-items: center; gap: 12px;
margin-bottom: 24px;
}
.btn-back {
background: var(--surface2);
border: 1px solid var(--border);
color: var(--text);
padding: 8px 16px;
border-radius: 8px;
cursor: pointer;
font-family: 'Montserrat', sans-serif;
font-size: 13px;
font-weight: 600;
transition: all 0.2s;
}
.btn-back:hover { border-color: var(--accent2); color: var(--accent2); }
.lesson-title-bar { flex: 1; }
.lesson-title-bar h2 { font-size: 18px; font-weight: 700; }
.lesson-title-bar .lesson-sub { font-size: 12px; color: var(--muted); margin-top: 2px; }
.slide-container {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 16px;
min-height: 420px;
position: relative;
}
.slide { display: none; padding: 32px; animation: fadeUp 0.4s ease both; }
.slide.active { display: block; }
.slide-num {
font-family: 'JetBrains Mono', monospace;
font-size: 11px;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 20px;
display: flex; align-items: center; gap: 8px;
}
.slide-num::after { content: ''; flex: 1; height: 1px; background: var(--border); }
.slide h3 { font-size: 22px; font-weight: 800; margin-bottom: 20px; line-height: 1.3; }
.slide h3 span { color: var(--accent); }
.slide h4 { font-size: 15px; font-weight: 700; margin: 20px 0 10px; color: var(--accent2); }
.slide p { font-size: 14px; color: #c9d1d9; line-height: 1.8; margin-bottom: 14px; }
.slide ul { list-style: none; margin: 10px 0; }
.slide ul li {
font-size: 14px;
color: #c9d1d9;
padding: 7px 0 7px 20px;
position: relative;
border-bottom: 1px solid rgba(255,255,255,0.04);
line-height: 1.6;
}
.slide ul li::before { content: '▸'; position: absolute; left: 0; color: var(--accent); }
.slide ul li strong { color: var(--text); }
.slide ul.compact li { padding: 4px 0 4px 18px; border: none; font-size: 13px; }
.info-box {
background: rgba(0,149,255,0.07);
border: 1px solid rgba(0,149,255,0.25);
border-left: 3px solid var(--accent2);
border-radius: 8px;
padding: 14px 16px;
margin: 16px 0;
font-size: 13px;
line-height: 1.7;
color: #c9d1d9;
}
.warn-box {
background: rgba(255,107,107,0.07);
border: 1px solid rgba(255,107,107,0.25);
border-left: 3px solid var(--warn);
border-radius: 8px;
padding: 14px 16px;
margin: 16px 0;
font-size: 13px;
line-height: 1.7;
color: #c9d1d9;
}
.tip-box {
background: rgba(0,212,170,0.07);
border: 1px solid rgba(0,212,170,0.25);
border-left: 3px solid var(--accent);
border-radius: 8px;
padding: 14px 16px;
margin: 16px 0;
font-size: 13px;
line-height: 1.7;
color: #c9d1d9;
}
.sla-table { width: 100%; border-collapse: collapse; margin: 14px 0; font-size: 13px; }
.sla-table th {
background: var(--surface2);
color: var(--accent2);
font-weight: 700;
text-align: left;
padding: 9px 12px;
border: 1px solid var(--border);
font-size: 11px;
letter-spacing: 0.5px;
text-transform: uppercase;
}
.sla-table td { padding: 8px 12px; border: 1px solid var(--border); color: #c9d1d9; vertical-align: top; line-height: 1.5; }
.sla-table tr:nth-child(even) td { background: rgba(255,255,255,0.02); }
.sla-table .highlight { color: var(--accent); font-weight: 700; font-family: 'JetBrains Mono', monospace; }
.sla-table .critical { color: var(--warn); font-weight: 700; }
.sla-table .gold { color: var(--gold); font-weight: 700; }
.lp-card {
background: var(--surface2);
border: 1px solid var(--border);
border-radius: 10px;
padding: 14px 16px;
margin: 8px 0;
display: flex; gap: 12px; align-items: flex-start;
}
.lp-badge {
background: linear-gradient(135deg, var(--accent2), var(--accent));
color: #000;
font-weight: 900;
font-size: 13px;
width: 32px; height: 32px;
border-radius: 8px;
display: flex; align-items: center; justify-content: center;
flex-shrink: 0;
font-family: 'JetBrains Mono', monospace;
}
.lp-content h4 { font-size: 13px; font-weight: 700; margin-bottom: 4px; color: var(--text); }
.lp-content p { font-size: 12px; color: var(--muted); margin: 0; line-height: 1.5; }
.rfo-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 12px 0; }
.rfo-item {
background: var(--surface2);
border: 1px solid var(--border);
border-radius: 8px;
padding: 10px 12px;
font-size: 12px;
}
.rfo-item .rfo-area { color: var(--accent2); font-weight: 700; font-size: 11px; margin-bottom: 3px; text-transform: uppercase; }
.rfo-item .rfo-name { color: var(--text); font-size: 12px; font-weight: 600; }
.rfo-item .rfo-div { color: var(--muted); font-size: 11px; margin-top: 2px; }
.is-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin: 12px 0; }
.is-item {
background: var(--surface2);
border: 1px solid var(--border);
border-radius: 6px;
padding: 8px 10px;
display: flex; justify-content: space-between; align-items: center; gap: 8px;
}
.is-name { color: var(--accent); font-family: 'JetBrains Mono', monospace; font-size: 12px; font-weight: 700; }
.is-owner { color: var(--muted); font-size: 11px; text-align: right; }
.flow-steps { margin: 16px 0; }
.flow-step {
display: flex; gap: 12px; align-items: flex-start;
padding: 10px 0;
border-bottom: 1px solid rgba(255,255,255,0.04);
}
.flow-num {
width: 26px; height: 26px;
border-radius: 50%;
background: linear-gradient(135deg, var(--accent2), var(--accent));
color: #000;
font-weight: 900;
font-size: 12px;
display: flex; align-items: center; justify-content: center;
flex-shrink: 0;
font-family: 'JetBrains Mono', monospace;
}
.flow-content { flex: 1; }
.flow-content strong { font-size: 13px; display: block; margin-bottom: 3px; }
.flow-content span { font-size: 12px; color: var(--muted); line-height: 1.5; }
.slide-controls {
display: flex; justify-content: space-between; align-items: center;
padding: 16px 32px;
border-top: 1px solid var(--border);
background: var(--surface2);
border-radius: 0 0 16px 16px;
}
.slide-dots { display: flex; gap: 6px; align-items: center; }
.slide-dot {
width: 6px; height: 6px;
border-radius: 50%;
background: var(--border);
transition: all 0.2s;
cursor: pointer;
}
.slide-dot.active { background: var(--accent); width: 18px; border-radius: 3px; }
.btn-slide {
background: var(--surface);
border: 1px solid var(--border);
color: var(--text);
padding: 8px 20px;
border-radius: 8px;
cursor: pointer;
font-family: 'Montserrat', sans-serif;
font-size: 13px;
font-weight: 600;
transition: all 0.2s;
display: flex; align-items: center; gap: 6px;
}
.btn-slide:hover { border-color: var(--accent); color: var(--accent); }
.btn-slide.primary { background: linear-gradient(135deg, var(--accent2), var(--accent)); border: none; color: #000; }
.btn-slide.primary:hover { opacity: 0.9; transform: translateY(-1px); }
.btn-slide:disabled { opacity: 0.3; cursor: not-allowed; pointer-events: none; }
.quiz-header {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 16px;
padding: 24px;
margin-bottom: 20px;
display: flex; align-items: center; gap: 16px;
}
.quiz-icon { font-size: 36px; }
.quiz-header-text h2 { font-size: 20px; font-weight: 800; }
.quiz-header-text p { font-size: 13px; color: var(--muted); margin-top: 4px; }
.quiz-stats { margin-left: auto; text-align: right; }
.quiz-stats .score-num { font-family: 'JetBrains Mono', monospace; font-size: 28px; font-weight: 700; color: var(--accent); }
.quiz-stats .score-label { font-size: 11px; color: var(--muted); }
.question-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 16px;
padding: 28px;
margin-bottom: 16px;
animation: fadeUp 0.4s ease both;
}
.q-meta { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; }
.q-num {
font-family: 'JetBrains Mono', monospace;
font-size: 11px;
color: var(--muted);
background: var(--surface2);
padding: 2px 8px;
border-radius: 4px;
border: 1px solid var(--border);
}
.q-category {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.5px;
text-transform: uppercase;
color: var(--accent2);
background: rgba(0,149,255,0.1);
padding: 2px 8px;
border-radius: 4px;
}
.q-text { font-size: 15px; font-weight: 600; line-height: 1.6; margin-bottom: 20px; }
.options { display: grid; gap: 10px; }
.option-btn {
background: var(--surface2);
border: 2px solid var(--border);
border-radius: 10px;
padding: 12px 16px;
cursor: pointer;
font-family: 'Montserrat', sans-serif;
font-size: 13px;
color: var(--text);
text-align: left;
transition: all 0.2s;
display: flex; align-items: center; gap: 12px;
font-weight: 500;
line-height: 1.5;
}
.option-btn:hover:not(:disabled) { border-color: var(--accent2); background: rgba(0,149,255,0.08); }
.option-letter {
width: 28px; height: 28px;
border-radius: 6px;
background: var(--border);
display: flex; align-items: center; justify-content: center;
font-size: 11px;
font-weight: 700;
flex-shrink: 0;
font-family: 'JetBrains Mono', monospace;
}
.option-btn.correct { border-color: var(--correct); background: rgba(63,185,80,0.1); }
.option-btn.correct .option-letter { background: var(--correct); color: #000; }
.option-btn.wrong { border-color: var(--wrong); background: rgba(248,81,73,0.1); }
.option-btn.wrong .option-letter { background: var(--wrong); color: #fff; }
.option-btn:disabled { cursor: not-allowed; }
.explanation { margin-top: 16px; padding: 12px 16px; border-radius: 8px; font-size: 13px; line-height: 1.7; display: none; }
.explanation.show { display: block; animation: fadeUp 0.3s ease both; }
.explanation.correct-exp { background: rgba(63,185,80,0.08); border: 1px solid rgba(63,185,80,0.3); }
.explanation.wrong-exp { background: rgba(248,81,73,0.08); border: 1px solid rgba(248,81,73,0.3); }
.quiz-nav { display: flex; justify-content: flex-end; margin-top: 8px; }
.btn-next {
background: linear-gradient(135deg, var(--accent2), var(--accent));
border: none; color: #000;
padding: 12px 28px; border-radius: 10px;
cursor: pointer;
font-family: 'Montserrat', sans-serif;
font-size: 14px; font-weight: 700;
transition: all 0.2s;
display: none;
}
.btn-next.show { display: flex; align-items: center; gap: 8px; animation: fadeUp 0.3s ease both; }
.btn-next:hover { opacity: 0.9; transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0,212,170,0.3); }
.results-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 20px;
padding: 48px 40px;
text-align: center;
animation: fadeUp 0.6s ease both;
}
.trophy { font-size: 72px; margin-bottom: 20px; display: block; }
.results-card h2 { font-size: 28px; font-weight: 900; margin-bottom: 8px; }
.score-big {
font-family: 'JetBrains Mono', monospace;
font-size: 64px; font-weight: 700; line-height: 1;
margin: 24px 0;
background: linear-gradient(135deg, var(--accent2), var(--accent));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.results-desc { font-size: 15px; color: var(--muted); max-width: 500px; margin: 0 auto 32px; line-height: 1.7; }
.results-breakdown { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin: 32px 0; text-align: left; }
.rb-card { background: var(--surface2); border: 1px solid var(--border); border-radius: 12px; padding: 16px; }
.rb-card .rb-val { font-family: 'JetBrains Mono', monospace; font-size: 24px; font-weight: 700; color: var(--accent); }
.rb-card .rb-label { font-size: 11px; color: var(--muted); margin-top: 4px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; }
.btn-restart {
background: linear-gradient(135deg, var(--accent2), var(--accent));
border: none; color: #000;
padding: 14px 36px; border-radius: 10px;
cursor: pointer; font-family: 'Montserrat', sans-serif;
font-size: 15px; font-weight: 700;
transition: all 0.2s; margin: 8px;
}
.btn-restart:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(0,212,170,0.35); }
.btn-outline {
background: transparent; border: 2px solid var(--border); color: var(--text);
padding: 12px 36px; border-radius: 10px;
cursor: pointer; font-family: 'Montserrat', sans-serif;
font-size: 15px; font-weight: 700;
transition: all 0.2s; margin: 8px;
}
.btn-outline:hover { border-color: var(--accent2); color: var(--accent2); }
.cert-card {
background: linear-gradient(135deg, #0d2137 0%, #0d1117 50%, #0a1f0d 100%);
border: 2px solid; border-image: linear-gradient(135deg, var(--accent2), var(--accent)) 1;
border-radius: 20px; padding: 48px; text-align: center; margin: 24px 0;
position: relative; overflow: hidden;
}
.cert-card::before { content: '🏆'; position: absolute; font-size: 200px; opacity: 0.04; top: 50%; left: 50%; transform: translate(-50%, -50%); }
.cert-card h3 { font-size: 11px; letter-spacing: 3px; text-transform: uppercase; color: var(--accent); margin-bottom: 20px; }
.cert-card h2 { font-size: 26px; font-weight: 900; margin-bottom: 8px; }
.cert-card .cert-name { font-size: 20px; color: var(--accent2); font-weight: 700; margin: 16px 0; }
.cert-card .cert-date { font-size: 12px; color: var(--muted); font-family: 'JetBrains Mono', monospace; }
.cert-stars { font-size: 24px; margin: 12px 0; letter-spacing: 4px; }
@keyframes fadeUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
@keyframes fadeDown { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } }
.welcome-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 20px;
padding: 48px 40px;
max-width: 480px;
margin: 0 auto;
animation: fadeUp 0.6s ease both;
}
.welcome-card h2 { font-size: 22px; font-weight: 800; margin-bottom: 12px; }
.welcome-card p { color: var(--muted); font-size: 14px; margin-bottom: 24px; line-height: 1.6; }
.welcome-card label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 8px; color: var(--text); }
.welcome-card input[type="text"] {
width: 100%;
background: var(--surface2);
border: 2px solid var(--border);
border-radius: 10px;
padding: 14px 16px;
font-family: 'Montserrat', sans-serif;
font-size: 15px;
color: var(--text);
margin-bottom: 24px;
box-sizing: border-box;
}
.welcome-card input[type="text"]:focus {
outline: none;
border-color: var(--accent2);
}
.welcome-card input[type="text"]::placeholder { color: var(--muted); }
.welcome-card .welcome-error { color: var(--warn); font-size: 13px; margin-bottom: 12px; display: none; }
.welcome-card .welcome-error.show { display: block; }
.welcome-card .btn-start {
background: linear-gradient(135deg, var(--accent2), var(--accent));
border: none; color: #000;
padding: 14px 32px; border-radius: 10px;
cursor: pointer; font-family: 'Montserrat', sans-serif;
font-size: 15px; font-weight: 700;
transition: all 0.2s;
width: 100%;
}
.welcome-card .btn-start:hover { opacity: 0.95; transform: translateY(-2px); }
.welcome-card .btn-start:disabled { opacity: 0.6; cursor: not-allowed; transform: none; }
.user-bar {
font-size: 12px; color: var(--muted);
margin-bottom: 16px;
display: flex; align-items: center; justify-content: flex-end; gap: 8px;
}
.user-bar span { color: var(--accent); font-weight: 600; }
@media (max-width: 600px) {
.results-breakdown { grid-template-columns: 1fr 1fr; }
.rfo-grid, .is-grid { grid-template-columns: 1fr; }
.slide { padding: 18px; }
.cert-card { padding: 24px 16px; }
.welcome-card { padding: 28px 20px; }
}
</style>
</head>
<body>
<div class="app">
<div class="header">
<div class="badge">ИТ-специалист · Обучение v2</div>
<h1>Управление ИТ</h1>
<p>Интерактивный курс по руководству ИТ-сервисами компании · Редакция 03.03.2026</p>
<div class="user-bar" id="user-bar" style="display:none;">Вы вошли как: <span id="user-fio"></span></div>
</div>
<div id="screen-welcome" class="screen active">
<div class="welcome-card">
<h2>Вход в курс</h2>
<p>Укажите ваши фамилию, имя и отчество для регистрации прохождения обучения и фиксации результата тестирования.</p>
<form id="welcome-form" onsubmit="return submitWelcome(event)">
<label for="input-fio">ФИО</label>
<input type="text" id="input-fio" placeholder="Иванов Иван Иванович" autocomplete="name" required maxlength="500">
<div class="welcome-error" id="welcome-error"></div>
<button type="submit" class="btn-start" id="btn-welcome-start">Начать обучение</button>
</form>
</div>
</div>
<div id="screen-menu" class="screen">
<div class="progress-bar-wrap">
<span class="progress-label">Прогресс курса</span>
<div class="progress-track"><div class="progress-fill" id="main-progress" style="width:0%"></div></div>
<span class="progress-count" id="progress-text">0 / 4</span>
</div>
<div class="modules-grid" id="modules-grid"></div>
</div>
<div id="screen-lesson" class="screen">
<div class="lesson-nav">
<button class="btn-back" onclick="goMenu()">← Назад</button>
<div class="lesson-title-bar">
<h2 id="lesson-title"></h2>
<div class="lesson-sub" id="lesson-sub"></div>
</div>
</div>
<div class="slide-container">
<div id="slides-host"></div>
<div class="slide-controls">
<div class="slide-dots" id="slide-dots"></div>
<div style="display:flex;gap:10px">
<button class="btn-slide" id="btn-prev" onclick="changeSlide(-1)">← Назад</button>
<button class="btn-slide primary" id="btn-next-slide" onclick="changeSlide(1)">Далее →</button>
</div>
</div>
</div>
</div>
<div id="screen-quiz" class="screen">
<div class="quiz-header">
<div class="quiz-icon">📝</div>
<div class="quiz-header-text">
<h2>Итоговое тестирование</h2>
<p>Подтверждение прохождения курса «Управление ИТ»</p>
</div>
<div class="quiz-stats">
<div class="score-num" id="quiz-score">0</div>
<div class="score-label">правильных</div>
</div>
</div>
<div class="progress-bar-wrap">
<span class="progress-label">Вопрос</span>
<div class="progress-track"><div class="progress-fill" id="quiz-progress" style="width:0%"></div></div>
<span class="progress-count" id="quiz-q-count">1 / 25</span>
</div>
<div id="question-host"></div>
<div class="quiz-nav">
<button class="btn-next" id="btn-next-q" onclick="nextQuestion()">Следующий вопрос →</button>
</div>
</div>
<div id="screen-results" class="screen">
<div class="results-card">
<span class="trophy" id="result-trophy">🏆</span>
<h2 id="result-title"></h2>
<div class="score-big" id="result-score"></div>
<p class="results-desc" id="result-desc"></p>
<div class="results-breakdown">
<div class="rb-card"><div class="rb-val" id="rb-correct"></div><div class="rb-label">Правильно</div></div>
<div class="rb-card"><div class="rb-val" id="rb-wrong"></div><div class="rb-label">Ошибок</div></div>
<div class="rb-card"><div class="rb-val" id="rb-percent"></div><div class="rb-label">Результат</div></div>
</div>
<div id="cert-host"></div>
<div>
<button class="btn-restart" onclick="restartQuiz()">↺ Пройти тест снова</button>
<button class="btn-outline" onclick="goMenu()">В меню курса</button>
</div>
</div>
</div>
</div>
<script>
const modules = [
{
id: 0, icon: '📋', title: 'Работа с запросами', sub: 'Раздел 1.11.2',
desc: 'Типы запросов, порядок подачи через ЕЗС, уведомления и эскалации, жизненный цикл инцидента, 4 линии поддержки, порядок маршрутизации и роль Менеджера инцидентов.',
tags: ['Запросы', 'Инциденты', 'ЛП', 'Эскалации'],
slides: [
{
title: 'Типы ИТ-запросов', num: 'Раздел 1 · Подраздел 1.1',
html: `
<div class="slide-num">Раздел 1 · Подраздел 1.1</div>
<h3>Три типа <span>ИТ-запросов</span></h3>
<p>Все обращения в Службу ИТ делятся на три категории. Правильная классификация запроса — обязательное условие его своевременного исполнения.</p>
<ul>
<li><strong>Обслуживание</strong> — обращение для получения ИТ-сервисов: доступы, оборудование, консультации, администрирование</li>
<li><strong>Инцидент</strong> — уведомление о неисправности информационной системы или ИТ-оборудования</li>
<li><strong>Изменение</strong> — разработка нового или изменение текущего функционала информационной системы</li>
</ul>
<div class="warn-box">⚠️ <strong>Важно:</strong> если инцидент содержит запрос на изменение ИС — он не может быть выполнен в рамках управления инцидентами. Инициатору будет отказано с рекомендацией оформить отдельный запрос на изменение согласно Подразделу 1.3.</div>
<div class="tip-box">✅ Все сотрудники компании обязаны знать правила работы с ИТ-сервисами, изложенные в Руководстве по ИТ.</div>
`
},
{
title: 'Способы подачи запросов', num: 'Раздел 1 · Подраздел 1.1',
html: `
<div class="slide-num">Раздел 1 · Подраздел 1.1</div>
<h3>Каналы подачи <span>запросов</span></h3>
<h4>Запрос на обслуживание</h4>
<ul>
<li><strong>Новые сотрудники</strong> — указывается в Заявке на трудоустройство (согласно регламенту КДП)</li>
<li><strong>Действующие сотрудники</strong> — в ЕЗС: кнопка «Добавить запрос» → «ИТ Сервис» → выбрать нужный вид сервиса с типом «Обслуживание»</li>
</ul>
<h4>Инцидент — для всех пользователей</h4>
<ul>
<li>В ЕЗС: выбрать сервис с типом «Инцидент»</li>
<li>📧 Электронная почта: <strong>it@omc.ru</strong></li>
<li>📞 Телефон единой поддержки: <strong>8-800-700-37-18</strong></li>
<li>Виджет на корпоративном портале или чат-бот в Telegram: <strong>@HelpOMCbot</strong></li>
</ul>
<h4>Изменение ИС</h4>
<ul>
<li>В ЕЗС: меню «Зарегистрировать Изменение ИС» → «Изменение/Доработка»</li>
</ul>
`
},
{
title: 'Уведомления и эскалации', num: 'Раздел 1 · Подраздел 1.1',
html: `
<div class="slide-num">Раздел 1 · Подраздел 1.1 + Приложение 1</div>
<h3>Система <span>оповещений</span></h3>
<p>Служба ИТ уведомляет инициатора через ЕЗС и по e-mail о статусе запроса и необходимости выполнения действий.</p>
<h4>Ключевые оповещения по инцидентам</h4>
<ul>
<li>При <strong>регистрации</strong> инцидента — инициатор получает номер, тип, SLA, описание, время регистрации</li>
<li>При переводе в статус <strong>«Решён»</strong> — инициатор получает ссылки для оценки удовлетворённости и возврата в работу</li>
<li>При <strong>эскалации на 80%</strong> срока — уведомляются исполнитель, старший группы поддержки, начальник отдела</li>
<li>При <strong>эскалации на 100%</strong> — добавляется контроль; нарушение фиксируется в системе</li>
</ul>
<div class="info-box">💡 Если превышено время регистрации или нарушен крайний срок исполнения — Система автоматически направляет оповещения и эскалации согласно Приложению 1 «Эскалации и оповещения».</div>
<div class="tip-box">✅ По завершении ИТ-сервиса инициатору направляется запрос на подтверждение завершения работ и оценку качества. Оценки влияют на KPI сотрудников ИТ.</div>
`
},
{
title: 'Жизненный цикл инцидента', num: 'Раздел 1 · Подраздел 1.2',
html: `
<div class="slide-num">Раздел 1 · Подраздел 1.2</div>
<h3>Статусы <span>инцидента</span></h3>
<div class="flow-steps">
<div class="flow-step">
<div class="flow-num">1</div>
<div class="flow-content">
<strong>Зарегистрирован</strong>
<span>Присваивается автоматически при поступлении запроса. При регистрации через телефон/email — Диспетчерская служба назначает на группу поддержки.</span>
</div>
</div>
<div class="flow-step">
<div class="flow-num">2</div>
<div class="flow-content">
<strong>Выполняется</strong>
<span>Инцидент назначен на линию поддержки. При регистрации через ЕЗС — система автоматически назначает группу по сервису и географии инициатора.</span>
</div>
</div>
<div class="flow-step">
<div class="flow-num">3</div>
<div class="flow-content">
<strong>Передан внешнему контрагенту</strong>
<span>Изменяется исполнителем при формировании задания внешней стороне. Исполнитель инцидента остаётся прежним до завершения задания.</span>
</div>
</div>
<div class="flow-step">
<div class="flow-num">4</div>
<div class="flow-content">
<strong>Решён</strong>
<span>Переводится исполнителем, если работы выполнены полностью. Ожидается подтверждение от инициатора.</span>
</div>
</div>
<div class="flow-step">
<div class="flow-num">5</div>
<div class="flow-content">
<strong>Закрыт</strong>
<span>Система закрывает автоматически: при получении подтверждения инициатором / аннулировании им / по истечении 3 дней ожидания с момента статуса «Решён».</span>
</div>
</div>
</div>
<div class="info-box">💡 Повторяющиеся инциденты (одни условия, тот же результат, те же причины) — решаются по известному решению, а Служба ИТ регистрирует корневую <strong>Проблему</strong> для устранения в рамках управления проблемами.</div>
`
},
{
title: 'Линии поддержки', num: 'Раздел 1 · Подраздел 1.2',
html: `
<div class="slide-num">Раздел 1 · Подраздел 1.2</div>
<h3>4 линии <span>поддержки</span></h3>
<div class="lp-card">
<div class="lp-badge">0</div>
<div class="lp-content">
<h4>Диспетчерская служба</h4>
<p>Приём, регистрация, обработка и решение типовых инцидентов. Направление: все инциденты.</p>
</div>
</div>
<div class="lp-card">
<div class="lp-badge">1</div>
<div class="lp-content">
<h4>Отдел технической поддержки</h4>
<p>Решение инцидентов, консультирование пользователей, эскалация на более высокоуровневые ЛП. Направление: ПО на рабочих местах сотрудников.</p>
</div>
</div>
<div class="lp-card">
<div class="lp-badge">2</div>
<div class="lp-content">
<h4>Отделы компетенций (инфраструктура, ИС, разработка)</h4>
<p>Консультирование, решение инцидентов, эскалация. Направления: инфраструктурные сервисы, телефония и каналы связи, закупки (ИТ), информационные системы.</p>
</div>
</div>
<div class="lp-card">
<div class="lp-badge">3</div>
<div class="lp-content">
<h4>Внешние подрядные организации</h4>
<p>Консультирование и решение инцидентов. Порядок работы регулируется договорами с подрядчиками.</p>
</div>
</div>
`
},
{
title: 'Маршрутизация и роли', num: 'Раздел 1 · Подраздел 1.2',
html: `
<div class="slide-num">Раздел 1 · Подраздел 1.2</div>
<h3>Маршрутизация инцидентов <span>и задач</span></h3>
<p>При невозможности самостоятельно решить инцидент исполнитель может:</p>
<ul>
<li>Создать <strong>Задачу</strong> и назначить её на другую группу поддержки (в рамках инцидента)</li>
<li><strong>Переназначить</strong> инцидент на другую группу специалистов</li>
<li>Если исполнитель не определён или есть административные препятствия — <strong>назначить на Менеджера инцидентов</strong></li>
</ul>
<h4>Правила назначения по линиям</h4>
<table class="sla-table">
<tr><th>Специалист</th><th>Может назначить на</th></tr>
<tr><td>1-я ЛП</td><td>Группу 2-й ЛП; Менеджеру инцидентов</td></tr>
<tr><td>2-я ЛП</td><td>Группе 1-й, 2-й или 3-й ЛП; Менеджеру инцидентов</td></tr>
<tr><td>3-я ЛП</td><td>Группе 2-й ЛП; Менеджеру инцидентов</td></tr>
<tr><td>Менеджер инцидентов</td><td>Любой группе 1-й, 2-й, 3-й ЛП; конкретному специалисту</td></tr>
</table>
<div class="warn-box">⚠️ До исполнения задания исполнитель инцидента не может быть изменён. Менеджер инцидентов при маршрутизации обязан указать в комментариях требуемые для решения действия.</div>
`
}
]
},
{
id: 1, icon: '🔄', title: 'Управление изменениями', sub: 'Раздел 1.3',
desc: 'Полный жизненный цикл запроса на изменение ИС: инициирование, цепочка согласований (РФО, Служба ИТ, Владелец ИС, ГД), реестр, разработка, тестирование, передача в поддержку.',
tags: ['Изменения', 'Согласование', 'КИ', 'РФО', 'ВИС'],
slides: [
{
title: 'Инициирование запроса', num: 'Раздел 1 · Подраздел 1.3',
html: `
<div class="slide-num">Раздел 1 · Подраздел 1.3</div>
<h3>Запрос на <span>изменение ИС</span></h3>
<p>Запрос создаётся инициатором в ЕЗС: «Зарегистрировать Изменение ИС» → «Изменение/Доработка». Форма запроса описана в Приложении 5.</p>
<p><strong>Обязательный состав запроса:</strong></p>
<ul>
<li>Описание изменений в бизнес-функциональности ИС</li>
<li>Цели, результаты, которые предполагается достичь</li>
<li>Перечень отчётных форм (если предусмотрены в рамках ЗНИ)</li>
<li>Описание преимуществ и выгод для Компании</li>
<li>Критичность, срочность и масштаб влияния на бизнес</li>
<li>При изменении законодательства — ссылки на регулирующие документы и подтверждение от ГД (если финансирование уже утверждено)</li>
</ul>
<div class="tip-box">✅ Ответственность за заполнение — на инициаторе. Проверка соответствия целей и метрик — на Руководителе Функциональной Области (РФО).</div>
`
},
{
title: 'Согласование РФО', num: 'Раздел 1 · Подраздел 1.3',
html: `
<div class="slide-num">Раздел 1 · Подраздел 1.3</div>
<h3>Первый этап: согласование <span>РФО</span></h3>
<p>Запрос направляется на согласование Руководителю Функциональной Области (РФО) согласно Приложению 2 «РФО и Владельцы систем».</p>
<h4>Правила маршрутизации к РФО</h4>
<ul>
<li><strong>Уровень КЦ</strong> → согласует РФО уровня ТОП-0,1</li>
<li><strong>Уровень ДЦ</strong> → сначала РФО ТОП-2 ДЦ, затем РФО ТОП-0,1 КЦ (при наличии). РФО ДЦ согласовывает вне ЕЗС и прикладывает письмо</li>
<li><strong>Междивизиональная услуга</strong> → согласуют все РФО дивизионов, где присутствует услуга</li>
</ul>
<h4>Точки контроля РФО</h4>
<ul>
<li>Отсутствие противоречий с корпоративными Стандартами</li>
<li>Применимость доработки для всей Компании (если функционал есть во всех Дивизионах)</li>
</ul>
<h4>Решения РФО</h4>
<ul class="compact">
<li>Согласовать → переход к параллельному согласованию Службой ИТ и Владельцем ИС</li>
<li>Вернуть на доработку → инициатор дорабатывает и повторно согласовывает</li>
<li>Аргументированно отклонить → запрос закрывается</li>
</ul>
<div class="info-box">💡 Срок согласования РФО — <strong>1 рабочий день</strong>. Срок на доработку запроса инициатором — <strong>1 рабочий день</strong>.</div>
`
},
{
title: 'Согласование Службой ИТ и Владельцем ИС', num: 'Раздел 1 · Подраздел 1.3',
html: `
<div class="slide-num">Раздел 1 · Подраздел 1.3</div>
<h3>Параллельное согласование: <span>ИТ и Владелец</span></h3>
<h4>Критерии Службы ИТ (1 раб. день)</h4>
<ul>
<li><strong>Новизна</strong> — изменение не было реализовано ранее и не реализуется сейчас</li>
<li><strong>Архитектура</strong> — не противоречит архитектуре ИС Компании</li>
<li><strong>Реализуемость</strong> — возможна доработка существующих ИС без внедрения новых</li>
<li><strong>Уникальность</strong> — отсутствие дублирующих запросов</li>
</ul>
<p>Если запрос требует внедрения новых ИС — Служба ИТ отклоняет его и информирует инициатора о переходе к проектной деятельности.</p>
<h4>Критерии Владельца ИС (1 раб. день)</h4>
<ul>
<li>Отсутствие противоречий со Стандартами Компании</li>
<li>Отсутствие противоречий с логикой настройки системы</li>
</ul>
<div class="info-box">💡 При отклонении Владельцем ИС — инициатор вправе созвать рабочее совещание с участием Владельца ИС, Руководителя Службы ИТ и РФО. По итогам принимается совместное решение.</div>
<div class="warn-box">⚠️ Если изменение классифицировано как <strong>Комплексное (КИ)</strong> — требуется согласование Руководителя КИ (срок: 3 раб. дня). Запросу присваивается признак КИ, формируется рабочая группа.</div>
`
},
{
title: 'Реестр и согласование с ГД', num: 'Раздел 1 · Подраздел 1.3',
html: `
<div class="slide-num">Раздел 1 · Подраздел 1.3</div>
<h3>Реестр изменений <span>и ГД</span></h3>
<p>По итогам согласований запросы делятся на 5 групп:</p>
<table class="sla-table">
<tr><th>Группа</th><th>Следующий шаг</th></tr>
<tr><td>Отклонённые запросы</td><td>Запрос закрывается</td></tr>
<tr><td>Изменения законодательства / ВНД (финансирование есть)</td><td>Сразу к разработке</td></tr>
<tr><td>Комплексные изменения (КИ)</td><td>В реестр, доп. рассмотрение</td></tr>
<tr><td>Требующие доп. финансирования</td><td>В реестр, доп. рассмотрение</td></tr>
<tr><td>Внутренние ресурсы, без доп. финансирования</td><td>В реестр, очередь на реализацию</td></tr>
</table>
<h4>Регламент реестра</h4>
<ul>
<li>Реестр формируется ежемесячно по всем поступившим и согласованным в ЕЗС запросам</li>
<li>Рассмотрение — 1 раз в месяц, <strong>не позже 7 рабочих дней</strong> с начала нового месяца</li>
<li>Ответственный за формирование — <strong>Директор по ИТ</strong></li>
<li>Совещание с ГД: обязательно участие ГД, Финансового директора КЦ, Директора по ИТ</li>
</ul>
<div class="info-box">💡 Срочный запрос, не терпящий отлагательств — Директор по ИТ инициирует <strong>внеплановое заочное рассмотрение</strong>: информация о запросе, стоимости и сроках направляется ГД и ФД КЦ по email.</div>
`
},
{
title: 'Разработка, тестирование и внедрение', num: 'Раздел 1 · Подраздел 1.3',
html: `
<div class="slide-num">Раздел 1 · Подраздел 1.3</div>
<h3>Разработка и <span>внедрение</span></h3>
<div class="flow-steps">
<div class="flow-step">
<div class="flow-num">1</div>
<div class="flow-content">
<strong>Оценка стоимости и сроков</strong>
<span>Служба ИТ оценивает стоимость и сроки реализации в течение <strong>7 раб. дней</strong> после согласования. Если запрос затрагивает несколько ИС — оценивается каждая система.</span>
</div>
</div>
<div class="flow-step">
<div class="flow-num">2</div>
<div class="flow-content">
<strong>Формирование ФТ</strong>
<span>Если требуется новая функциональность — Служба ИТ разрабатывает Функциональные Требования (<strong>10 раб. дней</strong>). ФТ согласовываются с РФО.</span>
</div>
</div>
<div class="flow-step">
<div class="flow-num">3</div>
<div class="flow-content">
<strong>Разработка</strong>
<span>Ведётся в согласованные сроки. При необходимости формируется «Программа и методика испытаний» (согласуется с РФО).</span>
</div>
</div>
<div class="flow-step">
<div class="flow-num">4</div>
<div class="flow-content">
<strong>Тестирование</strong>
<span>Проводят инициатор и РФО в согласованные сроки. Замечания, противоречащие исходным требованиям, оформляются как <strong>новый запрос на изменение</strong>.</span>
</div>
</div>
<div class="flow-step">
<div class="flow-num">5</div>
<div class="flow-content">
<strong>Внедрение и оценка</strong>
<span>После успешного тестирования — внедрение в продуктивную среду, запрос закрывается. Инициатор получает запрос на оценку сроков и качества.</span>
</div>
</div>
</div>
<div class="warn-box">⚠️ При внедрении новой ИС или существенном улучшении — Отдел развития ИС обязан передать систему в поддержку с документом «Передача ИС в поддержку» (Приложение 7). Документ утверждается Директором по ИТ.</div>
`
},
{
title: 'Руководители функциональных областей', num: 'Приложение 2',
html: `
<div class="slide-num">Приложение 2 · РФО и Владельцы систем</div>
<h3>Руководители <span>функциональных областей (РФО)</span></h3>
<p>РФО — руководитель функциональной области уровня ТОП-0,1, ММ для КЦ; ТОП-0,1,2 для ДЦ. Именно к ним направляется запрос на изменение ИС на первичное согласование.</p>
<p><strong>Примеры РФО по функциональным областям (КЦ):</strong></p>
<div class="rfo-grid">
<div class="rfo-item"><div class="rfo-area">HR / Управление персоналом</div><div class="rfo-name">Директор по управлению персоналом КЦ</div></div>
<div class="rfo-item"><div class="rfo-area">Экономика и финансы</div><div class="rfo-name">Финансовый директор КЦ</div></div>
<div class="rfo-item"><div class="rfo-area">Маркетинг</div><div class="rfo-name">Директор по маркетингу</div></div>
<div class="rfo-item"><div class="rfo-area">Качество (SQI)</div><div class="rfo-name">Директор по качеству и инновациям</div></div>
<div class="rfo-item"><div class="rfo-area">Управление проектами</div><div class="rfo-name">Директор по проектам</div></div>
<div class="rfo-item"><div class="rfo-area">Договорной / внутренний документооборот</div><div class="rfo-name">Руководитель отдела стандартов</div></div>
</div>
<p style="margin-top:14px;"><strong>Примеры РФО по направлениям (ДFFM, ДRS):</strong></p>
<div class="rfo-grid">
<div class="rfo-item"><div class="rfo-area">Услуги питания (ДFFM / ДRS)</div><div class="rfo-name">Директор по кейтерингу</div></div>
<div class="rfo-item"><div class="rfo-area">Услуга охрана / клининг (ДFFM)</div><div class="rfo-name">Директор по клинингу / услуге Охрана</div></div>
<div class="rfo-item"><div class="rfo-area">Техническая эксплуатация (ДFFM)</div><div class="rfo-name">Директор по технической эксплуатации</div></div>
<div class="rfo-item"><div class="rfo-area">Финансы (ДFFM, ДRS)</div><div class="rfo-name">Зам. фин. директора по направлению</div></div>
</div>
<div class="tip-box">✅ Полный актуальный перечень РФО по всем функциональным областям, дивизионам и филиалам закреплён в <strong>Приложении 2 «РФО и Владельцы систем»</strong>.</div>
`
},
{
title: 'Владельцы информационных систем', num: 'Приложение 2',
html: `
<div class="slide-num">Приложение 2 · РФО и Владельцы систем</div>
<h3>Владельцы <span>информационных систем</span></h3>
<p><strong>Владелец ИС</strong> — сотрудник Компании, ответственный за эксплуатацию ИС и предоставление прав доступа к ней. Именно Владелец ИС параллельно с Службой ИТ согласовывает запрос на изменение.</p>
<div class="is-grid">
<div class="is-item"><span class="is-name">ЕЗС</span><span class="is-owner">Директор по ИТ</span></div>
<div class="is-item"><span class="is-name">CRM</span><span class="is-owner">Директор по работе с Клиентами КЦ</span></div>
<div class="is-item"><span class="is-name">СЭД</span><span class="is-owner">Рук. отдела стандартов</span></div>
<div class="is-item"><span class="is-name">1С УХ</span><span class="is-owner">Финансовый директор КЦ</span></div>
<div class="is-item"><span class="is-name">1С БУХ</span><span class="is-owner">Главный бухгалтер</span></div>
<div class="is-item"><span class="is-name">ЗУП КОРП</span><span class="is-owner">Директор по управл. персоналом КЦ</span></div>
<div class="is-item"><span class="is-name">УПП ОМС / RS</span><span class="is-owner">Директор по проектам</span></div>
<div class="is-item"><span class="is-name">МДМ / BI</span><span class="is-owner">Рук. отдела компетенций по ИС</span></div>
<div class="is-item"><span class="is-name">СКК</span><span class="is-owner">Директор по качеству и инновациям</span></div>
<div class="is-item"><span class="is-name">Корп. Портал</span><span class="is-owner">Директор по управл. персоналом КЦ</span></div>
<div class="is-item"><span class="is-name">R-Keeper</span><span class="is-owner">Рук. отдела компетенций по ИС</span></div>
<div class="is-item"><span class="is-name">ЭДО / КЭДО</span><span class="is-owner">Рук. отдела компетенций по ИС</span></div>
<div class="is-item"><span class="is-name">TalentForce</span><span class="is-owner">Директор по управл. персоналом КЦ</span></div>
<div class="is-item"><span class="is-name">Сайты omc.ru</span><span class="is-owner">Директор по маркетингу</span></div>
<div class="is-item"><span class="is-name">Меню-онлайн</span><span class="is-owner">Рук. отдела разработки ИС</span></div>
<div class="is-item"><span class="is-name">ЛК Заказчика</span><span class="is-owner">Директор по ИТ</span></div>
</div>
<div class="tip-box">✅ Полный актуальный список — в <strong>Приложении 2 «РФО и Владельцы систем»</strong>. Проверяйте перед подачей запроса на изменение.</div>
`
}
]
},
{
id: 2, icon: '💻', title: 'Оборудование и ПО', sub: 'Раздел 2',
desc: 'Общие принципы обеспечения сотрудников АРМ, стандартное ПО, корпоративная мобильная связь и печатное оборудование: правила предоставления и использования.',
tags: ['АРМ', 'ПО', 'Мобильная связь', 'Печать'],
slides: [
{
title: 'АРМ: принципы обеспечения', num: 'Раздел 2 · Подраздел 2.1',
html: `
<div class="slide-num">Раздел 2 · Подраздел 2.1</div>
<h3>Автоматизированное <span>рабочее место</span></h3>
<p><strong>АРМ</strong> — комплекс средств вычислительной техники и ПО, расположенный на рабочем месте сотрудника для автоматизации его работы в рамках должности.</p>
<h4>Критерии определения перечня оборудования</h4>
<ul>
<li><strong>Характеристика рабочего места:</strong> стационарное или нестационарное</li>
<li><strong>Правила установки:</strong> обязательное (по умолчанию для всех сотрудников со стационарным РМ) или по запросу (ТОП-0,1 при наличии бюджета)</li>
<li>Технические ограничения и специфика функционала сотрудника</li>
</ul>
<h4>Правила использования</h4>
<ul>
<li>Пользователи несут <strong>материальную ответственность</strong> за эксплуатируемое ими оборудование</li>
<li>Оборудование и ПО — только для выполнения <strong>должностных обязанностей</strong></li>
<li>Использование в личных целях (веб-сайты, личные документы) — <strong>запрещено</strong></li>
<li>Служба ИТ не осуществляет поддержку личного оборудования и ПО</li>
</ul>
<div class="warn-box">⚠️ При выявлении нарушений использования оборудования Служба ИТ временно приостанавливает доступ пользователя, уведомив его руководителя.</div>
`
},
{
title: 'Стандартное ПО', num: 'Раздел 2 · Подраздел 2.1',
html: `
<div class="slide-num">Раздел 2 · Подраздел 2.1</div>
<h3>Стандартное <span>программное обеспечение</span></h3>
<p>На ноутбуки и системные блоки по умолчанию устанавливается следующий набор ПО:</p>
<table class="sla-table">
<tr><th>ПО</th><th>Назначение</th></tr>
<tr><td><strong>MS Windows 10/11</strong></td><td>Операционная система</td></tr>
<tr><td><strong>MS Office 20162019</strong></td><td>Офисный пакет (Excel, Word, PowerPoint)</td></tr>
<tr><td><strong>Adobe Reader</strong></td><td>Просмотр и работа с файлами PDF</td></tr>
<tr><td><strong>PRO32 Connect</strong></td><td>Удалённое подключение к ПК</td></tr>
<tr><td><strong>7-Zip</strong></td><td>Файловый архиватор</td></tr>
<tr><td><strong>Autodesk DWG TrueView</strong></td><td>Просмотр и печать чертежей DWG</td></tr>
<tr><td><strong>Zoom</strong></td><td>Видеосвязь, сообщения, файлы</td></tr>
</table>
<div class="info-box">💡 Для новых сотрудников требуемое оборудование и ПО указывается в Заявке на трудоустройство. Закупка осуществляется через заявку в ЕЗС при наличии бюджета.</div>
`
},
{
title: 'Корпоративная мобильная связь', num: 'Раздел 2 · Подраздел 2.2',
html: `
<div class="slide-num">Раздел 2 · Подраздел 2.2</div>
<h3>Корпоративная <span>мобильная связь</span></h3>
<p>Сотрудник может получить корпоративную SIM-карту или подключить личную к корпоративному договору. Служба ИТ принимает заявку и связывается с сотрудником в течение <strong>3 рабочих дней</strong>.</p>
<h4>Сервисы по умолчанию</h4>
<ul class="compact">
<li>GPRS, исходящая связь (домашний регион, МГ, МН)</li>
<li>SMS и MMS (домашний регион, МГ, МН)</li>
<li>Запрет SMS-контента (короткие номера)</li>
</ul>
<h4>По запросу</h4>
<ul class="compact">
<li>Роуминг (международный, национальный), оптимизирующие тарифы</li>
<li>Блокировки, переадресация, смена тарифа, переоформление номера</li>
</ul>
<h4>Финансовая ответственность</h4>
<ul>
<li>При превышении лимита — сумма вычитается из зарплаты месяца перерасхода</li>
<li>Компенсация возможна только при согласовании с ТОП 0-1 (КЦ) / ТОП 0-2 (ДЦ)</li>
<li>Счёт от провайдера — в Службу финансов не позже <strong>10 дней</strong> с момента получения</li>
</ul>
<div class="warn-box">⚠️ При увольнении сотрудника блокировка SIM-карты происходит <strong>в день обращения</strong>. Все запросы по мобильной связи оформляются через Службу ИТ.</div>
`
},
{
title: 'Печатное оборудование', num: 'Раздел 2 · Подраздел 2.3',
html: `
<div class="slide-num">Раздел 2 · Подраздел 2.3</div>
<h3>Принципы печатной <span>инфраструктуры</span></h3>
<p>Вся офисная печать переведена на устройства общего доступа. Это повышает эффективность использования оборудования и снижает TCO.</p>
<h4>Ключевые принципы размещения</h4>
<ul>
<li>Соотношение пользователи/устройство: <strong>30:1</strong></li>
<li>Максимальное расстояние до монохромного принтера А4: <strong>15 метров</strong></li>
<li>До устройства копирования / сканирования А4: <strong>30 метров</strong></li>
<li>Цветной принтер А4: <strong>одно устройство на этаж</strong></li>
<li>Доступ к принтеру — без необходимости подниматься/спускаться или проходить через охраняемые зоны</li>
</ul>
<h4>Ограничения</h4>
<ul>
<li>Персональные принтеры — не допускаются (только по утверждённым исключениям)</li>
<li>Струйные принтеры — не допускаются (только по исключениям)</li>
<li>Принтер по умолчанию для пользователей не может быть цветным</li>
<li>Все устройства должны быть подключены к сети</li>
</ul>
<div class="info-box">💡 Базовые бренды — <strong>HP и Xerox</strong>. Не более двух производителей в парке. Исключения для специальных задач утверждаются Директором Дивизиона (КЦ — Топ 1).</div>
`
}
]
},
{
id: 3, icon: '📊', title: 'SLA и KPI', sub: 'Раздел 3',
desc: 'Уровни сервиса для разных групп пользователей, критичность инцидентов, конкретные сроки по всем типам запросов и KPI ИТ-службы с целевыми значениями.',
tags: ['SLA', 'KPI', 'Сроки', 'VIP'],
slides: [
{
title: 'SLA — Запросы на обслуживание', num: 'Раздел 3 · Подраздел 3.1',
html: `
<div class="slide-num">Раздел 3 · Подраздел 3.1</div>
<h3>Уровни сервиса: <span>обслуживание</span></h3>
<p>Сроки зависят от группы пользователя:</p>
<table class="sla-table">
<tr><th>Группа</th><th>Режим</th><th>Срок согласования</th><th>Срок решения</th></tr>
<tr><td><strong>VIP</strong> (ТОП 01)</td><td class="highlight">24×7</td><td>Без согласования</td><td class="highlight">8 часов</td></tr>
<tr><td>Сотрудники офисов</td><td>9×5</td><td>8 часов на согласование; по истечении — отклонение</td><td>По таблице сервисов</td></tr>
</table>
<h4>Сроки ключевых сервисов (для офисных сотрудников)</h4>
<table class="sla-table">
<tr><th>Сервис</th><th>Срок</th></tr>
<tr><td>Предоставление / изменение доступа к ИС</td><td class="highlight">24 ч</td></tr>
<tr><td>Администрирование учётных записей AD</td><td class="highlight">24 ч</td></tr>
<tr><td>Администрирование справочников НСИ</td><td class="highlight">24 ч</td></tr>
<tr><td>Комплексная печать (КЦ)</td><td class="highlight">24 ч</td></tr>
<tr><td>Администрирование ПО кассовой системы (POS)</td><td class="highlight">8 ч</td></tr>
<tr><td>Консультирование, организация РМ, восстановление данных, закупка, мобильная связь и прочее</td><td>32 ч</td></tr>
</table>
`
},
{
title: 'SLA — Инциденты', num: 'Раздел 3 · Подраздел 3.2',
html: `
<div class="slide-num">Раздел 3 · Подраздел 3.2</div>
<h3>Параметры SLA: <span>инциденты</span></h3>
<table class="sla-table">
<tr><th>Критичность</th><th>Режим</th><th>Реакция оператора</th><th>Регистрация</th><th>Реакция исп.</th><th>Срок устранения</th></tr>
<tr><td class="critical">Критичный (VIP или ИС)</td><td class="highlight">24×7</td><td>30 сек</td><td>15 мин</td><td>0,5 ч</td><td class="highlight">4 часа</td></tr>
<tr><td style="color:var(--gold)">Высокий</td><td>9×5</td><td>30 сек</td><td>15 мин</td><td>4 ч</td><td class="highlight">8 часов</td></tr>
<tr><td>Стандартный</td><td>9×5</td><td>30 сек</td><td>15 мин</td><td>8 ч</td><td class="highlight">32 часа</td></tr>
</table>
<h4>Определения критичности</h4>
<ul>
<li><strong class="critical">Критичный</strong> — останавливает работу системы / аппаратного комплекса, влечёт серьёзные финансовые или репутационные риски</li>
<li><strong style="color:var(--gold)">Высокий</strong> — частично затрагивает работу системы, существует риск остановки бизнес-процесса</li>
<li><strong>Стандартный</strong> — ошибка, не останавливающая работу и бизнес-процесс, не влекущая репутационных или финансовых потерь</li>
</ul>
<div class="warn-box">⚠️ Любой инцидент от VIP-пользователя (или его ассистента) считается критичным и обрабатывается в режиме 24×7, срок — 4 часа.</div>
`
},
{
title: 'SLA — Изменения', num: 'Раздел 3 · Подраздел 3.3',
html: `
<div class="slide-num">Раздел 3 · Подраздел 3.3</div>
<h3>Регламентные сроки <span>по изменениям</span></h3>
<table class="sla-table">
<tr><th>#</th><th>Этап</th><th>Ответственный</th><th>Срок (р.д.)</th></tr>
<tr><td>1</td><td>Согласование РФО</td><td>РФО</td><td class="highlight">1</td></tr>
<tr><td>2</td><td>Доработка запроса</td><td>Инициатор</td><td class="highlight">1</td></tr>
<tr><td>3</td><td>Согласование Службой ИТ</td><td>Служба ИТ</td><td class="highlight">1</td></tr>
<tr><td>4</td><td>Согласование Владельцем ИС</td><td>Владелец ИС</td><td class="highlight">1</td></tr>
<tr><td>5</td><td>Согласование Руководителем КИ</td><td>Руководитель КИ</td><td class="highlight">3</td></tr>
<tr><td>7</td><td>Оценка стоимости и сроков</td><td>Служба ИТ</td><td class="highlight">7</td></tr>
<tr><td>10</td><td>Формирование ФТ</td><td>Служба ИТ</td><td class="highlight">10</td></tr>
<tr><td>14</td><td>Устранение замечаний тестирования</td><td>Служба ИТ</td><td class="highlight">2</td></tr>
</table>
<div class="warn-box">⚠️ Нарушение сроков по задачам может повлечь дисциплинарную ответственность. Перенос срока допустим только по договорённости с инициатором запроса.</div>
`
},
{
title: 'KPI ИТ-службы', num: 'Раздел 3 · Подраздел 3.4',
html: `
<div class="slide-num">Раздел 3 · Подраздел 3.4</div>
<h3>Ключевые показатели <span>эффективности</span></h3>
<table class="sla-table">
<tr><th>KPI</th><th>Целевое</th><th>Пороговое</th></tr>
<tr><td>Выполнение инцидентов в срок</td><td class="highlight">≥ 98%</td><td>≥ 97%</td></tr>
<tr><td>Возвращённые заявки (инциденты)</td><td class="highlight">≤ 0,5%</td><td>≤ 1%</td></tr>
<tr><td>Удовлетворённость пользователей (инциденты)</td><td class="highlight">≥ 98%</td><td>≥ 97%</td></tr>
<tr><td>Доступность ИС</td><td class="highlight">≥ 99,5%</td><td>≥ 99%</td></tr>
<tr><td>Удовлетворённость заказчика (изменения)</td><td class="highlight">≥ 95%</td><td>≥ 90%</td></tr>
<tr><td>Реализация изменений в срок</td><td class="highlight">≥ 95%</td><td>≥ 90%</td></tr>
</table>
<div class="tip-box">✅ Доступность ИС = (Д П) / Д × 100%, где Д — обещанное время доступности, П — время простоя. Запланированные простои из расчёта исключаются.</div>
<div class="info-box">💡 Оценки качества, проставленные сотрудниками после закрытия инцидентов и запросов на изменение, напрямую влияют на расчёт KPI конкретных специалистов ИТ.</div>
`
}
]
}
];
// ===================== QUESTIONS (25 шт) =====================
const questions = [
// --- ЗАПРОСЫ (7) ---
{ cat: 'Запросы', q: 'Какой тип запроса нужно создать, если система работает некорректно и мешает выполнению бизнес-процесса?', opts: ['Запрос на обслуживание', 'Инцидент', 'Запрос на изменение', 'Обращение к руководителю'], ans: 1, exp: 'Инцидент — это обращение в Службу ИТ для уведомления о неисправности информационной системы или ИТ-оборудования. Именно этот тип запроса используется при сбоях в работе системы.' },
{ cat: 'Запросы', q: 'Сотрудник хочет получить доступ к новой информационной системе. Какой тип запроса он должен создать?', opts: ['Инцидент', 'Изменение ИС', 'Запрос на обслуживание', 'Устное обращение к ИТ'], ans: 2, exp: 'Предоставление прав и доступов к ИС — это Запрос на обслуживание. Он подаётся в ЕЗС через кнопку «Добавить запрос» → «ИТ Сервис» → выбрать нужный вид сервиса с типом «Обслуживание».' },
{ cat: 'Запросы', q: 'Инцидент зарегистрирован в ЕЗС. Кто / что назначает его на группу поддержки?', opts: ['Диспетчерская служба вручную', 'Менеджер инцидентов', 'Система автоматически по сервису и географии', 'Инициатор выбирает группу'], ans: 2, exp: 'При регистрации через ЕЗС система автоматически назначает инцидент на Группу поддержки в соответствии с выбранным сервисом и географическим расположением инициатора. При регистрации по телефону/email — назначает Диспетчерская служба.' },
{ cat: 'Запросы', q: 'Через сколько дней инцидент в статусе «Решён» автоматически переводится в «Закрыт», если инициатор не реагирует?', opts: ['1 день', '2 дня', '3 дня', '5 дней'], ans: 2, exp: 'По регламенту инцидент автоматически переводится в статус «Закрыт» по истечении 3 дней с момента перевода в статус «Решён», если инициатор не подтвердил выполнение работ и не аннулировал запрос.' },
{ cat: 'Запросы', q: 'Что происходит, если инцидент содержит в описании запрос на добавление нового функционала в ИС?', opts: ['Инцидент выполняется полностью, включая изменение', 'Инциденту отказывают, рекомендуя оформить запрос на изменение', 'Инцидент передаётся на 3-ю линию поддержки', 'Инициатору предлагают переоформить как запрос на обслуживание'], ans: 1, exp: 'Инцидент, содержащий запрос на изменение ИС, не может быть выполнен в рамках управления инцидентами. Инициатору будет отказано с рекомендацией оформить запрос согласно Подразделу «Управление изменениями».' },
{ cat: 'Запросы', q: 'При нарушении срока исполнения инцидента — на какой процент времени срабатывает эскалация с оповещением начальника отдела?', opts: ['50%', '80%', '100%', 'Только после закрытия'], ans: 1, exp: 'По Приложению 1 «Эскалации и оповещения»: при достижении 80% регламентного времени уведомляются исполнитель, старший группы поддержки и начальник отдела. При 100% — добавляется контроль.' },
{ cat: 'Запросы', q: 'Какое действие специалист 1-й линии НЕ может выполнить самостоятельно?', opts: ['Решить стандартный инцидент по ПО', 'Назначить задачу на 2-ю ЛП', 'Назначить инцидент на Менеджера инцидентов', 'Назначить инцидент напрямую на 3-ю ЛП'], ans: 3, exp: 'Специалист 1-й ЛП может назначить инцидент или задачу только на группу специалистов 2-й ЛП или на Менеджера инцидентов. Прямое назначение на 3-ю ЛП — недопустимо, это прерогатива 2-й ЛП.' },
// --- ИЗМЕНЕНИЯ (9) ---
{ cat: 'Изменения', q: 'Кто несёт ответственность за корректное заполнение запроса на изменение ИС?', opts: ['Служба ИТ', 'Владелец ИС', 'Инициатор изменения', 'РФО'], ans: 2, exp: 'Ответственность за заполнение запроса лежит на инициаторе изменения. Проверка соответствия заявленных целей и метрик — на Руководителе Функциональной Области (РФО).' },
{ cat: 'Изменения', q: 'Запрос на изменение инициирован сотрудником Дивизиона (ДЦ). В каком порядке он согласовывается с РФО?', opts: ['Только с РФО КЦ уровня ТОП-0,1', 'Сначала с РФО ТОП-2 ДЦ, затем с РФО ТОП-0,1 КЦ (при наличии)', 'Только с РФО ДЦ, без КЦ', 'Одновременно с РФО ДЦ и КЦ через ЕЗС'], ans: 1, exp: 'Если запрос инициирован на уровне ДЦ, в согласующие последовательно включаются РФО уровня ТОП-2 ДЦ и РФО уровня ТОП-0,1 КЦ (при наличии в КЦ данной функциональной области). РФО ДЦ согласовывает вне ЕЗС и прикладывает письмо.' },
{ cat: 'Изменения', q: 'Что является критерием для классификации изменения ИС как Комплексного (КИ)?', opts: ['Стоимость изменения превышает бюджет', 'Изменение затрагивает более одной функциональной области и/или несколько ИС', 'Изменение инициировано на уровне ГД', 'Срок реализации превышает 3 месяца'], ans: 1, exp: 'Комплексное изменение (КИ) — разработка нового или изменение текущего функционала ИС, затрагивающая более одной функциональной области и/или несколько информационных систем, касающаяся нескольких Дивизионов/Служб и требующая выработки совместных решений.' },
{ cat: 'Изменения', q: 'Какой срок отводится Службе ИТ на оценку стоимости и сроков реализации изменений?', opts: ['3 рабочих дня', '5 рабочих дней', '7 рабочих дней', '10 рабочих дней'], ans: 2, exp: 'На оценку стоимости и сроков реализации изменений ИС Службе ИТ отводится 7 рабочих дней после согласования запроса.' },
{ cat: 'Изменения', q: 'В ходе тестирования выявлено замечание, которое противоречит первоначальным требованиям. Как оно обрабатывается?', opts: ['Устраняется Службой ИТ в рамках текущего запроса', 'Регистрируется как новый запрос на изменение ИС', 'Инициатор закрывает текущий запрос и создаёт новый', 'Выносится на совещание с ГД'], ans: 1, exp: 'Если выявленные замечания противоречат изначально заявленным требованиям, они регистрируются инициатором как новый Запрос на изменение ИС. При этом тестирование признаётся завершённым.' },
{ cat: 'Изменения', q: 'Кто является обязательным участником совещания по согласованию реестра изменений с ГД?', opts: ['Инициатор + РФО + Директор ИТ', 'ГД + Финансовый директор КЦ + Директор по ИТ', 'Владелец ИС + Директор ИТ + ГД', 'Только Директор по ИТ и ГД'], ans: 1, exp: 'Обязательные участники совещания: Генеральный Директор, Финансовый Директор КЦ и Директор по ИТ. При необходимости могут быть приглашены эксперты для дополнительных пояснений.' },
{ cat: 'Изменения', q: 'Кто является владельцем информационной системы ЕЗС?', opts: ['Руководитель отдела компетенций по ИС', 'Руководитель отдела разработки ИС', 'Директор по ИТ', 'Заместитель директора по поддержке ИС'], ans: 2, exp: 'Согласно Приложению 2 «РФО и Владельцы систем», владельцем ЕЗС является Директор по ИТ. Он же является владельцем ЛК Заказчика.' },
{ cat: 'Изменения', q: 'Кто является владельцем информационных систем CRM и 1С БУХ?', opts: ['CRM — Директор по ИТ; 1С БУХ — Финансовый директор КЦ', 'CRM — Директор по работе с Клиентами КЦ; 1С БУХ — Главный бухгалтер', 'Оба — Руководитель отдела компетенций по ИС', 'CRM — Директор по маркетингу; 1С БУХ — Директор по проектам'], ans: 1, exp: 'Согласно Приложению 2: владелец CRM — Директор по работе с Клиентами КЦ, владелец 1С БУХ — Главный бухгалтер. Это важно знать при подаче запроса на изменение этих систем.' },
{ cat: 'Изменения', q: 'РФО какой функциональной области согласовывает запрос на изменение в сфере Управления персоналом (КЦ)?', opts: ['Финансовый директор КЦ', 'Руководитель отдела стандартов', 'Директор по управлению персоналом КЦ', 'Директор по качеству и инновациям'], ans: 2, exp: 'Согласно Приложению 2 «РФО и Владельцы систем», РФО по функциональной области «Управление персоналом» на уровне КЦ — Директор по управлению персоналом КЦ.' },
// --- SLA (5) ---
{ cat: 'SLA', q: 'Каков режим обслуживания для VIP-пользователей (ТОП 01)?', opts: ['9×5 (рабочие часы)', '16×7', '24×5', '24×7 (круглосуточно)'], ans: 3, exp: 'VIP-пользователи (руководители ТОП 01) обслуживаются в режиме 24×7 — круглосуточно, без выходных. Любой инцидент от VIP считается критичным с SLA 4 часа.' },
{ cat: 'SLA', q: 'Инцидент частично затрагивает работу ИС, существует риск остановки бизнес-процесса. Какой SLA (срок устранения)?', opts: ['4 часа', '8 часов', '16 часов', '32 часа'], ans: 1, exp: 'Это инцидент с критичностью «Высокий». Срок устранения — 8 часов. Критичный (полная остановка) — 4 часа. Стандартный (ошибка без остановки) — 32 часа.' },
{ cat: 'SLA', q: 'Какой срок решения установлен для запроса на предоставление доступа к ИС для офисных сотрудников?', opts: ['8 часов', '16 часов', '24 часа', '32 часа'], ans: 2, exp: 'Согласно SLA (Подраздел 3.1), срок на предоставление/изменение доступа к информационным системам для офисных сотрудников составляет 24 часа.' },
{ cat: 'SLA', q: 'Сколько времени отводится на регистрацию инцидента с момента его поступления?', opts: ['5 минут', '10 минут', '15 минут', '30 минут'], ans: 2, exp: 'Время регистрации обращения — 15 минут с момента поступления запроса до момента присвоения ему уникального номера в единой заявочной системе.' },
{ cat: 'SLA', q: 'Какой срок согласования отводится на этап согласования РФО в процессе изменений?', opts: ['Немедленно', '1 рабочий день', '3 рабочих дня', '7 рабочих дней'], ans: 1, exp: 'По регламенту (Подраздел 3.3), срок согласования РФО — 1 рабочий день. Такой же срок установлен для Службы ИТ и Владельца ИС на первичное согласование.' },
// --- KPI (4) ---
{ cat: 'KPI', q: 'Каков целевой показатель доступности ИС согласно KPI?', opts: ['≥ 95%', '≥ 97%', '≥ 99%', '≥ 99,5%'], ans: 3, exp: 'Целевое значение KPI «Высокая доступность ИС» — ≥ 99,5%. Пороговое (минимально допустимое) — ≥ 99%. Доступность считается по формуле: (Д П) / Д × 100%.' },
{ cat: 'KPI', q: 'Что является пороговым значением KPI «Процент возвращённых заявок в работу» (инциденты)?', opts: ['≤ 0,3%', '≤ 0,5%', '≤ 1%', '≤ 2%'], ans: 2, exp: 'Целевое значение для этого KPI — ≤ 0,5%, пороговое — ≤ 1%. Это доля возвращённых инцидентов от общего числа решённых. Показатель влияет на оценку качества работы специалиста.' },
{ cat: 'KPI', q: 'Каков целевой показатель удовлетворённости заказчика по запросам на изменение ИС?', opts: ['≥ 90%', '≥ 95%', '≥ 97%', '≥ 98%'], ans: 1, exp: 'Целевое значение KPI «Уровень удовлетворённости заказчика (Запросы на изменение)» — ≥ 95%, пороговое — ≥ 90%. Это отличается от показателя по инцидентам, где целевое значение выше — ≥ 98%.' },
{ cat: 'KPI', q: 'Как используются оценки, выставленные сотрудниками после закрытия инцидентов?', opts: ['Только для статистики по отделу', 'Для расчёта KPI конкретных специалистов ИТ', 'Для определения стоимости следующего обслуживания', 'Публикуются в общем рейтинге ИТ-службы'], ans: 1, exp: 'Оценки, проставленные сотрудниками после подтверждения завершения работ, используются для оценки работы конкретных специалистов ИТ и участвуют в расчёте KPI ИТ-службы.' }
];
// ===================== STATE =====================
let completedModules = new Set();
let currentModule = null;
let currentSlide = 0;
let currentQuestion = 0;
let score = 0;
let answered = [];
// ===================== RENDER =====================
function renderMenu() {
const grid = document.getElementById('modules-grid');
grid.innerHTML = modules.map(m => `
<div class="module-card ${completedModules.has(m.id) ? 'done' : ''}" onclick="openModule(${m.id})">
<div class="module-header">
<div class="module-icon">${m.icon}</div>
<div>
<div class="module-title">${m.title}</div>
<div class="module-sub">${m.sub} · ${m.slides.length} слайдов</div>
</div>
</div>
<div class="module-desc">${m.desc}</div>
<div class="module-footer">
<div class="module-topics">${m.tags.map(t => `<span class="topic-tag">${t}</span>`).join('')}</div>
<div class="module-status">${completedModules.has(m.id) ? '✓ Изучено' : 'Открыть →'}</div>
</div>
</div>
`).join('') + `
<div class="module-card" onclick="startQuiz()" style="background:linear-gradient(135deg,rgba(0,149,255,0.08),rgba(0,212,170,0.08));border-color:rgba(0,212,170,0.3);">
<div class="module-header">
<div class="module-icon">🎯</div>
<div>
<div class="module-title">Итоговое тестирование</div>
<div class="module-sub">${questions.length} вопросов · Все разделы курса</div>
</div>
</div>
<div class="module-desc">Подтвердите знания прохождением теста по всем разделам курса. При результате ≥ 75% выдаётся сертификат.</div>
<div class="module-footer">
<div class="module-topics"><span class="topic-tag" style="color:var(--accent);border-color:rgba(0,212,170,0.4);background:rgba(0,212,170,0.1)">ТЕСТ</span></div>
<div class="module-status" style="color:var(--accent)">Пройти →</div>
</div>
</div>
`;
updateProgress();
}
function updateProgress() {
const pct = (completedModules.size / modules.length) * 100;
document.getElementById('main-progress').style.width = pct + '%';
document.getElementById('progress-text').textContent = `${completedModules.size} / ${modules.length}`;
}
function openModule(id) {
currentModule = modules[id];
currentSlide = 0;
document.getElementById('lesson-title').textContent = currentModule.title;
document.getElementById('lesson-sub').textContent = currentModule.sub;
renderSlides();
showScreen('screen-lesson');
}
function renderSlides() {
const host = document.getElementById('slides-host');
host.innerHTML = currentModule.slides.map((s, i) => `
<div class="slide ${i === 0 ? 'active' : ''}" id="slide-${i}">${s.html}</div>
`).join('');
const dots = document.getElementById('slide-dots');
dots.innerHTML = currentModule.slides.map((_, i) =>
`<div class="slide-dot ${i === 0 ? 'active' : ''}" onclick="jumpSlide(${i})"></div>`
).join('');
updateSlideControls();
}
function jumpSlide(idx) {
document.getElementById('slide-' + currentSlide).classList.remove('active');
document.querySelectorAll('.slide-dot')[currentSlide].classList.remove('active');
currentSlide = idx;
document.getElementById('slide-' + currentSlide).classList.add('active');
document.querySelectorAll('.slide-dot')[currentSlide].classList.add('active');
updateSlideControls();
}
function changeSlide(dir) {
const slides = currentModule.slides;
const prev = currentSlide;
currentSlide = Math.max(0, Math.min(slides.length - 1, currentSlide + dir));
if (prev !== currentSlide) {
document.getElementById('slide-' + prev).classList.remove('active');
document.querySelectorAll('.slide-dot')[prev].classList.remove('active');
document.getElementById('slide-' + currentSlide).classList.add('active');
document.querySelectorAll('.slide-dot')[currentSlide].classList.add('active');
}
updateSlideControls();
if (currentSlide === slides.length - 1) {
completedModules.add(currentModule.id);
renderMenu();
}
}
function updateSlideControls() {
const slides = currentModule.slides;
document.getElementById('btn-prev').disabled = currentSlide === 0;
const btn = document.getElementById('btn-next-slide');
if (currentSlide === slides.length - 1) {
btn.textContent = '✓ Завершить раздел';
btn.onclick = () => goMenu();
} else {
btn.textContent = 'Далее →';
btn.onclick = () => changeSlide(1);
}
}
// ===================== QUIZ =====================
function startQuiz() {
currentQuestion = 0;
score = 0;
answered = Array(questions.length).fill(null);
document.getElementById('quiz-score').textContent = '0';
document.getElementById('quiz-q-count').textContent = `1 / ${questions.length}`;
updateQuizProgress();
renderQuestion();
showScreen('screen-quiz');
}
function renderQuestion() {
const q = questions[currentQuestion];
const letters = ['A', 'B', 'C', 'D'];
document.getElementById('question-host').innerHTML = `
<div class="question-card">
<div class="q-meta">
<span class="q-num">Вопрос ${currentQuestion + 1} / ${questions.length}</span>
<span class="q-category">${q.cat}</span>
</div>
<div class="q-text">${q.q}</div>
<div class="options">
${q.opts.map((o, i) => `
<button class="option-btn" id="opt-${i}" onclick="selectAnswer(${i})">
<span class="option-letter">${letters[i]}</span>${o}
</button>
`).join('')}
</div>
<div class="explanation" id="explanation"></div>
</div>
`;
document.getElementById('btn-next-q').classList.remove('show');
}
function selectAnswer(idx) {
if (answered[currentQuestion] !== null) return;
answered[currentQuestion] = idx;
const q = questions[currentQuestion];
for (let i = 0; i < q.opts.length; i++) {
const btn = document.getElementById('opt-' + i);
btn.disabled = true;
if (i === q.ans) btn.classList.add('correct');
else if (i === idx && idx !== q.ans) btn.classList.add('wrong');
}
const exp = document.getElementById('explanation');
if (idx === q.ans) {
score++;
document.getElementById('quiz-score').textContent = score;
exp.className = 'explanation correct-exp show';
exp.innerHTML = '✅ ' + q.exp;
} else {
exp.className = 'explanation wrong-exp show';
exp.innerHTML = '❌ ' + q.exp;
}
const btn = document.getElementById('btn-next-q');
btn.classList.add('show');
btn.textContent = currentQuestion < questions.length - 1 ? 'Следующий вопрос →' : '📊 Посмотреть результаты';
}
function nextQuestion() {
if (currentQuestion < questions.length - 1) {
currentQuestion++;
updateQuizProgress();
renderQuestion();
} else {
showResults();
}
}
function updateQuizProgress() {
const pct = ((currentQuestion + 1) / questions.length) * 100;
document.getElementById('quiz-progress').style.width = pct + '%';
document.getElementById('quiz-q-count').textContent = `${currentQuestion + 1} / ${questions.length}`;
}
function showResults() {
const pct = Math.round((score / questions.length) * 100);
const wrong = questions.length - score;
document.getElementById('result-score').textContent = `${pct}%`;
document.getElementById('rb-correct').textContent = score;
document.getElementById('rb-wrong').textContent = wrong;
document.getElementById('rb-percent').textContent = pct + '%';
let trophy, title, desc;
if (pct >= 90) { trophy = '🏆'; title = 'Отличный результат!'; desc = 'Вы продемонстрировали отличное знание регламентов управления ИТ. Сертификат о прохождении курса сформирован.'; }
else if (pct >= 75) { trophy = '🎖'; title = 'Хороший результат!'; desc = 'Вы хорошо усвоили материал курса. Рекомендуем повторить темы с ошибками для закрепления знаний.'; }
else if (pct >= 60) { trophy = '📚'; title = 'Курс пройден'; desc = 'Базовые знания получены. Рекомендуем ещё раз изучить разделы курса перед повторным тестированием.'; }
else { trophy = '🔄'; title = 'Нужно повторить'; desc = 'Рекомендуем ещё раз пройти все обучающие материалы и затем повторить тестирование.'; }
document.getElementById('result-trophy').textContent = trophy;
document.getElementById('result-title').textContent = title;
document.getElementById('result-desc').textContent = desc;
const certHost = document.getElementById('cert-host');
if (pct >= 75) {
const today = new Date().toLocaleDateString('ru-RU', { day: '2-digit', month: 'long', year: 'numeric' });
const stars = pct >= 90 ? '★★★★★' : pct >= 80 ? '★★★★☆' : '★★★☆☆';
certHost.innerHTML = `
<div class="cert-card">
<h3>Сертификат о прохождении</h3>
<h2>Управление ИТ</h2>
<div class="cert-name">ИТ-специалист</div>
<div class="cert-stars">${stars}</div>
<div>Результат: <strong style="color:var(--accent)">${pct}%</strong> (${score} из ${questions.length})</div>
<div class="cert-date" style="margin-top:12px">${today}</div>
</div>
`;
} else { certHost.innerHTML = ''; }
sendResults(score, questions.length, pct, pct >= 75);
showScreen('screen-results');
}
function restartQuiz() { startQuiz(); }
function goMenu() {
renderMenu();
showScreen('screen-menu');
}
function showScreen(id) {
document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
document.getElementById(id).classList.add('active');
window.scrollTo({ top: 0, behavior: 'smooth' });
}
// ——— Связка с сервером: ФИО и результаты ———
const STORAGE_KEY_ID = 'lms_participant_id';
const STORAGE_KEY_FIO = 'lms_fio';
function getApiBase() {
const origin = window.location.origin;
const path = window.location.pathname;
const base = path.includes('/content/') ? path.replace(/\/content\/[^/]*$/, '') : '';
return origin + base;
}
function submitWelcome(e) {
e.preventDefault();
const input = document.getElementById('input-fio');
const errEl = document.getElementById('welcome-error');
const btn = document.getElementById('btn-welcome-start');
const fio = (input.value || '').trim();
if (!fio) {
errEl.textContent = 'Введите ФИО';
errEl.classList.add('show');
return false;
}
errEl.classList.remove('show');
btn.disabled = true;
btn.textContent = 'Загрузка…';
fetch(getApiBase() + '/api/start', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ fio: fio })
})
.then(r => r.json().then(data => ({ ok: r.ok, data })))
.then(({ ok, data }) => {
if (ok && data.participant_id) {
try {
sessionStorage.setItem(STORAGE_KEY_ID, data.participant_id);
sessionStorage.setItem(STORAGE_KEY_FIO, data.fio || fio);
} catch (e) {}
document.getElementById('user-fio').textContent = data.fio || fio;
document.getElementById('user-bar').style.display = 'flex';
showScreen('screen-menu');
renderMenu();
} else {
errEl.textContent = data.detail || 'Ошибка регистрации. Проверьте подключение.';
errEl.classList.add('show');
btn.disabled = false;
btn.textContent = 'Начать обучение';
}
})
.catch(() => {
errEl.textContent = 'Сервер недоступен. Проверьте подключение и обновите страницу.';
errEl.classList.add('show');
btn.disabled = false;
btn.textContent = 'Начать обучение';
});
return false;
}
function sendResults(score, totalQuestions, percent, passed) {
let participantId = null;
try {
participantId = sessionStorage.getItem(STORAGE_KEY_ID);
} catch (e) {}
if (!participantId) return;
fetch(getApiBase() + '/api/complete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
participant_id: participantId,
score: score,
total_questions: totalQuestions,
percent: percent,
passed: passed
})
}).catch(() => {});
}
// Инициализация: показать меню или экран ввода ФИО
function initScreens() {
let participantId = null;
let fio = null;
try {
participantId = sessionStorage.getItem(STORAGE_KEY_ID);
fio = sessionStorage.getItem(STORAGE_KEY_FIO);
} catch (e) {}
if (participantId && fio) {
document.getElementById('user-fio').textContent = fio;
document.getElementById('user-bar').style.display = 'flex';
showScreen('screen-menu');
renderMenu();
} else {
showScreen('screen-welcome');
}
}
renderMenu();
initScreens();
</script>
</body>
</html>