import React from 'react' import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, Filler } from 'chart.js' import { Line } from 'react-chartjs-2' import WeekProgressChart from './WeekProgressChart' import { getAllProjectsSorted, getProjectColor, sortProjectsLikeCurrentWeek } from '../utils/projectUtils' // Экспортируем для обратной совместимости (если используется в других местах) export { getProjectColorByIndex } from '../utils/projectUtils' const parseWeekKey = (weekKey) => { const [yearStr, weekStr] = weekKey.split('-W') return { year: Number(yearStr), week: Number(weekStr) } } const compareWeekKeys = (a, b) => { const [yearA, weekA] = a.split('-W').map(Number) const [yearB, weekB] = b.split('-W').map(Number) if (yearA !== yearB) { return yearA - yearB } return weekA - weekB } ChartJS.register( CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, Filler ) function FullStatistics({ selectedProject, onClearSelection, data, loading, error, onRetry, currentWeekData, onNavigate }) { const processData = () => { if (!data || data.length === 0) return null // Группируем данные по проектам const projectsMap = {} data.forEach(item => { const projectName = item.project_name const weekKey = `${item.report_year}-W${item.report_week.toString().padStart(2, '0')}` if (!projectsMap[projectName]) { projectsMap[projectName] = {} } projectsMap[projectName][weekKey] = parseFloat(item.total_score) }) // Собираем все уникальные недели и сортируем их по году и неделе const allWeeks = new Set() Object.values(projectsMap).forEach(weeks => { Object.keys(weeks).forEach(week => allWeeks.add(week)) }) const sortedWeeks = Array.from(allWeeks).sort(compareWeekKeys) // Получаем отсортированный список всех проектов для синхронизации цветов const allProjectNames = getAllProjectsSorted(data) // Фильтруем по выбранному проекту, если он указан let projectNames = allProjectNames.filter(name => projectsMap[name]) // Сортируем проекты так же, как на экране списка проектов (по priority и min_goal_score) if (currentWeekData) { projectNames = sortProjectsLikeCurrentWeek(projectNames, currentWeekData) } if (selectedProject) { projectNames = projectNames.filter(name => name === selectedProject) } const datasets = projectNames.map((projectName) => { let cumulativeSum = 0 const values = sortedWeeks.map(week => { const score = projectsMap[projectName]?.[week] || 0 cumulativeSum += score return cumulativeSum }) // Генерируем цвет для линии на основе индекса в полном списке проектов const color = getProjectColor(projectName, allProjectNames) return { label: projectName, data: values, borderColor: color, backgroundColor: color.replace('50%)', '20%)'), fill: false, tension: 0.4, pointRadius: 4, pointHoverRadius: 6, } }) return { labels: sortedWeeks, datasets: datasets, } } const chartData = processData() // Показываем loading только если данных нет и идет загрузка if (loading && !chartData) { return (
Загрузка данных...
) } if (error && !chartData) { return (
Ошибка загрузки
{error}
) } const chartOptions = { responsive: true, maintainAspectRatio: false, animation: false, plugins: { legend: { position: 'bottom', labels: { usePointStyle: true, padding: 15, font: { size: 12, }, }, padding: { top: 20, }, }, title: { display: false, }, tooltip: { mode: 'index', intersect: false, callbacks: { label: function(context) { return `${context.dataset.label}: ${context.parsed.y.toFixed(2)}` } } }, }, scales: { x: { display: true, title: { display: false, }, grid: { display: true, color: 'rgba(0, 0, 0, 0.05)', }, }, y: { display: true, title: { display: false, }, beginAtZero: true, grid: { display: true, color: 'rgba(0, 0, 0, 0.05)', }, }, }, interaction: { mode: 'nearest', axis: 'x', intersect: false, }, } if (!chartData) { return (
Нет данных для отображения
) } return (
{onNavigate && (
)}
) } export default FullStatistics