From 08d86eb5f540409b1f89069997f1828a8400a904 Mon Sep 17 00:00:00 2001 From: poignatov Date: Tue, 20 Jan 2026 20:40:38 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B8=D0=BD=D1=82=D0=B5=D1=80=D1=84=D0=B5?= =?UTF-8?q?=D0=B9=D1=81=D0=B0=20=D0=B7=D0=B0=D0=B4=D0=B0=D1=87=20=D0=B8=20?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D1=8F=203.21.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VERSION | 2 +- play-life-web/package.json | 2 +- play-life-web/src/App.jsx | 47 +++++++++--- play-life-web/src/components/CurrentWeek.jsx | 1 + play-life-web/src/components/TaskForm.jsx | 5 -- play-life-web/src/components/TaskList.jsx | 78 ++++++++++---------- play-life-web/src/components/Wishlist.jsx | 4 +- 7 files changed, 80 insertions(+), 59 deletions(-) diff --git a/VERSION b/VERSION index c253e50..6075c9a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.20.4 +3.21.0 diff --git a/play-life-web/package.json b/play-life-web/package.json index 93f1b6c..f9fc521 100644 --- a/play-life-web/package.json +++ b/play-life-web/package.json @@ -1,6 +1,6 @@ { "name": "play-life-web", - "version": "3.20.4", + "version": "3.21.0", "type": "module", "scripts": { "dev": "vite", diff --git a/play-life-web/src/App.jsx b/play-life-web/src/App.jsx index 10a4359..41dd0ac 100644 --- a/play-life-web/src/App.jsx +++ b/play-life-web/src/App.jsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback, useRef } from 'react' +import React, { useState, useEffect, useCallback, useRef } from 'react' import CurrentWeek from './components/CurrentWeek' import FullStatistics from './components/FullStatistics' import ProjectPriorityManager from './components/ProjectPriorityManager' @@ -120,6 +120,10 @@ function AppContent() { const [wordsRefreshTrigger, setWordsRefreshTrigger] = useState(0) const [wishlistRefreshTrigger, setWishlistRefreshTrigger] = useState(0) + // Состояние для сохранения позиции скролла каждого таба + const [scrollPositions, setScrollPositions] = useState({}) + + // Восстанавливаем последний выбранный таб после перезагрузки const [isInitialized, setIsInitialized] = useState(false) @@ -624,15 +628,24 @@ function AppContent() { setTabParams({}) updateUrl('full', {}, activeTab) } else if (tab !== activeTab || tab === 'task-form' || tab === 'wishlist-form') { + // Сохраняем позицию скролла для текущего таба перед переходом + const scrollContainer = document.querySelector('.flex-1.overflow-y-auto') + if (scrollContainer && mainTabs.includes(activeTab)) { + setScrollPositions(prev => ({ + ...prev, + [activeTab]: scrollContainer.scrollTop + })) + } + // Для task-form и wishlist-form всегда обновляем параметры, даже если это тот же таб markTabAsLoaded(tab) - + // Определяем, является ли текущий таб глубоким const isCurrentTabDeep = deepTabs.includes(activeTab) const isNewTabDeep = deepTabs.includes(tab) const isCurrentTabMain = mainTabs.includes(activeTab) const isNewTabMain = mainTabs.includes(tab) - + { // Для task-form и wishlist-form явно удаляем параметры, только если нет никаких параметров // task-form может иметь taskId (редактирование), wishlistId (создание из желания), или returnTo (возврат после создания) @@ -660,7 +673,7 @@ function AppContent() { } } } - + setActiveTab(tab) if (tab === 'current') { setSelectedProject(null) @@ -696,19 +709,19 @@ function AppContent() { // Загружаем данные при открытии таба (когда таб становится активным) const prevActiveTabRef = useRef(null) const lastLoadedTabRef = useRef(null) // Отслеживаем последний загруженный таб, чтобы избежать двойной загрузки - + useEffect(() => { if (!activeTab || !loadedTabs[activeTab]) return - + const isFirstLoad = !tabsInitializedRef.current[activeTab] const isReturningToTab = prevActiveTabRef.current !== null && prevActiveTabRef.current !== activeTab - + // Проверяем, не загружали ли мы уже этот таб в этом рендере const tabKey = `${activeTab}-${isFirstLoad ? 'first' : 'return'}` if (lastLoadedTabRef.current === tabKey) { return // Уже загружали } - + if (isFirstLoad) { // Первая загрузка таба lastLoadedTabRef.current = tabKey @@ -718,9 +731,25 @@ function AppContent() { lastLoadedTabRef.current = tabKey loadTabData(activeTab, true) } - + prevActiveTabRef.current = activeTab }, [activeTab, loadedTabs, loadTabData]) + + // Восстанавливаем позицию скролла при возвращении на таб + useEffect(() => { + if (mainTabs.includes(activeTab) && scrollPositions[activeTab] !== undefined) { + // Используем setTimeout, чтобы DOM успел обновиться + const timeoutId = setTimeout(() => { + const scrollContainer = document.querySelector('.flex-1.overflow-y-auto') + if (scrollContainer) { + scrollContainer.scrollTop = scrollPositions[activeTab] + } + }, 0) + + return () => clearTimeout(timeoutId) + } + }, [activeTab, scrollPositions]) + // Определяем общее состояние загрузки и ошибок для кнопки Refresh const isAnyLoading = currentWeekLoading || fullStatisticsLoading || prioritiesLoading || isRefreshing diff --git a/play-life-web/src/components/CurrentWeek.jsx b/play-life-web/src/components/CurrentWeek.jsx index a07b35f..0b4b6ee 100644 --- a/play-life-web/src/components/CurrentWeek.jsx +++ b/play-life-web/src/components/CurrentWeek.jsx @@ -1,3 +1,4 @@ +import React from 'react' import ProjectProgressBar from './ProjectProgressBar' import LoadingError from './LoadingError' import { getAllProjectsSorted, getProjectColor } from '../utils/projectUtils' diff --git a/play-life-web/src/components/TaskForm.jsx b/play-life-web/src/components/TaskForm.jsx index 890052f..580615c 100644 --- a/play-life-web/src/components/TaskForm.jsx +++ b/play-life-web/src/components/TaskForm.jsx @@ -177,7 +177,6 @@ function TaskForm({ onNavigate, taskId, wishlistId, isTest: isTestFromProps = fa // Парсим repetition_period если он есть setRepetitionMode('after') const periodStr = data.task.repetition_period.trim() - console.log('Parsing repetition_period:', periodStr, 'Full task data:', data.task) // Отладка // PostgreSQL может возвращать INTERVAL в разных форматах: // - "1 day" / "1 days" / "10 days" @@ -194,8 +193,6 @@ function TaskForm({ onNavigate, taskId, wishlistId, isTest: isTestFromProps = fa const value = parseInt(match[1], 10) const unit = match[2].toLowerCase() - console.log('Matched value:', value, 'unit:', unit) // Отладка - if (!isNaN(value) && value >= 0) { // Преобразуем единицы PostgreSQL в наш формат if (unit.startsWith('minute')) { @@ -293,8 +290,6 @@ function TaskForm({ onNavigate, taskId, wishlistId, isTest: isTestFromProps = fa console.log('Failed to parse repetition_period:', periodStr) // Отладка setRepetitionPeriodValue('') setRepetitionPeriodType('day') - } else { - console.log('Successfully parsed repetition_period - value will be set') // Отладка } } else { console.log('No repetition_period or repetition_date in task data') // Отладка diff --git a/play-life-web/src/components/TaskList.jsx b/play-life-web/src/components/TaskList.jsx index a64aff1..a8f3227 100644 --- a/play-life-web/src/components/TaskList.jsx +++ b/play-life-web/src/components/TaskList.jsx @@ -556,10 +556,43 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, error, onRetry onClick={(e) => handleCheckmarkClick(task, e)} title={isTest ? 'Запустить тест' : (showDetailOnCheckmark ? 'Открыть детали' : 'Выполнить задачу')} > - - - - + {isTest ? ( + + + + + ) : isWishlist ? ( + + + + + + + + ) : ( + + + + + )}
@@ -569,43 +602,6 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, error, onRetry (+{task.subtasks_count}) )} - {isWishlist && ( - - - - - - - - )} - {isTest && ( - - - - - )} {hasProgression && ( { if (!initialFetchDoneRef.current) { initialFetchDoneRef.current = true - + // Загружаем доски из кэша const boardsCacheLoaded = loadBoardsFromCache() if (boardsCacheLoaded) { setBoardsLoading(false) } - + // Загружаем доски с сервера fetchBoards() }