v3.1.0: Оптимизация загрузки списка задач - все данные в одном запросе, добавлены индикаторы подзадач и прогрессии
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 40s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 40s
This commit is contained in:
@@ -89,11 +89,31 @@
|
||||
color: #8b5cf6;
|
||||
}
|
||||
|
||||
.task-name-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.task-name {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
color: #1f2937;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.task-subtasks-count {
|
||||
color: #9ca3af;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.task-progression-icon {
|
||||
color: #9ca3af;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.task-actions {
|
||||
|
||||
@@ -10,8 +10,6 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
|
||||
const { authFetch } = useAuth()
|
||||
// Инициализируем tasks из data, если data есть, иначе пустой массив
|
||||
const [tasks, setTasks] = useState(() => data && Array.isArray(data) ? data : [])
|
||||
const [taskDetails, setTaskDetails] = useState({})
|
||||
const [loadingDetails, setLoadingDetails] = useState(false)
|
||||
const [selectedTaskForDetail, setSelectedTaskForDetail] = useState(null)
|
||||
const [isCompleting, setIsCompleting] = useState(false)
|
||||
const [expandedCompleted, setExpandedCompleted] = useState({})
|
||||
@@ -25,9 +23,6 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
|
||||
}
|
||||
})
|
||||
const [toast, setToast] = useState(null)
|
||||
|
||||
// Для отслеживания изменений в списке задач (чтобы не перезагружать детали без необходимости)
|
||||
const lastTaskIdsRef = useRef('')
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
@@ -38,64 +33,6 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
|
||||
// Загрузка данных управляется из App.jsx через loadTabData
|
||||
// TaskList не инициирует загрузку самостоятельно
|
||||
|
||||
// Загружаем детали для всех задач
|
||||
// Оптимизация: загружаем только если список задач изменился (по id и last_completed_at)
|
||||
useEffect(() => {
|
||||
if (!tasks || tasks.length === 0) {
|
||||
setTaskDetails({})
|
||||
lastTaskIdsRef.current = ''
|
||||
return
|
||||
}
|
||||
|
||||
// Создаем ключ из id и last_completed_at всех задач
|
||||
const taskKey = tasks.map(t => `${t.id}:${t.last_completed_at || ''}`).sort().join(',')
|
||||
|
||||
// Если ключ не изменился, не перезагружаем детали
|
||||
if (taskKey === lastTaskIdsRef.current) {
|
||||
return
|
||||
}
|
||||
|
||||
lastTaskIdsRef.current = taskKey
|
||||
|
||||
const loadTaskDetails = async () => {
|
||||
// Не показываем индикатор загрузки если детали уже есть (фоновое обновление)
|
||||
const hasExistingDetails = Object.keys(taskDetails).length > 0
|
||||
if (!hasExistingDetails) {
|
||||
setLoadingDetails(true)
|
||||
}
|
||||
|
||||
try {
|
||||
const detailPromises = tasks.map(async (task) => {
|
||||
try {
|
||||
const response = await authFetch(`${API_URL}/${task.id}`)
|
||||
if (response.ok) {
|
||||
const detail = await response.json()
|
||||
return { taskId: task.id, detail }
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`Error loading task detail for ${task.id}:`, err)
|
||||
}
|
||||
return null
|
||||
})
|
||||
|
||||
const details = await Promise.all(detailPromises)
|
||||
const detailsMap = {}
|
||||
details.forEach(item => {
|
||||
if (item) {
|
||||
detailsMap[item.taskId] = item.detail
|
||||
}
|
||||
})
|
||||
setTaskDetails(detailsMap)
|
||||
} catch (err) {
|
||||
console.error('Error loading task details:', err)
|
||||
} finally {
|
||||
setLoadingDetails(false)
|
||||
}
|
||||
}
|
||||
|
||||
loadTaskDetails()
|
||||
}, [tasks, authFetch])
|
||||
|
||||
const handleTaskClick = (task) => {
|
||||
onNavigate?.('task-form', { taskId: task.id })
|
||||
}
|
||||
@@ -103,9 +40,8 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
|
||||
const handleCheckmarkClick = async (task, e) => {
|
||||
e.stopPropagation()
|
||||
|
||||
const detail = taskDetails[task.id]
|
||||
const hasProgression = detail?.task?.progression_base != null
|
||||
const hasSubtasks = detail?.subtasks && detail.subtasks.length > 0
|
||||
const hasProgression = task.has_progression || task.progression_base != null
|
||||
const hasSubtasks = task.subtasks_count > 0
|
||||
|
||||
if (hasProgression || hasSubtasks) {
|
||||
// Открываем экран details
|
||||
@@ -174,36 +110,12 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
|
||||
})
|
||||
}
|
||||
|
||||
// Получаем все проекты из наград задачи и подзадач
|
||||
// Получаем все проекты из задачи (теперь они приходят в task.project_names)
|
||||
const getTaskProjects = (task) => {
|
||||
const projects = new Set()
|
||||
const detail = taskDetails[task.id]
|
||||
|
||||
if (detail) {
|
||||
// Проекты из основной задачи
|
||||
if (detail.rewards) {
|
||||
detail.rewards.forEach(reward => {
|
||||
if (reward.project_name) {
|
||||
projects.add(reward.project_name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Проекты из подзадач
|
||||
if (detail.subtasks) {
|
||||
detail.subtasks.forEach(subtask => {
|
||||
if (subtask.rewards) {
|
||||
subtask.rewards.forEach(reward => {
|
||||
if (reward.project_name) {
|
||||
projects.add(reward.project_name)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
if (task.project_names && Array.isArray(task.project_names)) {
|
||||
return task.project_names
|
||||
}
|
||||
|
||||
return Array.from(projects)
|
||||
return []
|
||||
}
|
||||
|
||||
// Функция для проверки, является ли период нулевым
|
||||
@@ -340,12 +252,11 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
|
||||
})
|
||||
|
||||
return groups
|
||||
}, [tasks, taskDetails])
|
||||
}, [tasks])
|
||||
|
||||
const renderTaskItem = (task) => {
|
||||
const detail = taskDetails[task.id]
|
||||
const hasProgression = detail?.task?.progression_base != null
|
||||
const hasSubtasks = detail?.subtasks && detail.subtasks.length > 0
|
||||
const hasProgression = task.has_progression || task.progression_base != null
|
||||
const hasSubtasks = task.subtasks_count > 0
|
||||
const showDetailOnCheckmark = hasProgression || hasSubtasks
|
||||
|
||||
return (
|
||||
@@ -365,7 +276,31 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
|
||||
<path d="M6 10 L9 13 L14 7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="checkmark-check" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="task-name">{task.name}</div>
|
||||
<div className="task-name-container">
|
||||
<div className="task-name">
|
||||
{task.name}
|
||||
{hasSubtasks && (
|
||||
<span className="task-subtasks-count">(+{task.subtasks_count})</span>
|
||||
)}
|
||||
</div>
|
||||
{hasProgression && (
|
||||
<svg
|
||||
className="task-progression-icon"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
title="Задача с прогрессией"
|
||||
>
|
||||
<polyline points="23 6 13.5 15.5 8.5 10.5 1 18"></polyline>
|
||||
<polyline points="17 6 23 6 23 12"></polyline>
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
<div className="task-actions">
|
||||
<span className="task-completed-count">{task.completed}</span>
|
||||
</div>
|
||||
@@ -408,10 +343,6 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
|
||||
Добавить
|
||||
</button>
|
||||
|
||||
{loadingDetails && tasks.length > 0 && (
|
||||
<div className="loading-details">Загрузка деталей задач...</div>
|
||||
)}
|
||||
|
||||
{projectNames.length === 0 && !loading && tasks.length === 0 && (
|
||||
<div className="empty-state">
|
||||
<p>Задач пока нет. Добавьте задачу через кнопку "Добавить".</p>
|
||||
|
||||
Reference in New Issue
Block a user