diff --git a/VERSION b/VERSION index c1b6143..6ed7776 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.8.11 +4.9.0 diff --git a/play-life-web/package.json b/play-life-web/package.json index aa3eb09..004a359 100644 --- a/play-life-web/package.json +++ b/play-life-web/package.json @@ -1,6 +1,6 @@ { "name": "play-life-web", - "version": "4.8.11", + "version": "4.9.0", "type": "module", "scripts": { "dev": "vite", diff --git a/play-life-web/src/App.jsx b/play-life-web/src/App.jsx index f7f5f8c..d1ad829 100644 --- a/play-life-web/src/App.jsx +++ b/play-life-web/src/App.jsx @@ -89,6 +89,9 @@ function AppContent() { // Параметры для навигации между вкладками const [tabParams, setTabParams] = useState({}) + + // Предыдущий таб для возврата из модальных окон + const [previousTab, setPreviousTab] = useState(null) // Модальное окно выбора типа задачи const [showAddModal, setShowAddModal] = useState(false) @@ -704,15 +707,29 @@ function AppContent() { if ((activeTab === 'task-form' || activeTab === 'test') && tab === 'tasks') { fetchTasksData(true) } + // Сохраняем предыдущий таб при открытии wishlist-form или wishlist-detail + if ((tab === 'wishlist-form' || tab === 'wishlist-detail') && activeTab !== tab) { + setPreviousTab(activeTab) + } + // Обновляем список желаний при возврате из экрана редактирования - if (activeTab === 'wishlist-form' && tab === 'wishlist') { + if (activeTab === 'wishlist-form' && tab !== 'wishlist-form') { // Сохраняем boardId из параметров или текущих tabParams const savedBoardId = params.boardId || tabParams.boardId // Параметры уже установлены в строке 649, но мы можем их обновить, чтобы сохранить boardId - if (savedBoardId) { + if (savedBoardId && tab === 'wishlist') { setTabParams(prev => ({ ...prev, boardId: savedBoardId })) } - setWishlistRefreshTrigger(prev => prev + 1) + if (tab === 'wishlist') { + setWishlistRefreshTrigger(prev => prev + 1) + } + } + + // Обновляем список желаний при возврате из экрана детализации + if (activeTab === 'wishlist-detail' && tab !== 'wishlist-detail') { + if (tab === 'wishlist') { + setWishlistRefreshTrigger(prev => prev + 1) + } } // Загрузка данных произойдет в useEffect при изменении activeTab } @@ -978,17 +995,6 @@ function AppContent() { )} - {loadedTabs['wishlist-detail'] && ( -
- setWishlistRefreshTrigger(prev => prev + 1)} - /> -
- )} {loadedTabs['board-form'] && (
diff --git a/play-life-web/src/components/TaskDetail.css b/play-life-web/src/components/TaskDetail.css index 73ed635..22a21bc 100644 --- a/play-life-web/src/components/TaskDetail.css +++ b/play-life-web/src/components/TaskDetail.css @@ -29,7 +29,7 @@ display: flex; align-items: center; justify-content: space-between; - padding: 1rem 1.5rem; + padding: 1rem 1.5rem 0.5rem 1.5rem; } .task-detail-close-button { @@ -54,7 +54,7 @@ } .task-detail-modal-content { - padding: 0 1.5rem 1.5rem 1.5rem; + padding: 0.5rem 1.5rem 1.5rem 1.5rem; overflow-y: auto; flex: 1; } @@ -64,9 +64,29 @@ font-weight: 600; color: #1f2937; margin: 0; - display: flex; - align-items: center; - gap: 0.5rem; + flex: 1; + min-width: 0; + word-wrap: break-word; + word-break: break-word; + transition: opacity 0.2s; + line-height: 1.5; +} + +.task-detail-title:hover { + opacity: 0.7; +} + +.task-detail-edit-icon { + color: #6b7280; + flex-shrink: 0; + transition: color 0.2s; + display: inline-block; + vertical-align: middle; + margin-left: 0.5rem; +} + +.task-detail-title:hover .task-detail-edit-icon { + color: #1f2937; } .task-detail-auto-complete-icon { diff --git a/play-life-web/src/components/TaskDetail.jsx b/play-life-web/src/components/TaskDetail.jsx index 9dab1ad..c590fba 100644 --- a/play-life-web/src/components/TaskDetail.jsx +++ b/play-life-web/src/components/TaskDetail.jsx @@ -731,10 +731,30 @@ function TaskDetail({ taskId, onClose, onRefresh, onTaskCompleted, onNavigate })
e.stopPropagation()}>
-

+

