Files
play-life/play-life-web/src/components/FullStatistics.jsx

203 lines
8.6 KiB
React
Raw Normal View History

import React, { useState, useEffect, useCallback } from 'react'
import WeekProgressChart from './WeekProgressChart'
import LoadingError from './LoadingError'
import TodayEntriesList from './TodayEntriesList'
import { getAllProjectsSorted } from '../utils/projectUtils'
import './Integrations.css'
// Экспортируем для обратной совместимости (если используется в других местах)
export { getProjectColorByIndex } from '../utils/projectUtils'
// Функция для получения дат текущей недели (понедельник - воскресенье)
const getCurrentWeekDates = () => {
const now = new Date()
const day = now.getDay()
// Вычисляем разницу до понедельника (1 = понедельник, 0 = воскресенье)
const diff = day === 0 ? -6 : 1 - day
const monday = new Date(now)
monday.setDate(now.getDate() + diff)
const dates = []
for (let i = 0; i < 7; i++) {
const date = new Date(monday)
date.setDate(monday.getDate() + i)
dates.push(date)
}
return dates
}
// Функция для форматирования даты в YYYY-MM-DD
const formatDate = (date) => {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
}
// Названия дней недели
const dayNames = ['пн', 'вт', 'ср', 'чт', 'пт', 'сб', 'вс']
function FullStatistics({ selectedProject, onClearSelection, data, loading, error, onRetry, currentWeekData, onNavigate, todayEntries, todayEntriesLoading, todayEntriesError, onRetryTodayEntries, fetchTodayEntries, activeTab }) {
const [selectedDate, setSelectedDate] = useState(null)
const prevActiveTabRef = React.useRef(activeTab)
const componentJustOpenedRef = React.useRef(false)
// Получаем даты текущей недели
const weekDates = getCurrentWeekDates()
// Определяем текущий день (используем useMemo для стабильности)
const today = React.useMemo(() => {
const date = new Date()
date.setHours(0, 0, 0, 0)
return date
}, [])
// Получаем строковое представление сегодняшней даты
const todayDateStr = React.useMemo(() => formatDate(today), [today])
// Фильтруем только прошедшие дни (включая сегодня)
const pastDays = weekDates.filter((date) => {
const dateOnly = new Date(date)
dateOnly.setHours(0, 0, 0, 0)
return dateOnly <= today
})
// Отслеживаем выход с экрана и сбрасываем выбор дня
useEffect(() => {
// Если мы были на экране full и перешли на другой экран - сбрасываем выбор дня
if (prevActiveTabRef.current === 'full' && activeTab !== 'full') {
setSelectedDate(todayDateStr)
}
}, [activeTab, todayDateStr])
// Инициализируем выбранную дату текущим днем при первом рендере
// Также проверяем, что выбранная дата все еще в списке доступных дней
useEffect(() => {
const pastDaysDateStrs = pastDays.map(date => formatDate(date))
if (selectedDate === null) {
// Первая инициализация - устанавливаем текущий день
setSelectedDate(todayDateStr)
} else if (!pastDaysDateStrs.includes(selectedDate)) {
// Если выбранная дата больше не в списке доступных (например, прошла неделя)
// Сбрасываем на текущий день
setSelectedDate(todayDateStr)
}
}, [selectedDate, todayDateStr, pastDays])
// Отслеживаем открытие компонента
useEffect(() => {
// Когда компонент открывается (activeTab становится 'full'), помечаем это
if (activeTab === 'full' && prevActiveTabRef.current !== 'full') {
componentJustOpenedRef.current = true
}
prevActiveTabRef.current = activeTab
}, [activeTab])
// Загружаем данные при изменении selectedDate или selectedProject
useEffect(() => {
if (selectedDate && fetchTodayEntries) {
// Если компонент только что открылся - используем фоновую загрузку
if (componentJustOpenedRef.current) {
componentJustOpenedRef.current = false
fetchTodayEntries(true, selectedProject, selectedDate)
} else {
// При изменении даты или проекта - используем обычную загрузку (не фоновую)
fetchTodayEntries(false, selectedProject, selectedDate)
}
}
}, [selectedDate, selectedProject, fetchTodayEntries])
// Обработчик выбора дня
const handleDaySelect = useCallback((date) => {
const dateStr = formatDate(date)
setSelectedDate(dateStr)
// Загрузка данных произойдет автоматически через useEffect выше
}, [])
if (error && (!data || data.length === 0) && !loading) {
return <LoadingError onRetry={onRetry} />
}
return (
<div className="max-w-2xl mx-auto">
{onNavigate && (
<button
onClick={() => {
// Сбрасываем выбор дня перед выходом с экрана
setSelectedDate(todayDateStr)
window.history.back()
}}
className="close-x-button"
title="Закрыть"
>
</button>
)}
{loading && (!data || data.length === 0) && (!todayEntries || (Array.isArray(todayEntries) && todayEntries.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>
) : (!data || data.length === 0) && !todayEntries ? (
<div className="flex justify-center items-center py-16">
<div className="text-gray-500 text-lg">Нет данных для отображения</div>
</div>
) : (
<>
<WeekProgressChart data={data} allProjectsSorted={getAllProjectsSorted(data)} currentWeekData={currentWeekData} selectedProject={selectedProject} />
{/* Чипсы дней недели */}
{pastDays.length > 0 && (
<div className="mt-3 mb-2">
<div className="flex flex-wrap gap-2.5">
{pastDays.map((date, index) => {
const dateStr = formatDate(date)
const dayOfWeek = index + 1 // 1 = понедельник
const isSelected = selectedDate === dateStr
const isToday = dateStr === todayDateStr
return (
<button
key={dateStr}
onClick={() => handleDaySelect(date)}
className={`
h-9 px-4 rounded-lg text-sm font-semibold
transition-all duration-200 ease-in-out
flex items-center justify-center
${
isSelected
? 'bg-white text-gray-900 shadow-sm border border-gray-200'
: 'bg-transparent text-gray-700 border border-gray-300 hover:border-gray-400'
}
${isToday && !isSelected ? 'ring-2 ring-indigo-200 border-indigo-300' : ''}
active:scale-95
`}
>
{dayNames[dayOfWeek - 1]}
</button>
)
})}
</div>
</div>
)}
<TodayEntriesList
data={todayEntries}
loading={todayEntriesLoading}
error={todayEntriesError}
onRetry={() => fetchTodayEntries && fetchTodayEntries(false, selectedProject, selectedDate)}
onDelete={() => fetchTodayEntries && fetchTodayEntries(false, selectedProject, selectedDate)}
/>
</>
)}
</div>
)
}
export default FullStatistics