Initial commit
This commit is contained in:
200
play-life-web/src/components/CurrentWeek.jsx
Normal file
200
play-life-web/src/components/CurrentWeek.jsx
Normal file
@@ -0,0 +1,200 @@
|
||||
import ProjectProgressBar from './ProjectProgressBar'
|
||||
import { getAllProjectsSorted, getProjectColor } from '../utils/projectUtils'
|
||||
|
||||
function CurrentWeek({ onProjectClick, data, loading, error, onRetry, allProjectsData, onNavigate }) {
|
||||
// Обрабатываем данные: может быть объект с projects и total, или просто массив
|
||||
const projectsData = data?.projects || (Array.isArray(data) ? data : [])
|
||||
|
||||
// Показываем loading только если данных нет и идет загрузка
|
||||
if (loading && (!data || projectsData.length === 0)) {
|
||||
return (
|
||||
<div className="flex justify-center items-center py-16">
|
||||
<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>
|
||||
)
|
||||
}
|
||||
|
||||
if (error && (!data || projectsData.length === 0)) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center py-16">
|
||||
<div className="bg-red-50 border border-red-200 rounded-lg p-6 mb-4 max-w-md">
|
||||
<div className="text-red-700 font-semibold mb-2">Ошибка загрузки</div>
|
||||
<div className="text-red-600 text-sm">{error}</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={onRetry}
|
||||
className="px-6 py-3 bg-gradient-to-r from-indigo-600 to-purple-600 text-white rounded-lg hover:from-indigo-700 hover:to-purple-700 transition-all duration-200 shadow-lg hover:shadow-xl font-semibold"
|
||||
>
|
||||
Попробовать снова
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Процент выполнения берем только из данных API
|
||||
const overallProgress = (() => {
|
||||
// Проверяем различные возможные названия поля
|
||||
const rawValue = data?.total ?? data?.progress ?? data?.percentage ?? data?.completion ?? data?.goal_progress
|
||||
const parsedValue = rawValue === undefined || rawValue === null ? null : parseFloat(rawValue)
|
||||
|
||||
if (Number.isFinite(parsedValue) && parsedValue >= 0) {
|
||||
return Math.max(0, parsedValue) // Убрали ограничение на 100, так как может быть больше
|
||||
}
|
||||
|
||||
return null // null означает, что данные не пришли
|
||||
})()
|
||||
|
||||
const hasProgressData = overallProgress !== null
|
||||
|
||||
// Логирование для отладки
|
||||
console.log('CurrentWeek data:', {
|
||||
data,
|
||||
dataTotal: data?.total,
|
||||
dataProgress: data?.progress,
|
||||
dataPercentage: data?.percentage,
|
||||
overallProgress,
|
||||
hasProgressData
|
||||
})
|
||||
|
||||
if (!projectsData || projectsData.length === 0) {
|
||||
return (
|
||||
<div className="flex justify-center items-center py-16">
|
||||
<div className="text-gray-500 text-lg">Нет данных для отображения</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Получаем отсортированный список всех проектов для синхронизации цветов
|
||||
const allProjects = getAllProjectsSorted(allProjectsData, projectsData)
|
||||
|
||||
const normalizePriority = (value) => {
|
||||
if (value === null || value === undefined) return Infinity
|
||||
const numeric = Number(value)
|
||||
return Number.isFinite(numeric) ? numeric : Infinity
|
||||
}
|
||||
|
||||
// Сортируем: сначала по priority (1, 2, ...; null в конце), затем по min_goal_score по убыванию
|
||||
const sortedData = [...projectsData].sort((a, b) => {
|
||||
const priorityA = normalizePriority(a.priority)
|
||||
const priorityB = normalizePriority(b.priority)
|
||||
|
||||
if (priorityA !== priorityB) {
|
||||
return priorityA - priorityB
|
||||
}
|
||||
|
||||
const minGoalA = parseFloat(a.min_goal_score) || 0
|
||||
const minGoalB = parseFloat(b.min_goal_score) || 0
|
||||
return minGoalB - minGoalA
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Информация об общем проценте выполнения целей */}
|
||||
<div className="mb-3 bg-gradient-to-r from-indigo-50 to-purple-50 rounded-lg p-4 border border-indigo-200">
|
||||
<div className="flex items-stretch justify-between gap-4">
|
||||
<div className="min-w-0 flex-1 flex items-center gap-4">
|
||||
<div className="flex-1">
|
||||
<div className="text-sm sm:text-base text-gray-600 mb-1">Выполнение целей</div>
|
||||
<div className="text-2xl sm:text-3xl lg:text-4xl font-bold bg-gradient-to-r from-indigo-600 to-purple-600 bg-clip-text text-transparent">
|
||||
{hasProgressData ? `${overallProgress.toFixed(1)}%` : 'N/A'}
|
||||
</div>
|
||||
</div>
|
||||
{hasProgressData && (
|
||||
<div className="w-12 h-12 sm:w-16 sm:h-16 relative flex-shrink-0">
|
||||
<svg className="transform -rotate-90" viewBox="0 0 64 64">
|
||||
<circle
|
||||
cx="32"
|
||||
cy="32"
|
||||
r="28"
|
||||
stroke="currentColor"
|
||||
strokeWidth="6"
|
||||
fill="none"
|
||||
className="text-gray-200"
|
||||
/>
|
||||
<circle
|
||||
cx="32"
|
||||
cy="32"
|
||||
r="28"
|
||||
stroke="url(#gradient)"
|
||||
strokeWidth="6"
|
||||
fill="none"
|
||||
strokeDasharray={`${Math.min(overallProgress / 100, 1) * 175.93} 175.93`}
|
||||
strokeLinecap="round"
|
||||
/>
|
||||
{overallProgress >= 100 && (
|
||||
<g className="transform rotate-90" style={{ transformOrigin: '32px 32px' }}>
|
||||
<path
|
||||
d="M 20 32 L 28 40 L 44 24"
|
||||
stroke="url(#gradient)"
|
||||
strokeWidth="4"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
fill="none"
|
||||
/>
|
||||
</g>
|
||||
)}
|
||||
<defs>
|
||||
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" stopColor="#4f46e5" />
|
||||
<stop offset="100%" stopColor="#9333ea" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{onNavigate && (
|
||||
<div className="flex flex-col flex-shrink-0 gap-1" style={{ minHeight: '64px', height: '100%' }}>
|
||||
<button
|
||||
onClick={() => onNavigate('full')}
|
||||
className="flex-1 flex items-center justify-center px-4 bg-white hover:bg-indigo-50 text-indigo-600 hover:text-indigo-700 rounded-lg border border-indigo-200 hover:border-indigo-300 transition-all duration-200 shadow-sm hover:shadow-md"
|
||||
title="Статистика"
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<line x1="18" y1="20" x2="18" y2="10"></line>
|
||||
<line x1="12" y1="20" x2="12" y2="4"></line>
|
||||
<line x1="6" y1="20" x2="6" y2="14"></line>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onNavigate('priorities')}
|
||||
className="flex-1 flex items-center justify-center px-4 bg-white hover:bg-indigo-50 text-indigo-600 hover:text-indigo-700 rounded-lg border border-indigo-200 hover:border-indigo-300 transition-all duration-200 shadow-sm hover:shadow-md"
|
||||
title="Приоритеты"
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3">
|
||||
{sortedData.map((project, index) => {
|
||||
const projectColor = getProjectColor(project.project_name, allProjects)
|
||||
|
||||
return (
|
||||
<div key={index}>
|
||||
<ProjectProgressBar
|
||||
projectName={project.project_name}
|
||||
totalScore={parseFloat(project.total_score)}
|
||||
minGoalScore={parseFloat(project.min_goal_score)}
|
||||
maxGoalScore={parseFloat(project.max_goal_score)}
|
||||
onProjectClick={onProjectClick}
|
||||
projectColor={projectColor}
|
||||
priority={project.priority}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CurrentWeek
|
||||
|
||||
Reference in New Issue
Block a user