6.15.6: Открытие редактирования задачи из статистики
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m6s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m6s
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "play-life-web",
|
"name": "play-life-web",
|
||||||
"version": "6.15.5",
|
"version": "6.15.6",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState, useEffect, useRef } from 'react'
|
||||||
import LoadingError from './LoadingError'
|
import LoadingError from './LoadingError'
|
||||||
import { useAuth } from './auth/AuthContext'
|
import { useAuth } from './auth/AuthContext'
|
||||||
|
import TaskDetail from './TaskDetail'
|
||||||
|
|
||||||
// Функция для форматирования скорa (аналогично formatScore из TaskDetail)
|
// Функция для форматирования скорa (аналогично formatScore из TaskDetail)
|
||||||
const formatScore = (num) => {
|
const formatScore = (num) => {
|
||||||
@@ -134,7 +135,56 @@ const formatEntryText = (text, nodes) => {
|
|||||||
function TodayEntriesList({ data, loading, error, onRetry, onDelete }) {
|
function TodayEntriesList({ data, loading, error, onRetry, onDelete }) {
|
||||||
const { authFetch } = useAuth()
|
const { authFetch } = useAuth()
|
||||||
const [deletingIds, setDeletingIds] = useState(new Set())
|
const [deletingIds, setDeletingIds] = useState(new Set())
|
||||||
const [clearingAutoCompleteIds, setClearingAutoCompleteIds] = useState(new Set())
|
const [selectedTaskId, setSelectedTaskId] = useState(null)
|
||||||
|
const selectedTaskIdRef = useRef(null)
|
||||||
|
const historyPushedRef = useRef(false)
|
||||||
|
|
||||||
|
// Обновляем ref при изменении selectedTaskId
|
||||||
|
useEffect(() => {
|
||||||
|
selectedTaskIdRef.current = selectedTaskId
|
||||||
|
}, [selectedTaskId])
|
||||||
|
|
||||||
|
// Управление историей браузера при открытии/закрытии TaskDetail
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedTaskId && !historyPushedRef.current) {
|
||||||
|
window.history.pushState({ modalOpen: true, type: 'task-detail-statistics' }, '', window.location.href)
|
||||||
|
historyPushedRef.current = true
|
||||||
|
} else if (!selectedTaskId) {
|
||||||
|
historyPushedRef.current = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!selectedTaskId) return
|
||||||
|
|
||||||
|
const handlePopState = () => {
|
||||||
|
if (selectedTaskIdRef.current) {
|
||||||
|
setSelectedTaskId(null)
|
||||||
|
historyPushedRef.current = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('popstate', handlePopState)
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('popstate', handlePopState)
|
||||||
|
}
|
||||||
|
}, [selectedTaskId])
|
||||||
|
|
||||||
|
const handleOpenTaskDetail = (taskId) => {
|
||||||
|
setSelectedTaskId(taskId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCloseTaskDetail = () => {
|
||||||
|
if (historyPushedRef.current) {
|
||||||
|
window.history.back()
|
||||||
|
} else {
|
||||||
|
setSelectedTaskId(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTaskSaved = () => {
|
||||||
|
if (onDelete) {
|
||||||
|
onDelete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleDelete = async (entryId) => {
|
const handleDelete = async (entryId) => {
|
||||||
if (deletingIds.has(entryId)) return
|
if (deletingIds.has(entryId)) return
|
||||||
@@ -175,45 +225,6 @@ function TodayEntriesList({ data, loading, error, onRetry, onDelete }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClearAutoComplete = async (taskId) => {
|
|
||||||
if (clearingAutoCompleteIds.has(taskId)) return
|
|
||||||
|
|
||||||
if (!window.confirm('Снять автовыполнение в конце дня? Задача не будет выполнена автоматически.')) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setClearingAutoCompleteIds(prev => new Set(prev).add(taskId))
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await authFetch(`/api/tasks/${taskId}/draft`, {
|
|
||||||
method: 'PUT',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ auto_complete: false }),
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
const errorText = await response.text()
|
|
||||||
console.error('Clear auto_complete error:', response.status, errorText)
|
|
||||||
throw new Error(`Ошибка: ${response.status}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onDelete) {
|
|
||||||
onDelete()
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Clear auto_complete failed:', err)
|
|
||||||
alert(err.message || 'Не удалось снять автовыполнение')
|
|
||||||
} finally {
|
|
||||||
setClearingAutoCompleteIds(prev => {
|
|
||||||
const next = new Set(prev)
|
|
||||||
next.delete(taskId)
|
|
||||||
return next
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-center items-center py-8">
|
<div className="flex justify-center items-center py-8">
|
||||||
@@ -235,6 +246,7 @@ function TodayEntriesList({ data, loading, error, onRetry, onDelete }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<div className="mt-2 mb-6">
|
<div className="mt-2 mb-6">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{data.map((entry) => {
|
{data.map((entry) => {
|
||||||
@@ -248,9 +260,8 @@ function TodayEntriesList({ data, loading, error, onRetry, onDelete }) {
|
|||||||
>
|
>
|
||||||
{isDraft ? (
|
{isDraft ? (
|
||||||
<button
|
<button
|
||||||
onClick={() => handleClearAutoComplete(entry.task_id)}
|
onClick={() => handleOpenTaskDetail(entry.task_id)}
|
||||||
disabled={clearingAutoCompleteIds.has(entry.task_id)}
|
className="absolute top-4 right-4"
|
||||||
className="absolute top-4 right-4 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
||||||
style={{
|
style={{
|
||||||
color: '#3b82f6',
|
color: '#3b82f6',
|
||||||
background: 'none',
|
background: 'none',
|
||||||
@@ -264,29 +275,19 @@ function TodayEntriesList({ data, loading, error, onRetry, onDelete }) {
|
|||||||
width: '24px',
|
width: '24px',
|
||||||
height: '24px',
|
height: '24px',
|
||||||
transition: 'all 0.2s',
|
transition: 'all 0.2s',
|
||||||
opacity: clearingAutoCompleteIds.has(entry.task_id) ? 0.5 : 1,
|
|
||||||
zIndex: 10
|
zIndex: 10
|
||||||
}}
|
}}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e) => {
|
||||||
if (!clearingAutoCompleteIds.has(entry.task_id)) {
|
|
||||||
e.currentTarget.style.backgroundColor = '#eff6ff'
|
e.currentTarget.style.backgroundColor = '#eff6ff'
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e) => {
|
||||||
e.currentTarget.style.backgroundColor = 'transparent'
|
e.currentTarget.style.backgroundColor = 'transparent'
|
||||||
}}
|
}}
|
||||||
title="Снять автовыполнение в конце дня"
|
title="Редактировать задачу"
|
||||||
>
|
>
|
||||||
{clearingAutoCompleteIds.has(entry.task_id) ? (
|
|
||||||
<svg className="w-5 h-5 animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
||||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
|
||||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
||||||
</svg>
|
|
||||||
) : (
|
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" title="Автовыполнение в конце дня">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" title="Автовыполнение в конце дня">
|
||||||
<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"></path>
|
<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
)}
|
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
@@ -353,6 +354,14 @@ function TodayEntriesList({ data, loading, error, onRetry, onDelete }) {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{selectedTaskId && (
|
||||||
|
<TaskDetail
|
||||||
|
taskId={selectedTaskId}
|
||||||
|
onClose={handleCloseTaskDetail}
|
||||||
|
onRefresh={handleTaskSaved}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user