{ + onClose?.() + onNavigate?.('task-form', { taskId: taskId }) + } : undefined} + style={{ cursor: taskDetail ? 'pointer' : 'default' }} + > {loading ? 'Загрузка...' : error ? 'Ошибка' : taskDetail ? ( <> {task.name} + + + ) : 'Задача'}

diff --git a/play-life-web/src/components/TaskList.jsx b/play-life-web/src/components/TaskList.jsx index ed14d1d..810a0fd 100644 --- a/play-life-web/src/components/TaskList.jsx +++ b/play-life-web/src/components/TaskList.jsx @@ -30,7 +30,7 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, error, onRetry // TaskList не инициирует загрузку самостоятельно const handleTaskClick = (task) => { - onNavigate?.('task-form', { taskId: task.id }) + setSelectedTaskForDetail(task.id) } const handleCheckmarkClick = async (task, e) => { diff --git a/play-life-web/src/components/Wishlist.jsx b/play-life-web/src/components/Wishlist.jsx index ffa6e2a..9d8d606 100644 --- a/play-life-web/src/components/Wishlist.jsx +++ b/play-life-web/src/components/Wishlist.jsx @@ -2,6 +2,7 @@ import React, { useState, useEffect, useRef } from 'react' import { useAuth } from './auth/AuthContext' import BoardSelector from './BoardSelector' import LoadingError from './LoadingError' +import WishlistDetail from './WishlistDetail' import './Wishlist.css' const API_URL = '/api/wishlist' @@ -43,6 +44,7 @@ function Wishlist({ onNavigate, refreshTrigger = 0, isActive = false, initialBoa const [completedExpanded, setCompletedExpanded] = useState(false) const [completedLoading, setCompletedLoading] = useState(false) const [selectedItem, setSelectedItem] = useState(null) + const [selectedWishlistForDetail, setSelectedWishlistForDetail] = useState(null) const fetchingRef = useRef(false) const fetchingCompletedRef = useRef(false) const initialFetchDoneRef = useRef(false) @@ -406,7 +408,11 @@ function Wishlist({ onNavigate, refreshTrigger = 0, isActive = false, initialBoa } const handleItemClick = (item) => { - onNavigate?.('wishlist-detail', { wishlistId: item.id, boardId: selectedBoardId }) + setSelectedWishlistForDetail(item.id) + } + + const handleCloseDetail = () => { + setSelectedWishlistForDetail(null) } const handleMenuClick = (item, e) => { @@ -697,6 +703,22 @@ function Wishlist({ onNavigate, refreshTrigger = 0, isActive = false, initialBoa
)} + + {/* Модальное окно для деталей желания */} + {selectedWishlistForDetail && ( + { + await fetchItems() + if (completedExpanded) { + await fetchCompleted() + } + }} + onClose={handleCloseDetail} + /> + )}
) } diff --git a/play-life-web/src/components/WishlistDetail.css b/play-life-web/src/components/WishlistDetail.css index c0cb629..6b75fc7 100644 --- a/play-life-web/src/components/WishlistDetail.css +++ b/play-life-web/src/components/WishlistDetail.css @@ -1,49 +1,102 @@ -.wishlist-detail { - padding: 1rem; - max-width: 800px; - margin: 0 auto; - position: relative; -} - -.close-x-button { +/* Модальное окно */ +.wishlist-detail-modal-overlay { position: fixed; - top: 1rem; - right: 1rem; - background: rgba(255, 255, 255, 0.9); - border: none; - font-size: 1.5rem; - color: #7f8c8d; - cursor: pointer; - width: 40px; - height: 40px; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); display: flex; align-items: center; justify-content: center; - border-radius: 50%; - transition: background-color 0.2s, color 0.2s; - z-index: 1600; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + z-index: 1700; + padding: 1rem; } -.close-x-button:hover { - background-color: #ffffff; - color: #2c3e50; -} - -.wishlist-detail h2 { - font-size: 1.5rem; - font-weight: 600; - color: #1f2937; - margin: 0 0 0.75rem 0; -} - -.wishlist-detail-content { +.wishlist-detail-modal { background: white; border-radius: 0.5rem; - padding: 1rem; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); + max-width: 500px; + width: 100%; + max-height: 90vh; + display: flex; + flex-direction: column; + overflow: hidden; } +.wishlist-detail-modal-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1rem 1.5rem 0.5rem 1.5rem; +} + +.wishlist-detail-title { + font-size: 1.25rem; + font-weight: 600; + color: #1f2937; + margin: 0; + flex: 1; + min-width: 0; + word-wrap: break-word; + word-break: break-word; + transition: opacity 0.2s; + line-height: 1.5; +} + +.wishlist-detail-title:hover { + opacity: 0.7; +} + +.wishlist-detail-edit-icon { + color: #6b7280; + flex-shrink: 0; + transition: color 0.2s; + display: inline-block; + vertical-align: middle; + margin-left: 0.5rem; +} + +.wishlist-detail-title:hover .wishlist-detail-edit-icon { + color: #1f2937; +} + +.wishlist-detail-close-button { + background: none; + border: none; + font-size: 1.5rem; + color: #6b7280; + cursor: pointer; + padding: 0; + width: 2rem; + height: 2rem; + display: flex; + align-items: center; + justify-content: center; + border-radius: 0.25rem; + transition: all 0.2s; + flex-shrink: 0; + margin-left: 1rem; +} + +.wishlist-detail-close-button:hover { + background: #f3f4f6; + color: #1f2937; +} + +.wishlist-detail-modal-content { + padding: 0.5rem 1.5rem 1.5rem 1.5rem; + overflow-y: auto; + flex: 1; +} + +.wishlist-detail { + /* Оставляем для обратной совместимости */ +} + +/* Контент теперь внутри модального окна, убираем лишние стили */ + .wishlist-detail-image { width: 100%; aspect-ratio: 5 / 6; diff --git a/play-life-web/src/components/WishlistDetail.jsx b/play-life-web/src/components/WishlistDetail.jsx index 42ec3d3..82f129e 100644 --- a/play-life-web/src/components/WishlistDetail.jsx +++ b/play-life-web/src/components/WishlistDetail.jsx @@ -8,7 +8,7 @@ import './TaskList.css' const API_URL = '/api/wishlist' -function WishlistDetail({ wishlistId, onNavigate, onRefresh, boardId }) { +function WishlistDetail({ wishlistId, onNavigate, onRefresh, boardId, onClose, previousTab }) { const { authFetch, user } = useAuth() const [wishlistItem, setWishlistItem] = useState(null) const [loading, setLoading] = useState(true) @@ -51,6 +51,7 @@ function WishlistDetail({ wishlistId, onNavigate, onRefresh, boardId }) { }, [wishlistId, fetchWishlistDetail]) const handleEdit = () => { + onClose?.() onNavigate?.('wishlist-form', { wishlistId: wishlistId, boardId: boardId }) } @@ -159,6 +160,27 @@ function WishlistDetail({ wishlistId, onNavigate, onRefresh, boardId }) { setSelectedTaskForDetail(null) } + const handleClose = () => { + // Используем onClose если передан, иначе возвращаемся на wishlist + if (onClose) { + onClose() + } else { + // Возвращаемся на предыдущий таб, если он был сохранен, иначе на wishlist + if (previousTab) { + // Сохраняем boardId при возврате на предыдущий таб + if (boardId) { + onNavigate?.(previousTab, { boardId }) + } else { + onNavigate?.(previousTab) + } + } else if (boardId) { + onNavigate?.('wishlist', { boardId }) + } else { + onNavigate?.('wishlist') + } + } + } + const handleTaskCompleted = () => { setToastMessage({ text: 'Задача выполнена', type: 'success' }) // После выполнения задачи желание тоже завершается, перенаправляем на список @@ -303,26 +325,51 @@ function WishlistDetail({ wishlistId, onNavigate, onRefresh, boardId }) { return ( -
- - {loadingWishlist && ( -
-
-
-
Загрузка...
-
+
+
e.stopPropagation()}> +
+

+ {loadingWishlist ? 'Загрузка...' : error ? 'Ошибка' : wishlistItem ? wishlistItem.name : 'Желание'} + {wishlistItem && ( + + + + )} +

+
- )} -

{wishlistItem ? wishlistItem.name : 'Желание'}

-
- {error && ( - - )} +
+ {loadingWishlist && ( +
+
+
+
Загрузка...
+
+
+ )} - {!error && wishlistItem && ( + {error && !loadingWishlist && ( + + )} + + {!loadingWishlist && !error && wishlistItem && ( <> {/* Изображение */} {wishlistItem.image_url && ( @@ -492,31 +539,32 @@ function WishlistDetail({ wishlistId, onNavigate, onRefresh, boardId }) { )} )} - + + )} +
+ + {toastMessage && ( + setToastMessage(null)} + /> + )} + + {/* Модальное окно для деталей задачи */} + {selectedTaskForDetail && ( + { + fetchWishlistDetail() + if (onRefresh) onRefresh() + }} + onTaskCompleted={handleTaskCompleted} + onNavigate={onNavigate} + /> )}
- - {toastMessage && ( - setToastMessage(null)} - /> - )} - - {/* Модальное окно для деталей задачи */} - {selectedTaskForDetail && ( - { - fetchWishlistDetail() - if (onRefresh) onRefresh() - }} - onTaskCompleted={handleTaskCompleted} - onNavigate={onNavigate} - /> - )}
) }