diff --git a/VERSION b/VERSION
index e43686a..c8e38b6 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.8.6
+2.9.0
diff --git a/play-life-web/package.json b/play-life-web/package.json
index f99181f..91f8794 100644
--- a/play-life-web/package.json
+++ b/play-life-web/package.json
@@ -1,6 +1,6 @@
{
"name": "play-life-web",
- "version": "2.6.1",
+ "version": "2.9.0",
"type": "module",
"scripts": {
"dev": "vite",
diff --git a/play-life-web/src/App.jsx b/play-life-web/src/App.jsx
index 0037d63..92abfd8 100644
--- a/play-life-web/src/App.jsx
+++ b/play-life-web/src/App.jsx
@@ -8,6 +8,8 @@ import TestConfigSelection from './components/TestConfigSelection'
import AddConfig from './components/AddConfig'
import TestWords from './components/TestWords'
import Profile from './components/Profile'
+import TaskList from './components/TaskList'
+import TaskForm from './components/TaskForm'
import { AuthProvider, useAuth } from './components/auth/AuthContext'
import AuthScreen from './components/auth/AuthScreen'
@@ -42,6 +44,8 @@ function AppContent() {
'test-config': false,
'add-config': false,
test: false,
+ tasks: false,
+ 'task-form': false,
profile: false,
})
@@ -55,6 +59,8 @@ function AppContent() {
'test-config': false,
'add-config': false,
test: false,
+ tasks: false,
+ 'task-form': false,
profile: false,
})
@@ -64,16 +70,19 @@ function AppContent() {
// Кеширование данных
const [currentWeekData, setCurrentWeekData] = useState(null)
const [fullStatisticsData, setFullStatisticsData] = useState(null)
+ const [tasksData, setTasksData] = useState(null)
// Состояния загрузки для каждого таба (показываются только при первой загрузке)
const [currentWeekLoading, setCurrentWeekLoading] = useState(false)
const [fullStatisticsLoading, setFullStatisticsLoading] = useState(false)
const [prioritiesLoading, setPrioritiesLoading] = useState(false)
+ const [tasksLoading, setTasksLoading] = useState(false)
// Состояния фоновой загрузки (не показываются визуально)
const [currentWeekBackgroundLoading, setCurrentWeekBackgroundLoading] = useState(false)
const [fullStatisticsBackgroundLoading, setFullStatisticsBackgroundLoading] = useState(false)
const [prioritiesBackgroundLoading, setPrioritiesBackgroundLoading] = useState(false)
+ const [tasksBackgroundLoading, setTasksBackgroundLoading] = useState(false)
// Ошибки
const [currentWeekError, setCurrentWeekError] = useState(null)
@@ -94,7 +103,7 @@ function AppContent() {
try {
const savedTab = window.localStorage?.getItem('activeTab')
- const validTabs = ['current', 'priorities', 'full', 'words', 'add-words', 'test-config', 'add-config', 'test', 'profile']
+ const validTabs = ['current', 'priorities', 'full', 'words', 'add-words', 'test-config', 'add-config', 'test', 'tasks', 'task-form', 'profile']
if (savedTab && validTabs.includes(savedTab)) {
setActiveTab(savedTab)
setLoadedTabs(prev => ({ ...prev, [savedTab]: true }))
@@ -194,6 +203,30 @@ function AppContent() {
}
}, [authFetch])
+ const fetchTasksData = useCallback(async (isBackground = false) => {
+ try {
+ if (isBackground) {
+ setTasksBackgroundLoading(true)
+ } else {
+ setTasksLoading(true)
+ }
+ const response = await authFetch('/api/tasks')
+ if (!response.ok) {
+ throw new Error('Ошибка загрузки данных')
+ }
+ const jsonData = await response.json()
+ setTasksData(jsonData)
+ } catch (err) {
+ console.error('Ошибка загрузки списка задач:', err)
+ } finally {
+ if (isBackground) {
+ setTasksBackgroundLoading(false)
+ } else {
+ setTasksLoading(false)
+ }
+ }
+ }, [authFetch])
+
// Используем ref для отслеживания инициализации табов (чтобы избежать лишних пересозданий функции)
const tabsInitializedRef = useRef({
current: false,
@@ -204,6 +237,8 @@ function AppContent() {
'test-config': false,
'add-config': false,
test: false,
+ tasks: false,
+ 'task-form': false,
profile: false,
})
@@ -211,6 +246,7 @@ function AppContent() {
const cacheRef = useRef({
current: null,
full: null,
+ tasks: null,
})
// Обновляем ref при изменении данных
@@ -222,6 +258,10 @@ function AppContent() {
cacheRef.current.full = fullStatisticsData
}, [fullStatisticsData])
+ useEffect(() => {
+ cacheRef.current.tasks = tasksData
+ }, [tasksData])
+
// Функция для загрузки данных таба
const loadTabData = useCallback((tab, isBackground = false) => {
if (tab === 'current') {
@@ -275,8 +315,21 @@ function AppContent() {
// Возврат на таб - фоновая загрузка
setTestConfigRefreshTrigger(prev => prev + 1)
}
+ } else if (tab === 'tasks') {
+ const hasCache = cacheRef.current.tasks !== null
+ const isInitialized = tabsInitializedRef.current.tasks
+
+ if (!isInitialized) {
+ // Первая загрузка таба - загружаем с индикатором
+ fetchTasksData(false)
+ tabsInitializedRef.current.tasks = true
+ setTabsInitialized(prev => ({ ...prev, tasks: true }))
+ } else if (hasCache && isBackground) {
+ // Возврат на таб с кешем - фоновая загрузка
+ fetchTasksData(true)
+ }
}
- }, [fetchCurrentWeekData, fetchFullStatisticsData])
+ }, [fetchCurrentWeekData, fetchFullStatisticsData, fetchTasksData])
// Функция для обновления всех данных (для кнопки Refresh, если она есть)
const refreshAllData = useCallback(async () => {
@@ -325,13 +378,19 @@ function AppContent() {
if (tab === 'full' && activeTab === 'full') {
// При повторном клике на "Полная статистика" сбрасываем выбранный проект
setSelectedProject(null)
- } else if (tab !== activeTab) {
+ } else if (tab !== activeTab || tab === 'task-form') {
+ // Для task-form всегда обновляем параметры, даже если это тот же таб
markTabAsLoaded(tab)
// Сбрасываем tabParams при переходе с add-config на другой таб
if (activeTab === 'add-config' && tab !== 'add-config') {
setTabParams({})
} else {
- setTabParams(params)
+ // Для task-form явно удаляем taskId, если он undefined
+ if (tab === 'task-form' && params.taskId === undefined) {
+ setTabParams({})
+ } else {
+ setTabParams(params)
+ }
}
setActiveTab(tab)
if (tab === 'current') {
@@ -341,6 +400,11 @@ function AppContent() {
if (activeTab === 'add-words' && tab === 'words') {
setWordsRefreshTrigger(prev => prev + 1)
}
+ // Обновляем список задач при возврате из экрана редактирования
+ // Используем фоновую загрузку, чтобы не показывать индикатор загрузки
+ if (activeTab === 'task-form' && tab === 'tasks') {
+ fetchTasksData(true)
+ }
// Загрузка данных произойдет в useEffect при изменении activeTab
}
}
@@ -393,7 +457,7 @@ function AppContent() {
}, [activeTab])
// Определяем, нужно ли скрывать нижнюю панель (для fullscreen экранов)
- const isFullscreenTab = activeTab === 'test' || activeTab === 'add-words' || activeTab === 'add-config'
+ const isFullscreenTab = activeTab === 'test' || activeTab === 'add-words' || activeTab === 'add-config' || activeTab === 'task-form'
return (
@@ -493,6 +557,28 @@ function AppContent() {
)}
+ {loadedTabs.tasks && (
+
+ fetchTasksData(isBackground)}
+ />
+
+ )}
+
+ {loadedTabs['task-form'] && (
+
+
+
+ )}
+
{loadedTabs.profile && (
@@ -546,6 +632,25 @@ function AppContent() {
)}
+