prombez-analytics/index.html

1591 lines
56 KiB
HTML
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.

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>ИИ-агент — Аналитика производственной безопасности</title>
<style>
:root {
--ink: #0F1218;
--blue: #2563EB;
--blue-50: #EFF6FF;
--blue-100: #DBEAFE;
--green: #059669;
--green-50: #ECFDF5;
--red: #DC2626;
--red-50: #FEF2F2;
--amber: #D97706;
--amber-50: #FFFBEB;
--white: #FFFFFF;
--gray-50: #F9FAFB;
--gray-100: #F2F4F7;
--gray-200: #E5E7EB;
--gray-500: #5B6573;
--gray-600: #4B5563;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font: 17px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;
color: var(--ink);
background: var(--white);
}
.container {
max-width: 1140px;
margin: 0 auto;
padding: 0 24px;
}
/* Hero */
.hero {
background: linear-gradient(135deg, #2563EB 0%, #1D4ED8 100%);
color: var(--white);
padding: 100px 0 80px;
}
.hero-badge {
display: inline-block;
background: rgba(255,255,255,0.15);
color: var(--white);
padding: 6px 16px;
border-radius: 100px;
font-size: 14px;
font-weight: 600;
margin-bottom: 24px;
backdrop-filter: blur(10px);
}
.hero h1 {
font-size: 48px;
font-weight: 800;
line-height: 1.1;
max-width: 700px;
margin-bottom: 20px;
}
.hero p {
font-size: 19px;
color: rgba(255,255,255,0.8);
max-width: 560px;
margin-bottom: 36px;
line-height: 1.6;
}
.hero-actions {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 14px 28px;
border-radius: 8px;
font-weight: 700;
font-size: 15px;
text-decoration: none;
transition: transform 0.15s, box-shadow 0.15s;
cursor: pointer;
border: none;
font-family: inherit;
}
.btn:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.btn-primary {
background: var(--white);
color: #1D4ED8;
}
.btn-outline {
background: transparent;
color: var(--white);
border: 2px solid rgba(255,255,255,0.4);
}
.btn-outline:hover {
border-color: var(--white);
}
.btn-blue {
background: var(--blue);
color: var(--white);
}
.btn-blue:hover {
background: #1D4ED8;
}
.btn-sm {
padding: 8px 18px;
font-size: 13px;
font-weight: 600;
}
/* Section */
.section {
padding: 80px 0;
}
.section-label {
display: inline-block;
background: var(--blue-50);
color: var(--blue);
padding: 4px 14px;
border-radius: 100px;
font-size: 13px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 16px;
}
.section h2 {
font-size: 36px;
font-weight: 700;
margin-bottom: 12px;
line-height: 1.2;
}
.section-subtitle {
font-size: 17px;
color: var(--gray-500);
max-width: 600px;
margin-bottom: 40px;
}
/* Quick Links */
.quick-links {
background: var(--gray-50);
}
.quick-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.quick-card {
background: var(--white);
border: 1px solid var(--gray-200);
border-radius: 12px;
padding: 28px;
text-decoration: none;
color: var(--ink);
transition: border-color 0.2s, box-shadow 0.2s;
display: flex;
align-items: flex-start;
gap: 16px;
}
.quick-card:hover {
border-color: var(--blue);
box-shadow: 0 4px 16px rgba(37,99,235,0.08);
}
.quick-card-icon {
font-size: 28px;
flex-shrink: 0;
width: 48px;
height: 48px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
}
.quick-card-icon.blue { background: var(--blue-50); }
.quick-card-icon.green { background: var(--green-50); }
.quick-card-icon.red { background: var(--red-50); }
.quick-card h3 {
font-size: 17px;
font-weight: 700;
margin-bottom: 4px;
}
.quick-card p {
font-size: 14px;
color: var(--gray-500);
}
/* Blocks */
.blocks-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 20px;
}
.block-card {
background: var(--white);
border: 1px solid var(--gray-200);
border-radius: 12px;
padding: 32px;
transition: border-color 0.2s;
}
.block-card:hover {
border-color: var(--blue-100);
}
.block-number {
font-size: 13px;
font-weight: 700;
color: var(--blue);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 12px;
}
.block-card h3 {
font-size: 20px;
font-weight: 700;
margin-bottom: 12px;
}
.block-card ul {
list-style: none;
}
.block-card li {
font-size: 15px;
color: var(--gray-600);
padding: 6px 0;
padding-left: 20px;
position: relative;
}
.block-card li::before {
content: '';
position: absolute;
left: 0;
top: 13px;
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--blue);
}
/* Steps */
.steps {
display: grid;
gap: 16px;
counter-reset: step;
}
.step {
display: flex;
gap: 20px;
align-items: flex-start;
}
.step-num {
width: 44px;
height: 44px;
background: var(--blue);
color: var(--white);
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-weight: 800;
font-size: 18px;
flex-shrink: 0;
counter-increment: step;
}
.step-num::before {
content: counter(step);
}
.step-text h3 {
font-size: 17px;
font-weight: 700;
margin-bottom: 4px;
}
.step-text p {
font-size: 15px;
color: var(--gray-500);
}
/* Categories */
.cat-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 12px;
}
.cat-tag {
background: var(--gray-50);
border: 1px solid var(--gray-200);
border-radius: 8px;
padding: 12px 16px;
font-size: 14px;
font-weight: 600;
display: flex;
align-items: center;
gap: 8px;
}
.cat-dot {
width: 10px;
height: 10px;
border-radius: 50%;
flex-shrink: 0;
}
.cat-dot.d1 { background: #2563EB; }
.cat-dot.d2 { background: #7C3AED; }
.cat-dot.d3 { background: #DC2626; }
.cat-dot.d4 { background: #EA580C; }
.cat-dot.d5 { background: #D97706; }
.cat-dot.d6 { background: #059669; }
.cat-dot.d7 { background: #0891B2; }
.cat-dot.d8 { background: #4F46E5; }
.cat-dot.d9 { background: #DB2777; }
/* Dashboard Preview */
.dash-preview {
background: var(--gray-50);
border: 1px solid var(--gray-200);
border-radius: 16px;
padding: 40px;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 24px;
}
.dash-metric {
text-align: center;
padding: 20px;
background: var(--white);
border-radius: 10px;
}
.dash-metric .value {
font-size: 36px;
font-weight: 800;
color: var(--blue);
line-height: 1;
margin-bottom: 6px;
}
.dash-metric .label {
font-size: 14px;
color: var(--gray-500);
}
.dash-chart {
grid-column: 1 / -1;
background: var(--white);
border-radius: 10px;
padding: 24px;
min-height: 200px;
display: flex;
align-items: center;
justify-content: center;
}
.chart-bars {
display: flex;
align-items: flex-end;
gap: 16px;
height: 160px;
}
.chart-bar-wrapper {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.chart-bar {
width: 40px;
border-radius: 6px 6px 0 0;
transition: height 0.3s;
}
.chart-bar-label {
font-size: 11px;
color: var(--gray-500);
font-weight: 600;
max-width: 60px;
text-align: center;
line-height: 1.2;
}
/* CTA */
.cta {
background: linear-gradient(135deg, #1D4ED8 0%, #1E40AF 100%);
color: var(--white);
text-align: center;
padding: 80px 0;
}
.cta h2 {
font-size: 32px;
font-weight: 700;
margin-bottom: 12px;
}
.cta p {
color: rgba(255,255,255,0.7);
margin-bottom: 28px;
max-width: 480px;
margin-left: auto;
margin-right: auto;
}
.cta .btn {
background: var(--white);
color: #1D4ED8;
}
/* Footer */
.footer {
background: var(--gray-50);
border-top: 1px solid var(--gray-200);
padding: 32px 0;
text-align: center;
}
.footer p {
font-size: 14px;
color: var(--gray-500);
}
/* ===================================== */
/* ANALYZER */
/* ===================================== */
.analyzer {
background: var(--gray-50);
}
.upload-area {
background: var(--white);
border: 2px dashed var(--gray-200);
border-radius: 16px;
padding: 48px 24px;
text-align: center;
transition: border-color 0.2s, background 0.2s;
cursor: pointer;
margin-bottom: 16px;
}
.upload-area:hover,
.upload-area.drag-over {
border-color: var(--blue);
background: var(--blue-50);
}
.upload-area .upload-icon {
font-size: 40px;
margin-bottom: 12px;
}
.upload-area p {
color: var(--gray-500);
font-size: 15px;
}
.upload-area .upload-hint {
font-size: 13px;
color: var(--gray-500);
margin-top: 4px;
}
.upload-actions {
display: flex;
gap: 12px;
flex-wrap: wrap;
align-items: center;
margin-bottom: 8px;
}
.upload-status {
font-size: 14px;
color: var(--gray-500);
margin-left: 12px;
}
/* Results */
#results {
display: none;
}
#results.active {
display: block;
}
.result-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 16px;
margin-bottom: 32px;
}
.result-metric {
background: var(--white);
border: 1px solid var(--gray-200);
border-radius: 12px;
padding: 24px;
text-align: center;
}
.result-metric .value {
font-size: 32px;
font-weight: 800;
color: var(--blue);
line-height: 1;
margin-bottom: 6px;
}
.result-metric .label {
font-size: 13px;
color: var(--gray-500);
line-height: 1.3;
}
.result-metric.warn .value { color: var(--red); }
.result-metric.good .value { color: var(--green); }
/* Charts */
.result-charts {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
margin-bottom: 32px;
}
.result-chart-card {
background: var(--white);
border: 1px solid var(--gray-200);
border-radius: 12px;
padding: 28px;
}
.result-chart-card h3 {
font-size: 17px;
font-weight: 700;
margin-bottom: 20px;
}
.result-chart-card.full {
grid-column: 1 / -1;
}
/* Bar chart list */
.bar-list-item {
margin-bottom: 14px;
}
.bar-list-header {
display: flex;
justify-content: space-between;
font-size: 14px;
margin-bottom: 4px;
}
.bar-list-header .name {
font-weight: 600;
}
.bar-list-header .count {
color: var(--gray-500);
}
.bar-list-track {
height: 8px;
background: var(--gray-100);
border-radius: 4px;
overflow: hidden;
}
.bar-list-fill {
height: 100%;
border-radius: 4px;
background: var(--blue);
transition: width 0.4s ease;
}
.bar-list-fill.warn { background: var(--red); }
.bar-list-fill.good { background: var(--green); }
/* Table */
.result-table-wrap {
overflow-x: auto;
margin-bottom: 32px;
}
.result-table {
width: 100%;
border-collapse: collapse;
font-size: 14px;
background: var(--white);
border-radius: 12px;
overflow: hidden;
border: 1px solid var(--gray-200);
}
.result-table th {
background: var(--gray-50);
padding: 12px 16px;
text-align: left;
font-weight: 700;
font-size: 13px;
color: var(--gray-500);
text-transform: uppercase;
letter-spacing: 0.3px;
white-space: nowrap;
}
.result-table td {
padding: 10px 16px;
border-top: 1px solid var(--gray-100);
}
.result-table tbody tr:hover {
background: var(--gray-50);
}
.section-tabs {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-bottom: 32px;
margin-top: 40px;
}
.tab-btn {
padding: 10px 20px;
border: 1px solid var(--gray-200);
border-radius: 8px;
background: var(--white);
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.15s;
color: var(--ink);
font-family: inherit;
}
.tab-btn:hover {
border-color: var(--blue);
color: var(--blue);
}
.tab-btn.active {
background: var(--blue);
color: var(--white);
border-color: var(--blue);
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
/* Conclusions */
.conclusion-box {
background: var(--blue-50);
border: 1px solid var(--blue-100);
border-radius: 12px;
padding: 24px;
margin-top: 32px;
}
.conclusion-box.warn-box {
background: var(--red-50);
border-color: #FECACA;
}
.conclusion-box h4 {
font-size: 16px;
font-weight: 700;
margin-bottom: 12px;
}
.conclusion-box p {
font-size: 15px;
color: var(--gray-600);
line-height: 1.7;
}
/* Mobile */
@media (max-width: 640px) {
.hero { padding: 64px 0 48px; }
.hero h1 { font-size: 32px; }
.hero p { font-size: 16px; }
.section { padding: 48px 0; }
.section h2 { font-size: 26px; }
.blocks-grid { grid-template-columns: 1fr; }
.dash-preview { padding: 24px; }
.dash-metric .value { font-size: 28px; }
.result-charts { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<!-- Hero -->
<header class="hero">
<div class="container">
<span class="hero-badge">AI Analytics</span>
<h1>ИИ-агент аналитики производственной безопасности</h1>
<p>Автоматический сбор, анализ и визуализация результатов внутренних проверок. Контроль выполнения плана и формирование управленческих отчётов.</p>
<div class="hero-actions">
<a href="#analyzer" class="btn btn-primary">Загрузить данные</a>
<a href="#quick" class="btn btn-outline">Инструменты</a>
<a href="#blocks" class="btn btn-outline">Возможности</a>
</div>
</div>
</header>
<!-- Quick Links -->
<section id="quick" class="section quick-links">
<div class="container">
<span class="section-label">Инструменты</span>
<h2>Быстрый доступ</h2>
<p class="section-subtitle">Основные рабочие инструменты команды</p>
<div class="quick-grid">
<a href="#" class="quick-card">
<span class="quick-card-icon blue">📋</span>
<div>
<h3>Google Forms — проверки</h3>
<p>Заполнить форму по результатам проверки</p>
</div>
</a>
<a href="#" class="quick-card">
<span class="quick-card-icon green">📊</span>
<div>
<h3>Google Sheets — данные</h3>
<p>Просмотр и выгрузка всех записей проверок</p>
</div>
</a>
<a href="#" class="quick-card">
<span class="quick-card-icon green">📅</span>
<div>
<h3>План проверок</h3>
<p>Таблица плановых показателей по подразделениям</p>
</div>
</a>
<a href="#analyzer" class="quick-card">
<span class="quick-card-icon blue">🤖</span>
<div>
<h3>Анализатор CSV</h3>
<p>Загрузите выгрузку из Google Sheets — получите аналитику</p>
</div>
</a>
<a href="#" class="quick-card">
<span class="quick-card-icon green">📄</span>
<div>
<h3>Месячный отчёт</h3>
<p>Автоматическая аналитическая справка (PDF)</p>
</div>
</a>
<a href="#" class="quick-card">
<span class="quick-card-icon red">📖</span>
<div>
<h3>Инструкция</h3>
<p>Руководство по работе с системой</p>
</div>
</a>
</div>
</div>
</section>
<!-- Functional Blocks -->
<section id="blocks" class="section">
<div class="container">
<span class="section-label">Функционал</span>
<h2>6 аналитических блоков</h2>
<p class="section-subtitle">Что умеет ИИ-агент и какую информацию выдаёт автоматически</p>
<div class="blocks-grid">
<div class="block-card">
<span class="block-number">Блок №1</span>
<h3>Анализ нарушений</h3>
<ul>
<li>Общее количество выявленных нарушений</li>
<li>Количество по каждому виду нарушений</li>
<li>Доля каждого вида</li>
<li>Распределение по подразделениям и регионам</li>
<li>Рейтинг подразделений (лучшие / худшие)</li>
<li>Рейтинг видов нарушений</li>
</ul>
</div>
<div class="block-card">
<span class="block-number">Блок №2</span>
<h3>Анализ динамики</h3>
<ul>
<li>Сравнение: текущий месяц vs предыдущий</li>
<li>Сравнение с аналогичным периодом прошлого года</li>
<li>Процент роста / снижения</li>
<li>Тренды по каждому виду нарушений</li>
<li>Динамика по подразделениям</li>
</ul>
</div>
<div class="block-card">
<span class="block-number">Блок №3</span>
<h3>Проблемные зоны</h3>
<ul>
<li>Повторяющиеся нарушения</li>
<li>Подразделения с устойчиво высоким уровнем</li>
<li>Ухудшение показателей</li>
<li>Наиболее частые нарушения</li>
<li>Автоматические выводы — «проблемное направление X, оно даёт 27% нарушений»</li>
</ul>
</div>
<div class="block-card">
<span class="block-number">Блок №4</span>
<h3>Контроль плана проверок</h3>
<ul>
<li>Выполнение (%) = Факт / План × 100</li>
<li>Рейтинг подразделений по выполнению</li>
<li>Перечень с низким исполнением</li>
<li>Анализ по регионам</li>
<li>Перевыполнение / невыполнение</li>
</ul>
</div>
<div class="block-card">
<span class="block-number">Блок №5</span>
<h3>Аналитическая справка</h3>
<ul>
<li>Общая информация за месяц</li>
<li>Количество проверок и выполнение плана</li>
<li>ТОП-3 распространённых нарушений</li>
<li>Подразделения-лидеры и аутсайдеры</li>
<li>Тенденции месяца</li>
</ul>
</div>
<div class="block-card">
<span class="block-number">Блок №6</span>
<h3>Рекомендации</h3>
<ul>
<li>Внеплановые проверки по проблемным зонам</li>
<li>Целевые инструктажи</li>
<li>Дополнительное обучение</li>
<li>Выборочный аудит документации</li>
<li>Усиление контроля</li>
</ul>
</div>
</div>
</div>
</section>
<!-- ========================================= -->
<!-- ANALYZER -->
<!-- ========================================= -->
<section id="analyzer" class="section analyzer">
<div class="container">
<span class="section-label">Анализатор</span>
<h2>Загрузите данные из Google Sheets</h2>
<p class="section-subtitle">Экспортируйте таблицу в CSV (Файл → Скачать → CSV) и загрузите сюда. Анализатор сам определит категории, построит рейтинги и покажет выводы.</p>
<div class="upload-area" id="dropArea">
<div class="upload-icon">📂</div>
<p><strong>Перетащите CSV-файл сюда</strong> или нажмите для выбора</p>
<p class="upload-hint">Поддерживаются файлы .csv из Google Sheets (кодировка UTF-8)</p>
<input type="file" id="fileInput" accept=".csv" style="display:none">
</div>
<div class="upload-actions">
<button class="btn btn-blue btn-sm" id="sampleBtn">Загрузить демо-данные</button>
<span class="upload-status" id="uploadStatus"></span>
</div>
<!-- Results -->
<div id="results">
<div class="sort-info" style="font-size:14px;color:var(--gray-500);margin-bottom:24px" id="dataInfo"></div>
<!-- Key Metrics -->
<div class="result-grid" id="metricsGrid"></div>
<!-- Tabs -->
<div class="section-tabs">
<button class="tab-btn active" data-tab="tab-categories">По категориям</button>
<button class="tab-btn" data-tab="tab-divisions">По подразделениям</button>
<button class="tab-btn" data-tab="tab-regions">По регионам</button>
<button class="tab-btn" data-tab="tab-inspectors">По проверяющим</button>
<button class="tab-btn" data-tab="tab-repeat">Повторяющиеся</button>
<button class="tab-btn" data-tab="tab-table">Все записи</button>
</div>
<div class="tab-content active" id="tab-categories"></div>
<div class="tab-content" id="tab-divisions"></div>
<div class="tab-content" id="tab-regions"></div>
<div class="tab-content" id="tab-inspectors"></div>
<div class="tab-content" id="tab-repeat"></div>
<div class="tab-content" id="tab-table"></div>
<!-- Auto conclusion -->
<div id="conclusions"></div>
</div>
</div>
</section>
<!-- Dashboard Preview -->
<section id="dashboard-preview" class="section" style="background: var(--gray-50);">
<div class="container">
<span class="section-label">Дашборд</span>
<h2>Визуализация данных</h2>
<p class="section-subtitle">Пример того, как выглядит аналитическая панель ИИ-агента</p>
<div class="dash-preview">
<div class="dash-metric">
<div class="value">247</div>
<div class="label">Нарушений за месяц</div>
</div>
<div class="dash-metric">
<div class="value">87%</div>
<div class="label">Выполнение плана</div>
</div>
<div class="dash-metric">
<div class="value">142</div>
<div class="label">Проведено проверок</div>
</div>
<div class="dash-metric">
<div class="value">32</div>
<div class="label">Устранено за месяц</div>
</div>
<div class="dash-chart">
<div class="chart-bars">
<div class="chart-bar-wrapper">
<div class="chart-bar" style="height: 120px; background: #2563EB;"></div>
<span class="chart-bar-label">Янв</span>
</div>
<div class="chart-bar-wrapper">
<div class="chart-bar" style="height: 90px; background: #2563EB;"></div>
<span class="chart-bar-label">Фев</span>
</div>
<div class="chart-bar-wrapper">
<div class="chart-bar" style="height: 140px; background: #2563EB;"></div>
<span class="chart-bar-label">Мар</span>
</div>
<div class="chart-bar-wrapper">
<div class="chart-bar" style="height: 100px; background: #2563EB;"></div>
<span class="chart-bar-label">Апр</span>
</div>
<div class="chart-bar-wrapper">
<div class="chart-bar" style="height: 160px; background: #DC2626;"></div>
<span class="chart-bar-label">Май</span>
</div>
<div class="chart-bar-wrapper">
<div class="chart-bar" style="height: 130px; background: #2563EB;"></div>
<span class="chart-bar-label">Июн</span>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- How to work -->
<section id="steps" class="section">
<div class="container">
<span class="section-label">Процесс</span>
<h2>Как работать с системой</h2>
<p class="section-subtitle">4 шага от проверки до управленческого отчёта</p>
<div class="steps">
<div class="step">
<div class="step-num"></div>
<div class="step-text">
<h3>Заполните Google Forms после проверки</h3>
<p>Проверяющий вносит данные: дата, подразделение, регион, вид и количество нарушений, описание. Данные автоматически попадают в Google Sheets.</p>
</div>
</div>
<div class="step">
<div class="step-num"></div>
<div class="step-text">
<h3>Выгрузите CSV и загрузите в анализатор</h3>
<p>В Google Sheets: Файл → Скачать → CSV. Перетащите файл на эту страницу — анализатор сам разберёт данные.</p>
</div>
</div>
<div class="step">
<div class="step-num"></div>
<div class="step-text">
<h3>Смотрите аналитику</h3>
<p>Рейтинги категорий, подразделений и регионов. Графики, проблемные зоны, повторяющиеся нарушения — всё считается автоматически.</p>
</div>
</div>
<div class="step">
<div class="step-num"></div>
<div class="step-text">
<h3>Получайте выводы и рекомендации</h3>
<p>Анализатор формирует текстовые выводы с цифрами и конкретными предложениями по улучшению ситуации.</p>
</div>
</div>
</div>
</div>
</section>
<!-- Categories -->
<section class="section" style="background: var(--gray-50);">
<div class="container">
<span class="section-label">Классификация</span>
<h2>9 категорий нарушений</h2>
<p class="section-subtitle">Анализатор автоматически распределяет нарушения по категориям</p>
<div class="cat-grid">
<span class="cat-tag"><span class="cat-dot d1"></span> Документация по БиОТ</span>
<span class="cat-tag"><span class="cat-dot d2"></span> Наряды-допуски</span>
<span class="cat-tag"><span class="cat-dot d3"></span> Пожарная безопасность</span>
<span class="cat-tag"><span class="cat-dot d4"></span> Транспортная безопасность</span>
<span class="cat-tag"><span class="cat-dot d5"></span> Промышленная безопасность</span>
<span class="cat-tag"><span class="cat-dot d6"></span> Санитарно-бытовые требования</span>
<span class="cat-tag"><span class="cat-dot d7"></span> Средства индивидуальной защиты</span>
<span class="cat-tag"><span class="cat-dot d8"></span> Обучение и проверка знаний</span>
<span class="cat-tag"><span class="cat-dot d9"></span> Электробезопасность</span>
</div>
</div>
</section>
<!-- CTA -->
<section class="cta">
<div class="container">
<h2>Начните использовать анализатор</h2>
<p>Выгрузите данные из Google Sheets в CSV — и получите полную аналитику за минуту. Все вопросы — в рабочий чат команды.</p>
<a href="#analyzer" class="btn">Загрузить данные</a>
</div>
</section>
<!-- Footer -->
<footer class="footer">
<div class="container">
<p>ИИ-агент аналитики производственной безопасности &middot; Внутренний портал команды &middot; 2026</p>
</div>
</footer>
<!-- ========================================= -->
<!-- JAVASCRIPT: CSV Analyzer -->
<!-- ========================================= -->
<script>
(function() {
'use strict';
var CATEGORIES = [
'Документация по БиОТ',
'Наряды-допуски',
'Пожарная безопасность',
'Транспортная безопасность',
'Промышленная безопасность',
'Санитарно-бытовые требования',
'Средства индивидуальной защиты',
'Обучение и проверка знаний',
'Электробезопасность',
'Организационные требования БиОТ',
'Производственная санитария'
];
var CAT_SHORT = {
'Документация по БиОТ': 'Документация БиОТ',
'Наряды-допуски': 'Наряды-допуски',
'Пожарная безопасность': 'Пожарная безопасность',
'Транспортная безопасность': 'Транспортная безопасность',
'Промышленная безопасность': 'Промышленная безопасность',
'Санитарно-бытовые требования': 'Санитарно-бытовые',
'Средства индивидуальной защиты': 'СИЗ',
'Обучение и проверка знаний': 'Обучение',
'Электробезопасность': 'Электробезопасность',
'Организационные требования БиОТ': 'Орг. требования',
'Производственная санитария': 'Произв. санитария',
'Без категории': 'Без категории'
};
function findColumn(headers, keywords) {
var h = headers.map(function(hdr) { return hdr.toLowerCase().trim(); });
for (var i = 0; i < h.length; i++) {
for (var k = 0; k < keywords.length; k++) {
if (h[i].indexOf(keywords[k].toLowerCase()) !== -1) return i;
}
}
return -1;
}
function parseCSV(text) {
var rows = [];
var row = [];
var cell = '';
var quoted = false;
for (var i = 0; i < text.length; i++) {
var ch = text[i];
if (quoted) {
if (ch === '"') {
if (i + 1 < text.length && text[i + 1] === '"') { cell += '"'; i++; }
else quoted = false;
} else { cell += ch; }
} else {
if (ch === '"') { quoted = true; }
else if (ch === ',' || ch === ';' || ch === '\t') {
row.push(cell.trim()); cell = '';
} else if (ch === '\n' || ch === '\r') {
if (ch === '\r' && i + 1 < text.length && text[i + 1] === '\n') i++;
row.push(cell.trim());
if (row.length > 0 && (row.length > 1 || row[0])) rows.push(row);
row = []; cell = '';
} else { cell += ch; }
}
}
row.push(cell.trim());
if (row.length > 0 && (row.length > 1 || row[0])) rows.push(row);
return rows;
}
function normalizeCategory(raw) {
if (!raw) return '';
var s = raw.toLowerCase().trim();
if (s === 'сиз' || s === 'не обеспечение сиз' || s.indexOf('сиз') !== -1) return 'Средства индивидуальной защиты';
if (s.indexOf('пожарн') !== -1 || s.indexOf('пожар') !== -1 || s.indexOf('өрт') !== -1) return 'Пожарная безопасность';
if (s.indexOf('электро') !== -1) return 'Электробезопасность';
if (s.indexOf('транспорт') !== -1 || s.indexOf('тсс') !== -1) return 'Транспортная безопасность';
if (s.indexOf('промышленн') !== -1 || s.indexOf('помышленн') !== -1 || s.indexOf('өнеркәсіп') !== -1) return 'Промышленная безопасность';
if (s.indexOf('наряд') !== -1 || s.indexOf('допуск') !== -1) return 'Наряды-допуски';
if (s.indexOf('санитар') !== -1 || s.indexOf('бытов') !== -1 || s.indexOf('гигиенич') !== -1 || s.indexOf('санитари') !== -1) return 'Санитарно-бытовые требования';
if (s.indexOf('производственн') !== -1 && s.indexOf('санитар') !== -1) return 'Производственная санитария';
if (s.indexOf('обучен') !== -1 || s.indexOf('знаний') !== -1 || s.indexOf('оқыту') !== -1 || s.indexOf('нұсқау') !== -1) return 'Обучение и проверка знаний';
if (s.indexOf('4-х ступенчат') !== -1 || s.indexOf('четырехступенчат') !== -1 || s.indexOf('организационн') !== -1) return 'Организационные требования БиОТ';
if (s.indexOf('биот') !== -1 || s.indexOf('документаци') !== -1 || s.indexOf('док-ты') !== -1 || s.indexOf('документ') !== -1) return 'Документация по БиОТ';
if (s.indexOf('инструктаж') !== -1 || s.indexOf('инструкци') !== -1) return 'Документация по БиОТ';
if (s.length > 3) return raw.trim();
return '';
}
function isFixedStatus(val) {
var s = (val || '').toLowerCase().trim();
if (s === 'исполнено' || s === 'орындалды' || s === 'устранено' || s === 'выполнено') return true;
if (s.indexOf('исполн') !== -1) return true;
return false;
}
function analyzeData(rows) {
if (rows.length < 2) return null;
var headers = rows[0];
var data = rows.slice(1);
var colDate = findColumn(headers, ['дата']);
var colDept = findColumn(headers, ['филиал','подразделение','структур']);
var colRegion = findColumn(headers, ['область','регион']);
var colCity = findColumn(headers, ['город','район','населен']);
var colAddr = findColumn(headers, ['объект','адрес']);
var colCategory = findColumn(headers, ['категория','нарушен']);
var colInspector = findColumn(headers, ['выдал','кто выдал','проверяющий']);
var colStatus = findColumn(headers, ['исполнено','статус','устран']);
var colDeadline = findColumn(headers, ['срок']);
var colDescCat2 = -1;
// Second category column detection: find second match
for (var hi = 0; hi < headers.length; hi++) {
var hdr = headers[hi].toLowerCase().trim();
if (hdr.indexOf('категори') !== -1 && hi !== colCategory) { colDescCat2 = hi; break; }
if (hdr.indexOf('описан') !== -1 || hdr.indexOf('суть') !== -1) { colDescCat2 = hi; break; }
}
var colDesc = colDescCat2 >= 0 ? colDescCat2 : (colCategory >= 0 ? colCategory + 1 : -1);
var records = [];
for (var i = 0; i < data.length; i++) {
var r = data[i];
if (r.length < 2) continue;
var catRaw = colCategory >= 0 ? (r[colCategory] || '') : '';
var catNorm = normalizeCategory(catRaw);
var descRaw = colDesc >= 0 && colDesc < r.length ? (r[colDesc] || '') : '';
if (!catNorm && descRaw) catNorm = normalizeCategory(descRaw);
if (!catNorm && catRaw) catNorm = catRaw;
var statusRaw = colStatus >= 0 ? (r[colStatus] || '') : '';
var fixed = isFixedStatus(statusRaw);
records.push({
date: colDate >= 0 ? (r[colDate] || '') : '',
dept: colDept >= 0 ? (r[colDept] || 'Не указано') : 'Не указано',
region: colRegion >= 0 ? (r[colRegion] || 'Не указано') : 'Не указано',
city: colCity >= 0 ? (r[colCity] || '') : '',
addr: colAddr >= 0 ? (r[colAddr] || '') : '',
inspector: colInspector >= 0 ? (r[colInspector] || 'Не указано') : 'Не указано',
count: 1,
category: catNorm || 'Без категории',
desc: descRaw,
status: statusRaw,
fixed: fixed,
deadline: colDeadline >= 0 ? (r[colDeadline] || '') : '',
raw: r
});
}
if (records.length === 0) return null;
var totalRecords = records.length;
var totalFixed = records.filter(function(r) { return r.fixed; }).length;
var totalPending = totalRecords - totalFixed;
var catStats = {};
records.forEach(function(r) {
if (!catStats[r.category]) catStats[r.category] = 0;
catStats[r.category] += r.count;
});
var catSorted = Object.entries(catStats).sort(function(a, b) { return b[1] - a[1]; });
var deptStats = {};
records.forEach(function(r) {
if (!deptStats[r.dept]) deptStats[r.dept] = 0;
deptStats[r.dept] += r.count;
});
var deptSorted = Object.entries(deptStats).sort(function(a, b) { return b[1] - a[1]; });
var regionStats = {};
records.forEach(function(r) {
if (!regionStats[r.region]) regionStats[r.region] = 0;
regionStats[r.region] += r.count;
});
var regionSorted = Object.entries(regionStats).sort(function(a, b) { return b[1] - a[1]; });
var inspStats = {};
records.forEach(function(r) {
if (!inspStats[r.inspector]) inspStats[r.inspector] = 0;
inspStats[r.inspector] += r.count;
});
var inspSorted = Object.entries(inspStats).sort(function(a, b) { return b[1] - a[1]; });
var descStats = {};
records.forEach(function(r) {
var key = (r.desc || r.category || '').trim().toLowerCase();
if (!key || key.length < 3) key = r.category.toLowerCase() + '_' + (Math.random() + '').slice(2, 6);
if (!descStats[key]) descStats[key] = { count: 0, desc: r.desc || r.category, cat: r.category };
descStats[key].count += r.count;
});
var repeatSorted = Object.entries(descStats)
.filter(function(e) { return e[1].count > 1; })
.sort(function(a, b) { return b[1].count - a[1].count; });
var topCat = catSorted.length > 0 ? catSorted[0] : null;
return {
records: records, totalRecords: totalRecords,
totalFixed: totalFixed, totalPending: totalPending,
catSorted: catSorted, deptSorted: deptSorted,
regionSorted: regionSorted, inspSorted: inspSorted,
repeatSorted: repeatSorted, topCat: topCat,
headers: headers
};
}
function escHtml(s) {
return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
function buildMetric(value, label, cls) {
return '<div class="result-metric' + (cls ? ' ' + cls : '') + '">' +
'<div class="value">' + value + '</div><div class="label">' + label + '</div></div>';
}
function renderResults(data) {
var results = document.getElementById('results');
results.classList.add('active');
document.getElementById('dataInfo').textContent =
'Загружено: ' + data.totalRecords + ' записей. Устранено: ' + data.totalFixed +
' (' + Math.round(data.totalFixed / data.totalRecords * 100) + '%). На контроле: ' + data.totalPending + '.';
var metricsHTML = '';
metricsHTML += buildMetric(data.totalRecords, 'Всего записей');
metricsHTML += buildMetric(data.totalFixed, 'Устранено', 'good');
metricsHTML += buildMetric(data.totalPending, 'На контроле', data.totalPending > data.totalFixed * 2 ? 'warn' : '');
metricsHTML += buildMetric(data.catSorted.length, 'Категорий нарушений');
if (data.topCat) {
metricsHTML += buildMetric(
Math.round(data.topCat[1] / data.totalRecords * 100) + '%',
'Доля «' + (CAT_SHORT[data.topCat[0]] || data.topCat[0]) + '»',
data.topCat[1] / data.totalRecords > 0.25 ? 'warn' : ''
);
}
document.getElementById('metricsGrid').innerHTML = metricsHTML;
renderCategoriesTab(data);
renderDivisionsTab(data);
renderRegionsTab(data);
renderInspectorsTab(data);
renderRepeatTab(data);
renderTableTab(data);
renderConclusions(data);
document.querySelectorAll('.tab-btn').forEach(function(b) { b.classList.remove('active'); });
document.querySelector('[data-tab="tab-categories"]').classList.add('active');
document.querySelectorAll('.tab-content').forEach(function(c) { c.classList.remove('active'); });
document.getElementById('tab-categories').classList.add('active');
results.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
function renderBarList(containerId, items, total, maxBars) {
maxBars = maxBars || 15;
var container = document.getElementById(containerId);
var titles = {
'tab-categories': 'Распределение по категориям нарушений',
'tab-divisions': 'Рейтинг филиалов',
'tab-regions': 'Рейтинг областей',
'tab-inspectors': 'По проверяющим',
'tab-repeat': 'Повторяющиеся нарушения'
};
var html = '<div class="result-chart-card full"><h3>' + (titles[containerId] || '') + '</h3>';
var shown = items.slice(0, maxBars);
var maxVal = shown.length > 0 ? shown[0][1] : 1;
shown.forEach(function(item, idx) {
var pct = total > 0 ? Math.round(item[1] / total * 100) : 0;
var barPct = maxVal > 0 ? Math.round(item[1] / maxVal * 100) : 0;
var isWarn = containerId === 'tab-categories' && idx === 0 && pct > 20;
html += '<div class="bar-list-item"><div class="bar-list-header"><span class="name">' +
escHtml(item[0]) + '</span><span class="count">' + item[1] + ' (' + pct + '%)</span></div>' +
'<div class="bar-list-track"><div class="bar-list-fill' + (isWarn ? ' warn' : '') +
'" style="width:' + barPct + '%"></div></div></div>';
});
if (items.length > maxBars) {
html += '<p style="font-size:13px;color:var(--gray-500);margin-top:12px">Показаны первые ' + maxBars + ' из ' + items.length + '</p>';
}
html += '</div>';
container.innerHTML = html;
}
function renderCategoriesTab(data) { renderBarList('tab-categories', data.catSorted, data.totalRecords, 15); }
function renderDivisionsTab(data) { renderBarList('tab-divisions', data.deptSorted, data.totalRecords); }
function renderRegionsTab(data) { renderBarList('tab-regions', data.regionSorted, data.totalRecords); }
function renderInspectorsTab(data) { renderBarList('tab-inspectors', data.inspSorted, data.totalRecords); }
function renderRepeatTab(data) {
var container = document.getElementById('tab-repeat');
var items = data.repeatSorted;
var html = '';
if (items.length === 0) {
html = '<div class="result-chart-card full"><h3>Повторяющиеся нарушения</h3><p style="color:var(--gray-500)">Повторяющихся нарушений не найдено.</p></div>';
} else {
html = '<div class="result-chart-card full"><h3>Повторяющиеся нарушения (' + items.length + ')</h3>';
items.slice(0, 30).forEach(function(item) {
html += '<div class="bar-list-item"><div class="bar-list-header"><span class="name">' +
escHtml(item[1].desc || item[0]) + '</span><span class="count">' + item[1].count + ' раз</span></div>' +
'<div class="bar-list-track"><div class="bar-list-fill warn" style="width:' + Math.min(100, item[1].count * 15) + '%"></div></div></div>';
});
html += '</div>';
}
container.innerHTML = html;
}
function renderTableTab(data) {
var container = document.getElementById('tab-table');
var r = data.records;
var cols = ['date','dept','region','inspector','category','desc','status'];
var labels = { date: 'Дата', dept: 'Филиал', region: 'Область', inspector: 'Проверяющий', category: 'Категория', desc: 'Описание', status: 'Статус' };
var html = '<div class="result-table-wrap"><table class="result-table"><thead><tr>';
cols.forEach(function(c) { html += '<th>' + labels[c] + '</th>'; });
html += '</tr></thead><tbody>';
r.slice(0, 500).forEach(function(rec) {
html += '<tr>';
cols.forEach(function(c) {
var val = rec[c] || '';
if (c === 'status') val = rec.fixed ? 'Устранено' : 'На контроле';
if (c === 'desc' && val.length > 80) val = val.slice(0, 80) + '…';
html += '<td>' + escHtml(String(val)) + '</td>';
});
html += '</tr>';
});
if (r.length > 500) {
html += '<tr><td colspan="7" style="text-align:center;color:var(--gray-500)">Показаны первые 500 из ' + r.length + ' записей</td></tr>';
}
html += '</tbody></table></div>';
container.innerHTML = html;
}
function renderConclusions(data) {
var html = '';
var topCat = data.catSorted[0];
var topDept = data.deptSorted[0];
var worstDept = data.deptSorted[data.deptSorted.length - 1];
if (topCat && topCat[1] / data.totalRecords > 0.2) {
html += '<div class="conclusion-box warn-box"><h4>Проблемная зона</h4><p>Наиболее частой категорией является <strong>' +
escHtml(topCat[0]).toLowerCase() + '</strong> — доля <strong>' +
Math.round(topCat[1] / data.totalRecords * 100) + '%</strong> (' + topCat[1] + ' из ' + data.totalRecords + ' записей). ' +
'Основная концентрация — филиал <strong>' + escHtml(topDept[0]) + '</strong> (' + topDept[1] + ' нарушений).</p></div>';
}
html += '<div class="conclusion-box"><h4>Общие выводы</h4><p>Всего <strong>' + data.totalRecords +
'</strong> записей о нарушениях. Устранено: <strong>' + data.totalFixed +
'</strong> (' + Math.round(data.totalFixed / data.totalRecords * 100) + '%). На контроле: <strong>' + data.totalPending + '</strong>. ' +
'ТОП-3 категории: ' + data.catSorted.slice(0, 3).map(function(c, i) {
return (i + 1) + ') ' + escHtml(c[0]) + ' — ' + c[1] + ' (' + Math.round(c[1] / data.totalRecords * 100) + '%)';
}).join('; ') + '.</p></div>';
if (data.totalPending > data.totalFixed) {
html += '<div class="conclusion-box"><h4>Рекомендации</h4><p>На контроле <strong>' + data.totalPending +
'</strong> нарушений — требуется усилить контроль устранения. ';
if (topCat) {
var cat = topCat[0];
if (cat === 'Средства индивидуальной защиты') html += 'По СИЗ: провести внеплановые проверки применения средств защиты и целевой инструктаж. ';
else if (cat === 'Наряды-допуски') html += 'По нарядам-допускам: организовать дополнительное обучение и выборочный аудит. ';
else if (cat === 'Электробезопасность') html += 'По электробезопасности: усилить контроль со стороны ответственных лиц. ';
else if (cat === 'Пожарная безопасность') html += 'По пожарной безопасности: проверить сроки и качество инструктажей. ';
else html += 'По «' + escHtml(cat) + '»: провести внеплановую проверку и целевой инструктаж. ';
}
html += 'Рекомендуется еженедельный мониторинг.</p></div>';
}
document.getElementById('conclusions').innerHTML = html;
}
function processFile(file) {
document.getElementById('uploadStatus').textContent = 'Обработка...';
var reader = new FileReader();
reader.onload = function(e) {
try {
var text = e.target.result;
// Remove BOM
if (text.charCodeAt(0) === 0xFEFF) text = text.slice(1);
// Auto-detect delimiter
var commaCount = (text.split('\n')[0] || '').split(',').length;
var semiCount = (text.split('\n')[0] || '').split(';').length;
if (semiCount > commaCount + 2) {
text = text.replace(/;/g, ',');
}
var rows = parseCSV(text);
if (rows.length < 2) {
document.getElementById('uploadStatus').textContent = 'Ошибка: файл пуст.';
return;
}
var data = analyzeData(rows);
if (!data || data.totalRecords === 0) {
document.getElementById('uploadStatus').textContent = 'Ошибка: не удалось распознать данные.';
return;
}
document.getElementById('uploadStatus').textContent = 'Загружен «' + file.name + '» (' + data.totalRecords + ' записей)';
renderResults(data);
} catch (err) {
document.getElementById('uploadStatus').textContent = 'Ошибка: ' + err.message;
}
};
reader.readAsText(file, 'UTF-8');
}
var dropArea = document.getElementById('dropArea');
var fileInput = document.getElementById('fileInput');
dropArea.addEventListener('click', function() { fileInput.click(); });
fileInput.addEventListener('change', function() {
if (fileInput.files.length > 0) processFile(fileInput.files[0]);
});
dropArea.addEventListener('dragover', function(e) {
e.preventDefault();
dropArea.classList.add('drag-over');
});
dropArea.addEventListener('dragleave', function() {
dropArea.classList.remove('drag-over');
});
dropArea.addEventListener('drop', function(e) {
e.preventDefault();
dropArea.classList.remove('drag-over');
if (e.dataTransfer.files.length > 0) processFile(e.dataTransfer.files[0]);
});
document.querySelectorAll('.tab-btn').forEach(function(btn) {
btn.addEventListener('click', function() {
document.querySelectorAll('.tab-btn').forEach(function(b) { b.classList.remove('active'); });
document.querySelectorAll('.tab-content').forEach(function(c) { c.classList.remove('active'); });
btn.classList.add('active');
var target = document.getElementById(btn.getAttribute('data-tab'));
if (target) target.classList.add('active');
});
});
document.getElementById('sampleBtn').addEventListener('click', function() {
document.getElementById('uploadStatus').textContent = 'Загрузка демо-данных...';
var sampleCSV = generateSampleCSV();
var blob = new Blob(['\uFEFF' + sampleCSV], { type: 'text/csv;charset=utf-8' });
processFile(new File([blob], 'demo_data.csv', { type: 'text/csv' }));
});
function generateSampleCSV() {
var header = 'Дата,Филиал,Область,Город/Район,Объект с адресом,Категория нарушения,Описание нарушения,Срок исполнения,исполнено,кто выдал,кому выдано';
var filials = ['ОДС','ОДС','ОДС','ДРБ','ДИТ','Сервисная фабрика','Сервисная фабрика','ДКБ','ДУП','СФ','ЦТО СФ','ЦЭиК'];
var oblasts = ['Акмолинская','Карагандинская','Астана','Астана','Улытауская','Карагандинская','Акмолинская','Астана','Карагандинская'];
var cities = ['Шахтинск','Астана','Караганда','Темиртау','Щучинск','Атбасар','Балхаш','Кокшетау'];
var inspectors = [
'Инженер по БиОТ Семидоцкий С.А.',
'Ведущий инженер ОБиОТ Туржанов А.Т.',
'Инженер ОБиОТ Бачинская Н.В.',
'Ведущий инженер ОБиОТ Ажакметов М.З.',
'Ведущий инженер ОБиОТ Баяхметова Ж.Т.',
'Инженер ОБиОТ Садыков М.Ф.',
'Ведущий инженер ОБиОТ Тумабаева С.А.',
'Инженер по БиОТ Джусупова А.Т.',
'Инженер по БиОТ Кришталь В.П.'
];
var realCats = [
'основные док-ты по БиОТ','основные док-ты по БиОТ','основные документы по БиОТ',
'Пожарная безопасность','Пожарная безопасность','пожарная безопасность',
'Электробезопасность',
'СИЗ','СИЗ','Не обеспечение СИЗ',
'Транспортная безопасность',
'Промышленная безопасность','промышленная безопасность',
'Санитарно-бытовые условия',
'организационные требования БиОТ','Инструкция по проведению 4-х ступенчатого контроля',
'Производственная санитария'
];
var descs = [
'Отсутствует журнал инструктажа на рабочем месте',
'Не проведён повторный инструктаж за 1 квартал',
'Работники не ознакомлены с регламентом обеспечения СИЗ',
'Форма журнала наряд-допусков не соответствует правилам',
'Просроченные диэлектрические перчатки',
'Лестница не имеет даты проверки лабораторных испытаний',
'Неактуальные инструкции по ТБ по видам работ',
'Отсутствует аптечка для оказания первой помощи',
'Пожарный кран не проверялся на работоспособность',
'Работники не применяли СИЗ (без касок, пояса)',
'Прошёл срок поверки инструмента с изолированными ручками',
'Не корректно ведётся журнал 4-х ступенчатого контроля',
'На территории скопление снега, требуется вывоз',
'Отсутствует комната для сушки спецодежды',
'Аварийный выход заблокирован',
'В журнале учёта такелажных средств нет подписи председателя',
'Нет таблички об ответственности по пожарной безопасности',
'Работники не обеспечены спецодеждой и обувью'
];
var statuses = ['исполнено','исполнено','исполнено','на контроле','на контроле','исполнено','на контроле'];
var months = ['01','01','02','02','03','03','04','04','05','05','06'];
var lines = [header];
for (var i = 0; i < 200; i++) {
var m = months[Math.floor(Math.random() * months.length)];
var day = String(Math.floor(Math.random() * 28) + 1).padStart(2, '0');
var date = day + '.' + m + '.2026';
var fil = filials[Math.floor(Math.random() * filials.length)];
var obl = oblasts[Math.floor(Math.random() * oblasts.length)];
var city = cities[Math.floor(Math.random() * cities.length)];
var addr = 'ул. Примерная ' + (Math.floor(Math.random() * 50) + 1);
var cat = realCats[Math.floor(Math.random() * realCats.length)];
var desc = descs[Math.floor(Math.random() * descs.length)];
var insp = inspectors[Math.floor(Math.random() * inspectors.length)];
var status = statuses[Math.floor(Math.random() * statuses.length)];
lines.push([date, fil, obl, city, addr, cat, desc, '', status, insp, ''].join(','));
}
return lines.join('\n');
}
})();
</script>
</body>
</html>