analyzer / index.html
AssanaliAidarkhan's picture
Upload index.html
f2b585a verified
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mindia AI - Аналитика инцидентов</title>
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script src="https://cdn.tailwindcss.com"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in-up {
animation: fadeInUp 0.6s ease-out forwards;
}
.stagger-1 { animation-delay: 0.1s; }
.stagger-2 { animation-delay: 0.2s; }
.stagger-3 { animation-delay: 0.3s; }
.stagger-4 { animation-delay: 0.4s; }
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const { useState, useEffect, useRef } = React;
// Icons
const AlertTriangle = ({ size = 24, className = "" }) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}>
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
<line x1="12" y1="9" x2="12" y2="13"/>
<line x1="12" y1="17" x2="12.01" y2="17"/>
</svg>
);
const Brain = ({ size = 24, className = "" }) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}>
<path d="M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44 2.5 2.5 0 0 1-2.96-3.08 3 3 0 0 1-.34-5.58 2.5 2.5 0 0 1 1.32-4.24 2.5 2.5 0 0 1 1.98-3A2.5 2.5 0 0 1 9.5 2Z"/>
<path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24 2.5 2.5 0 0 0-1.98-3A2.5 2.5 0 0 0 14.5 2Z"/>
</svg>
);
const TrendingDown = ({ size = 24, className = "" }) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}>
<polyline points="23 18 13.5 8.5 8.5 13.5 1 6"/>
<polyline points="17 18 23 18 23 12"/>
</svg>
);
const BarChart = ({ size = 24, className = "" }) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}>
<line x1="12" y1="20" x2="12" y2="10"/>
<line x1="18" y1="20" x2="18" y2="4"/>
<line x1="6" y1="20" x2="6" y2="16"/>
</svg>
);
const FileText = ({ size = 24, className = "" }) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}>
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
<polyline points="14 2 14 8 20 8"/>
</svg>
);
const Lightbulb = ({ size = 24, className = "" }) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}>
<path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"/>
<path d="M9 18h6"/>
<path d="M10 22h4"/>
</svg>
);
const Calendar = ({ size = 24, className = "" }) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}>
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"/>
<line x1="16" y1="2" x2="16" y2="6"/>
<line x1="8" y1="2" x2="8" y2="6"/>
<line x1="3" y1="10" x2="21" y2="10"/>
</svg>
);
const Target = ({ size = 24, className = "" }) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}>
<circle cx="12" cy="12" r="10"/>
<circle cx="12" cy="12" r="6"/>
<circle cx="12" cy="12" r="2"/>
</svg>
);
const CheckCircle = ({ size = 24, className = "" }) => (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}>
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/>
<polyline points="22 4 12 14.01 9 11.01"/>
</svg>
);
const IncidentAnalyticsDashboard = () => {
const [selectedPattern, setSelectedPattern] = useState(null);
const chartCanvasRef = useRef(null);
const patterns = [
{
id: 1,
severity: 'high',
title: 'Падение груза со стеллажей на складе №3',
percentage: 65,
description: '65% случаев падения груза со стеллажей на складе №3 происходит в ночную смену (22:00-06:00).',
hypothesis: 'Недостаточное освещение, усталость персонала.',
reports: 23,
location: 'Склад №3',
timeframe: 'Ночная смена (22:00-06:00)',
recommendations: [
'Усилить освещение на складе №3',
'Ввести дополнительные перерывы в ночную смену',
'Провести аудит состояния стеллажей'
]
},
{
id: 2,
severity: 'medium',
title: 'Случаи падения из-за скользкого пола в Цехе №1',
percentage: 45,
description: 'Слово "скользкий пол" упоминается в 12 отчетах по Цеху №1, чаще всего после проезда уборочной машины.',
hypothesis: 'Неэффективные предупреждающие знаки, неправильное время уборки.',
reports: 12,
location: 'Цех №1',
timeframe: 'После уборки (10:00-11:00)',
recommendations: [
'Изменить график уборки на нерабочее время',
'Установить более заметные предупреждающие знаки',
'Использовать быстросохнущие чистящие средства'
]
},
{
id: 3,
severity: 'medium',
title: 'Микротравмы рук при работе с инструментом',
percentage: 38,
description: '38% случаев микротравм рук происходит при работе с пневмоинструментом в первые 30 минут смены.',
hypothesis: 'Недостаточная разминка, холодные ручки инструмента, спешка в начале смены.',
reports: 15,
location: 'Цех №2, Участок сборки',
timeframe: 'Начало смены (первые 30 мин)',
recommendations: [
'Ввести обязательную разминку перед сменой',
'Установить подогрев ручек пневмоинструмента',
'Пересмотреть планы работы на начало смены'
]
},
{
id: 4,
severity: 'low',
title: 'Ложные срабатывания пожарной сигнализации',
percentage: 85,
description: '85% ложных срабатываний пожарной сигнализации происходит в зоне сварки из-за повышенного задымления.',
hypothesis: 'Неправильное размещение датчиков, недостаточная вентиляция.',
reports: 28,
location: 'Цех №2, Зона сварки',
timeframe: 'Дневная смена',
recommendations: [
'Переместить датчики дальше от зоны сварки',
'Улучшить вентиляцию в зоне сварки',
'Установить датчики другого типа'
]
}
];
const getSeverityConfig = (severity) => {
switch(severity) {
case 'high':
return {
color: 'from-red-500 to-red-700',
bgColor: 'bg-red-50',
borderColor: 'border-red-400',
textColor: 'text-red-700',
badgeColor: 'bg-red-100 text-red-700 border-red-300',
label: 'ВЫСОКИЙ РИСК'
};
case 'medium':
return {
color: 'from-yellow-500 to-orange-600',
bgColor: 'bg-yellow-50',
borderColor: 'border-yellow-400',
textColor: 'text-yellow-700',
badgeColor: 'bg-yellow-100 text-yellow-700 border-yellow-300',
label: 'СРЕДНИЙ РИСК'
};
default:
return {
color: 'from-blue-500 to-blue-700',
bgColor: 'bg-blue-50',
borderColor: 'border-blue-400',
textColor: 'text-blue-700',
badgeColor: 'bg-blue-100 text-blue-700 border-blue-300',
label: 'НИЗКИЙ РИСК'
};
}
};
// Draw incident chart
useEffect(() => {
const canvas = chartCanvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
// Background
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Data for 2022-2023
const months = ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'];
const incidents2022 = [12, 15, 10, 14, 18, 16, 13, 11, 9, 8, 7, 10];
const incidents2023 = [8, 7, 6, 5, 7, 6, 5, 4, 5, 4, 3, 4];
const maxIncidents = 20;
const barWidth = 25;
const spacing = 60;
const startX = 60;
const startY = 40;
const chartHeight = canvas.height - 80;
// Grid
ctx.strokeStyle = '#e5e7eb';
ctx.lineWidth = 1;
for (let i = 0; i <= 4; i++) {
const y = startY + (chartHeight / 4) * i;
ctx.beginPath();
ctx.moveTo(startX, y);
ctx.lineTo(canvas.width - 40, y);
ctx.stroke();
// Y-axis labels
ctx.fillStyle = '#6b7280';
ctx.font = '12px Arial';
ctx.textAlign = 'right';
ctx.fillText((maxIncidents - (i * 5)).toString(), startX - 10, y + 4);
}
// Bars
months.forEach((month, index) => {
const x = startX + index * spacing;
// 2022 bar
const height2022 = (incidents2022[index] / maxIncidents) * chartHeight;
ctx.fillStyle = '#ef4444';
ctx.fillRect(x, startY + chartHeight - height2022, barWidth, height2022);
// 2023 bar
const height2023 = (incidents2023[index] / maxIncidents) * chartHeight;
ctx.fillStyle = '#10b981';
ctx.fillRect(x + barWidth + 5, startY + chartHeight - height2023, barWidth, height2023);
// Month label
ctx.fillStyle = '#374151';
ctx.font = '11px Arial';
ctx.textAlign = 'center';
ctx.fillText(month, x + barWidth + 2, canvas.height - 20);
});
// Legend
ctx.fillStyle = '#ef4444';
ctx.fillRect(startX, 10, 20, 15);
ctx.fillStyle = '#374151';
ctx.font = 'bold 12px Arial';
ctx.textAlign = 'left';
ctx.fillText('2022', startX + 25, 22);
ctx.fillStyle = '#10b981';
ctx.fillRect(startX + 80, 10, 20, 15);
ctx.fillStyle = '#374151';
ctx.fillText('2023', startX + 105, 22);
// Title
ctx.fillStyle = '#1f2937';
ctx.font = 'bold 14px Arial';
ctx.textAlign = 'center';
ctx.fillText('Динамика инцидентов по месяцам', canvas.width / 2, canvas.height - 5);
}, []);
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-red-50 p-6">
<div className="max-w-7xl mx-auto">
{/* Header */}
<div className="bg-gradient-to-r from-red-600 to-orange-600 rounded-xl shadow-2xl p-6 mb-6 border-2 border-red-300">
<div className="flex justify-between items-center">
<div className="flex items-center gap-4">
<div className="bg-white p-3 rounded-xl shadow-lg">
<Brain size={32} className="text-red-600" />
</div>
<div>
<h1 className="text-3xl font-bold text-white">Аналитика инцидентов</h1>
<p className="text-red-50 text-sm font-medium flex items-center gap-2">
<Calendar size={16} />
Период: 2022-2023 • Mindia AI Analysis
</p>
</div>
</div>
<div className="text-right">
<p className="text-red-100 text-sm">Всего проанализировано</p>
<p className="text-4xl font-bold text-white">487</p>
<p className="text-red-100 text-xs">отчетов об инцидентах</p>
</div>
</div>
</div>
<div className="grid grid-cols-3 gap-6">
{/* Left Column - Patterns */}
<div className="col-span-2 space-y-6">
{/* Patterns List */}
<div className="bg-white rounded-xl shadow-xl p-6 border-2 border-gray-200">
<div className="flex items-center gap-2 mb-6">
<Target className="text-red-600" size={24} />
<h2 className="text-2xl font-bold text-gray-800">Обнаруженные закономерности</h2>
</div>
<div className="space-y-4">
{patterns.map((pattern, index) => {
const config = getSeverityConfig(pattern.severity);
return (
<div
key={pattern.id}
className={`rounded-xl p-6 border-2 ${config.borderColor} ${config.bgColor} cursor-pointer hover:shadow-lg transition-all fade-in-up stagger-${index + 1}`}
onClick={() => setSelectedPattern(pattern)}
>
<div className="flex items-start gap-4 mb-4">
<div className="flex-shrink-0">
<span className={`inline-block px-3 py-1 rounded-full text-xs font-bold border-2 ${config.badgeColor}`}>
{config.label}
</span>
</div>
<div className="flex-1">
<h3 className="text-lg font-bold text-gray-800 mb-2">
{index + 1}. {pattern.title}
</h3>
<p className="text-gray-700 mb-3">
{pattern.description}
</p>
<div className="flex items-center gap-4 mb-3">
<div className="flex items-center gap-2 text-sm text-gray-600">
<FileText size={16} />
<span className="font-semibold">{pattern.reports} отчетов</span>
</div>
<div className={`px-3 py-1 rounded-full text-xs font-bold ${config.textColor} bg-white border`}>
{pattern.percentage}% случаев
</div>
</div>
<div className="bg-white rounded-lg p-3 border border-gray-200">
<div className="flex items-start gap-2">
<Lightbulb className="text-yellow-500 flex-shrink-0 mt-0.5" size={18} />
<div>
<p className="text-xs text-gray-600 font-semibold mb-1">Гипотеза:</p>
<p className="text-sm text-gray-800">{pattern.hypothesis}</p>
</div>
</div>
</div>
</div>
</div>
<button className="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-lg flex items-center justify-center gap-2 transition-colors">
<FileText size={18} />
Посмотреть отчеты
</button>
</div>
);
})}
</div>
</div>
{/* Chart */}
<div className="bg-white rounded-xl shadow-xl p-6 border-2 border-gray-200">
<div className="flex items-center gap-2 mb-4">
<BarChart className="text-blue-600" size={24} />
<h2 className="text-xl font-bold text-gray-800">Динамика инцидентов</h2>
</div>
<canvas
ref={chartCanvasRef}
width={800}
height={300}
className="w-full"
/>
</div>
</div>
{/* Right Column - Details and Stats */}
<div className="col-span-1 space-y-6">
{/* Selected Pattern Details */}
{selectedPattern ? (
<div className="bg-white rounded-xl shadow-xl p-6 border-2 border-purple-200 fade-in-up">
<div className="flex items-center gap-2 mb-4">
<Target className="text-purple-600" size={24} />
<h2 className="text-lg font-bold text-gray-800">Детали анализа</h2>
</div>
<div className="space-y-4">
<div>
<p className="text-xs text-gray-600 font-semibold mb-1">Локация:</p>
<p className="text-sm font-bold text-gray-800">{selectedPattern.location}</p>
</div>
<div>
<p className="text-xs text-gray-600 font-semibold mb-1">Временной период:</p>
<p className="text-sm font-bold text-gray-800">{selectedPattern.timeframe}</p>
</div>
<div>
<p className="text-xs text-gray-600 font-semibold mb-1">Количество случаев:</p>
<p className="text-2xl font-bold text-red-600">{selectedPattern.reports}</p>
</div>
<div>
<p className="text-xs text-gray-600 font-semibold mb-2">Рекомендации AI:</p>
<div className="space-y-2">
{selectedPattern.recommendations.map((rec, index) => (
<div key={index} className="flex items-start gap-2 bg-green-50 p-2 rounded border border-green-200">
<CheckCircle className="text-green-600 flex-shrink-0 mt-0.5" size={16} />
<p className="text-xs text-gray-800">{rec}</p>
</div>
))}
</div>
</div>
</div>
</div>
) : (
<div className="bg-white rounded-xl shadow-xl p-6 border-2 border-gray-200 text-center">
<Target size={48} className="mx-auto mb-3 text-gray-300" />
<p className="text-sm text-gray-600">
Нажмите на закономерность для просмотра деталей
</p>
</div>
)}
{/* Statistics */}
<div className="bg-white rounded-xl shadow-xl p-6 border-2 border-blue-200">
<h2 className="text-lg font-bold text-gray-800 mb-4">Эффективность</h2>
<div className="space-y-4">
<div className="bg-gradient-to-br from-green-500 to-green-600 rounded-lg p-4 text-white">
<div className="flex items-center gap-2 mb-1">
<TrendingDown size={18} />
<p className="text-sm font-semibold">Снижение инцидентов</p>
</div>
<p className="text-3xl font-bold">-40%</p>
<p className="text-xs opacity-90">за 2023 год</p>
</div>
<div className="bg-gradient-to-br from-blue-500 to-blue-600 rounded-lg p-4 text-white">
<div className="flex items-center gap-2 mb-1">
<Brain size={18} />
<p className="text-sm font-semibold">Найдено закономерностей</p>
</div>
<p className="text-3xl font-bold">{patterns.length}</p>
<p className="text-xs opacity-90">системных причин</p>
</div>
<div className="bg-gradient-to-br from-purple-500 to-purple-600 rounded-lg p-4 text-white">
<div className="flex items-center gap-2 mb-1">
<AlertTriangle size={18} />
<p className="text-sm font-semibold">Предотвращено повторов</p>
</div>
<p className="text-3xl font-bold">67</p>
<p className="text-xs opacity-90">потенциальных инцидентов</p>
</div>
</div>
</div>
{/* AI Insights */}
<div className="bg-gradient-to-br from-indigo-600 to-purple-700 rounded-xl shadow-xl p-6 text-white">
<div className="flex items-center gap-2 mb-4">
<Brain size={24} />
<h2 className="text-lg font-bold">AI Инсайты</h2>
</div>
<div className="space-y-3 text-sm">
<p className="opacity-90">
Анализ выявил, что <span className="font-bold">большинство инцидентов</span> имеют системные, а не случайные причины.
</p>
<p className="opacity-90">
<span className="font-bold">Время суток</span> является ключевым фактором в 73% случаев.
</p>
<p className="opacity-90">
Рекомендуется сфокусироваться на <span className="font-bold">изменении условий труда</span>, а не на дисциплинарных мерах.
</p>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<IncidentAnalyticsDashboard />);
</script>
</body>
</html>