4.11.0: Редактирование экрана статистики
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m1s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m1s
This commit is contained in:
@@ -1,187 +1,18 @@
|
||||
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 LoadingError from './LoadingError'
|
||||
import { getAllProjectsSorted, getProjectColor, sortProjectsLikeCurrentWeek } from '../utils/projectUtils'
|
||||
import { getAllProjectsSorted } from '../utils/projectUtils'
|
||||
import './Integrations.css'
|
||||
|
||||
// Экспортируем для обратной совместимости (если используется в других местах)
|
||||
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()
|
||||
|
||||
if (error && !chartData && !loading) {
|
||||
if (error && (!data || data.length === 0) && !loading) {
|
||||
return <LoadingError onRetry={onRetry} />
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className="max-w-2xl mx-auto">
|
||||
@@ -194,24 +25,19 @@ function FullStatistics({ selectedProject, onClearSelection, data, loading, erro
|
||||
✕
|
||||
</button>
|
||||
)}
|
||||
{loading && !chartData ? (
|
||||
{loading && (!data || data.length === 0) ? (
|
||||
<div className="fixed inset-0 flex justify-center items-center">
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="w-12 h-12 border-4 border-indigo-200 border-t-indigo-600 rounded-full animate-spin mb-4"></div>
|
||||
<div className="text-gray-600 font-medium">Загрузка...</div>
|
||||
</div>
|
||||
</div>
|
||||
) : !chartData ? (
|
||||
) : (!data || data.length === 0) ? (
|
||||
<div className="flex justify-center items-center py-16">
|
||||
<div className="text-gray-500 text-lg">Нет данных для отображения</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div style={{ height: '550px', paddingTop: '100px' }}>
|
||||
<Line data={chartData} options={chartOptions} />
|
||||
</div>
|
||||
<WeekProgressChart data={data} allProjectsSorted={getAllProjectsSorted(data)} currentWeekData={currentWeekData} selectedProject={selectedProject} />
|
||||
</>
|
||||
<WeekProgressChart data={data} allProjectsSorted={getAllProjectsSorted(data)} currentWeekData={currentWeekData} selectedProject={selectedProject} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user