diff --git a/VERSION b/VERSION index 419f300..eb9b76c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.19.0 +3.20.0 diff --git a/play-life-web/package-lock.json b/play-life-web/package-lock.json index 389c8b6..754a55a 100644 --- a/play-life-web/package-lock.json +++ b/play-life-web/package-lock.json @@ -1,12 +1,12 @@ { "name": "play-life-web", - "version": "3.8.9", + "version": "3.19.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "play-life-web", - "version": "3.8.9", + "version": "3.19.0", "dependencies": { "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", @@ -14,6 +14,7 @@ "chart.js": "^4.4.0", "react": "^18.2.0", "react-chartjs-2": "^5.2.0", + "react-circular-progressbar": "^2.2.0", "react-dom": "^18.2.0", "react-easy-crop": "^5.5.6" }, @@ -5913,6 +5914,15 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/react-circular-progressbar": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/react-circular-progressbar/-/react-circular-progressbar-2.2.0.tgz", + "integrity": "sha512-cgyqEHOzB0nWMZjKfWN3MfSa1LV3OatcDjPz68lchXQUEiBD5O1WsAtoVK4/DSL0B4USR//cTdok4zCBkq8X5g==", + "license": "MIT", + "peerDependencies": { + "react": ">=0.14.0" + } + }, "node_modules/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", diff --git a/play-life-web/package.json b/play-life-web/package.json index c403737..afc7b4b 100644 --- a/play-life-web/package.json +++ b/play-life-web/package.json @@ -1,6 +1,6 @@ { "name": "play-life-web", - "version": "3.19.0", + "version": "3.20.0", "type": "module", "scripts": { "dev": "vite", @@ -14,6 +14,7 @@ "chart.js": "^4.4.0", "react": "^18.2.0", "react-chartjs-2": "^5.2.0", + "react-circular-progressbar": "^2.2.0", "react-dom": "^18.2.0", "react-easy-crop": "^5.5.6" }, diff --git a/play-life-web/src/components/CurrentWeek.jsx b/play-life-web/src/components/CurrentWeek.jsx index 9fd4f90..5657558 100644 --- a/play-life-web/src/components/CurrentWeek.jsx +++ b/play-life-web/src/components/CurrentWeek.jsx @@ -1,133 +1,108 @@ import ProjectProgressBar from './ProjectProgressBar' import LoadingError from './LoadingError' import { getAllProjectsSorted, getProjectColor } from '../utils/projectUtils' +import { CircularProgressbar, buildStyles } from 'react-circular-progressbar' +import 'react-circular-progressbar/dist/styles.css' -// Компонент круглого прогрессбара -function CircularProgressBar({ progress, size = 120, strokeWidth = 8, showCheckmark = true }) { +// Компонент круглого прогрессбара с использованием react-circular-progressbar +function CircularProgressBar({ progress, size = 120, strokeWidth = 8, showCheckmark = true, extraProgress = null, maxProgress = 100, textSize = 'large', displayProgress = null, textPosition = 'default', projectColor = null }) { + // Нормализуем прогресс для визуализации (0-100%) const normalizedProgress = Math.min(Math.max(progress || 0, 0), 100) - const radius = (size - strokeWidth) / 2 - const circumference = radius * 2 * Math.PI - const strokeDasharray = `${(normalizedProgress / 100) * circumference} ${circumference}` + + // Если есть extra progress, вычисляем визуальный прогресс для overlay + const extraVisual = extraProgress !== null && extraProgress > 0 + ? Math.min((extraProgress / maxProgress) * 100, 100) + : 0 + + // Определяем, достигнут ли 100% или выше + const isComplete = (displayProgress !== null ? displayProgress : progress) >= 100 + + // Определяем градиент ID: зелёный если >= 100%, иначе по наличию extra progress + const gradientId = isComplete ? 'success-gradient' : (extraVisual > 0 ? 'project-gradient' : 'overall-gradient') + const extraGradientId = 'project-extra-gradient' + + // Определяем класс размера текста + const textSizeClass = textSize === 'large' ? 'text-4xl' : textSize === 'small' ? 'text-base' : 'text-lg' + + // Используем displayProgress если передан (может быть больше 100%), иначе progress + const progressToDisplay = displayProgress !== null ? displayProgress : progress return ( -
- - {/* Фоновая окружность */} - + + + {/* Extra progress overlay (если есть) */} + {extraVisual > 0 && ( + - {/* Прогресс окружность */} - - {/* Галочка при 100% */} - {showCheckmark && progress >= 100 && ( - - - - )} + )} + + {/* Иконка статистики в центре */} +
+ + + + + +
+ + {/* Кастомный текст снизу */} +
+
+
+ {progressToDisplay !== null && progressToDisplay !== undefined ? `${progressToDisplay.toFixed(0)}%` : 'N/A'} +
+
+
+ + {/* Градиенты для SVG */} + - - - {/* Процент в центре */} -
-
-
- {progress !== null && progress !== undefined ? `${progress.toFixed(0)}%` : 'N/A'} -
-
-
-
- ) -} - -// Специальный компонент круглого прогрессбара с поддержкой экстра прогресса -function ProjectCircularProgressBar({ progress, extraProgress, size = 120, strokeWidth = 8, showCheckmark = true, percentage }) { - const normalizedProgress = Math.min(Math.max(progress || 0, 0), 100) - const normalizedExtraProgress = Math.min(Math.max(extraProgress || 0, 0), 100) - const radius = (size - strokeWidth) / 2 - const circumference = radius * 2 * Math.PI - const strokeDasharray = `${(normalizedProgress / 100) * circumference} ${circumference}` - const extraStrokeDasharray = normalizedExtraProgress > 0 ? `${(normalizedExtraProgress / 100) * circumference} ${circumference}` : '' - - return ( -
- - {/* Фоновая окружность */} - - {/* Базовый прогресс окружность */} - - {/* Экстра прогресс окружность (другой цвет) */} - {normalizedExtraProgress > 0 && ( - - )} - {/* Галочка при 100% */} - {showCheckmark && progress >= 100 && ( - - - - )} - @@ -136,18 +111,12 @@ function ProjectCircularProgressBar({ progress, extraProgress, size = 120, strok + + + + - {/* Процент в центре */} - {percentage && ( -
-
-
- {percentage}% -
-
-
- )}
) } @@ -212,10 +181,14 @@ function ProjectCard({ project, projectColor, onProjectClick }) { })() // Для визуального отображения: 100% прогрессбара = максимум для данного приоритета - const visualProgress = (goalProgress / maxProgressForPriority) * 100 - const baseProgress = Math.min(goalProgress, 100) // Базовая часть (0-100%) - const extraProgress = Math.max(0, goalProgress - 100) // Экстра часть (свыше 100%) - const extraVisualProgress = (extraProgress / maxProgressForPriority) * 100 // Экстра часть в процентах от полного диапазона + // visualProgress показывает процент заполнения прогрессбара (0-100%), где 100% = maxProgressForPriority + const visualProgress = Math.min((goalProgress / maxProgressForPriority) * 100, 100) + + // Для extra overlay: если goalProgress > 100%, показываем extra часть + // Но визуально это уже учтено в visualProgress, так что extra overlay не нужен + // Однако если нужно показать, что достигнут максимум, можно использовать другой подход + const baseVisualProgress = visualProgress + const extraVisualProgress = 0 // Не используем extra overlay, так как visualProgress уже показывает весь прогресс // Вычисляем целевую зону const getTargetZone = () => { @@ -239,32 +212,33 @@ function ProjectCard({ project, projectColor, onProjectClick }) { return (
{/* Верхняя часть с названием и прогрессом */}
- {/* Левая часть - текст (название, баллы, целевая зона) */} -
-
- {project_name} -
-
- {total_score?.toFixed(1) || '0.0'} -
-
- Целевая зона: {getTargetZone()} -
-
+ {/* Левая часть - текст (название, баллы, целевая зона) */} +
+
+ {project_name} +
+
+ {total_score?.toFixed(1) || '0.0'} +
+
+ Целевая зона: {getTargetZone()} +
+
{/* Правая часть - круглый прогрессбар */}
-
@@ -446,6 +420,7 @@ function CurrentWeek({ onProjectClick, data, loading, error, onRetry, allProject progress={displayOverallProgress} size={180} strokeWidth={12} + showCheckmark={true} /> {/* Подсказка при наведении */}
diff --git a/play-life-web/src/components/FullStatistics.jsx b/play-life-web/src/components/FullStatistics.jsx index 0932ed0..baec64e 100644 --- a/play-life-web/src/components/FullStatistics.jsx +++ b/play-life-web/src/components/FullStatistics.jsx @@ -213,7 +213,7 @@ function FullStatistics({ selectedProject, onClearSelection, data, loading, erro ✕ )} -
+