)
}
// Компонент группы проектов по приоритету
function PriorityGroup({ title, subtitle, projects, allProjects, onProjectClick }) {
if (projects.length === 0) return null
return (
)
return typeof document !== 'undefined'
? createPortal(modalContent, document.body)
: modalContent
}
function CurrentWeek({ onProjectClick, data, loading, error, onRetry, allProjectsData, onNavigate, onOpenAddModal }) {
const { authFetch } = useAuth()
const [isAddModalOpen, setIsAddModalOpen] = useState(false)
const [toastMessage, setToastMessage] = useState(null)
// Экспортируем функцию открытия модала для использования из App.jsx
useEffect(() => {
if (onOpenAddModal) {
const openFn = () => {
setIsAddModalOpen(true)
}
onOpenAddModal(openFn)
}
}, [onOpenAddModal])
// Функция для обновления данных после добавления записи
const refreshData = () => {
if (onRetry) {
onRetry()
}
}
// Обрабатываем данные: может быть объект с projects и total, или просто массив
const projectsData = data?.projects || (Array.isArray(data) ? data : []) || []
// Показываем loading только если данных нет и идет загрузка
if (loading && (!data || projectsData.length === 0)) {
return (
Загрузка...
)
}
if (error && (!data || projectsData.length === 0)) {
return
}
// Процент выполнения берем только из данных 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
// Получаем отсортированный список всех проектов для синхронизации цветов
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
}
// Группируем проекты по приоритетам
const priorityGroups = {
main: [], // priority === 1
important: [], // priority === 2
others: [] // остальные
}
if (projectsData && projectsData.length > 0) {
projectsData.forEach(project => {
if (!project || !project.project_name) return
const priority = normalizePriority(project.priority)
if (priority === 1) {
priorityGroups.main.push(project)
} else if (priority === 2) {
priorityGroups.important.push(project)
} else {
priorityGroups.others.push(project)
}
})
// Сортируем внутри каждой группы по min_goal_score по убыванию
Object.values(priorityGroups).forEach(group => {
group.sort((a, b) => {
const minGoalA = parseFloat(a.min_goal_score) || 0
const minGoalB = parseFloat(b.min_goal_score) || 0
return minGoalB - minGoalA
})
})
}
// Получаем проценты групп из API данных
const mainProgress = (() => {
const rawValue = data?.group_progress_1
const parsedValue = rawValue === undefined || rawValue === null ? null : parseFloat(rawValue)
return Number.isFinite(parsedValue) && parsedValue >= 0 ? parsedValue : 0
})()
const importantProgress = (() => {
const rawValue = data?.group_progress_2
const parsedValue = rawValue === undefined || rawValue === null ? null : parseFloat(rawValue)
return Number.isFinite(parsedValue) && parsedValue >= 0 ? parsedValue : 0
})()
const othersProgress = (() => {
const rawValue = data?.group_progress_0
const parsedValue = rawValue === undefined || rawValue === null ? null : parseFloat(rawValue)
return Number.isFinite(parsedValue) && parsedValue >= 0 ? parsedValue : 0
})()
// Используем общий прогресс из API данных
const displayOverallProgress = overallProgress
return (
{/* Кнопка "Приоритеты" в правом верхнем углу */}
{onNavigate && (
)}
{/* Общий прогресс - большой круг в центре */}
onNavigate && onNavigate('full')}>
{/* Подсказка при наведении */}