Files
play-life/play-life-web/src/components/TaskDetail.jsx

207 lines
6.8 KiB
React
Raw Normal View History

import React, { useState, useEffect, useCallback } from 'react'
import { useAuth } from './auth/AuthContext'
import './TaskDetail.css'
const API_URL = '/api/tasks'
function TaskDetail({ taskId, onClose, onRefresh, onTaskCompleted }) {
const { authFetch } = useAuth()
const [taskDetail, setTaskDetail] = useState(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const [selectedSubtasks, setSelectedSubtasks] = useState(new Set())
const [progressionValue, setProgressionValue] = useState('')
const [isCompleting, setIsCompleting] = useState(false)
const fetchTaskDetail = useCallback(async () => {
try {
setLoading(true)
setError(null)
const response = await authFetch(`${API_URL}/${taskId}`)
if (!response.ok) {
throw new Error('Ошибка загрузки задачи')
}
const data = await response.json()
setTaskDetail(data)
} catch (err) {
setError(err.message)
console.error('Error fetching task detail:', err)
} finally {
setLoading(false)
}
}, [taskId, authFetch])
useEffect(() => {
if (taskId) {
fetchTaskDetail()
} else {
// Сбрасываем состояние при закрытии модального окна
setTaskDetail(null)
setLoading(true)
setError(null)
setSelectedSubtasks(new Set())
setProgressionValue('')
}
}, [taskId, fetchTaskDetail])
const handleSubtaskToggle = (subtaskId) => {
setSelectedSubtasks(prev => {
const newSet = new Set(prev)
if (newSet.has(subtaskId)) {
newSet.delete(subtaskId)
} else {
newSet.add(subtaskId)
}
return newSet
})
}
const handleComplete = async () => {
if (!taskDetail) return
// Валидация: если progression_base != null, то value обязателен
if (taskDetail.task.progression_base != null && !progressionValue.trim()) {
alert('Поле "Значение" обязательно для задач с прогрессией')
return
}
setIsCompleting(true)
try {
const payload = {
children_task_ids: Array.from(selectedSubtasks)
}
if (taskDetail.task.progression_base != null && progressionValue.trim()) {
payload.value = parseFloat(progressionValue)
if (isNaN(payload.value)) {
throw new Error('Неверное значение')
}
}
const response = await authFetch(`${API_URL}/${taskId}/complete`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
})
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
throw new Error(errorData.message || 'Ошибка при выполнении задачи')
}
// Показываем уведомление о выполнении
if (onTaskCompleted) {
onTaskCompleted()
}
// Обновляем список и закрываем модальное окно
if (onRefresh) {
onRefresh()
}
if (onClose) {
onClose()
}
} catch (err) {
console.error('Error completing task:', err)
alert(err.message || 'Ошибка при выполнении задачи')
} finally {
setIsCompleting(false)
}
}
if (!taskId) return null
const { task, rewards, subtasks } = taskDetail || {}
const hasProgression = task?.progression_base != null
const canComplete = !hasProgression || (hasProgression && progressionValue.trim())
return (
<div className="task-detail-modal-overlay" onClick={onClose}>
<div className="task-detail-modal" onClick={(e) => e.stopPropagation()}>
<div className="task-detail-modal-header">
<h2 className="task-detail-title">
{loading ? 'Загрузка...' : error ? 'Ошибка' : taskDetail ? task.name : 'Задача'}
</h2>
<button onClick={onClose} className="task-detail-close-button">
</button>
</div>
<div className="task-detail-modal-content">
{loading && (
<div className="loading">Загрузка...</div>
)}
{error && (
<div className="error-message">{error}</div>
)}
{!loading && !error && taskDetail && (
<>
{subtasks && subtasks.length > 0 && (
<div className="task-subtasks">
{subtasks.map((subtask) => {
const subtaskName = subtask.task.name || 'Подзадача'
return (
<div key={subtask.task.id} className="subtask-item">
<label className="subtask-checkbox-label">
<input
type="checkbox"
checked={selectedSubtasks.has(subtask.task.id)}
onChange={() => handleSubtaskToggle(subtask.task.id)}
className="subtask-checkbox"
/>
<div className="subtask-content">
<div className="subtask-name">{subtaskName}</div>
</div>
</label>
</div>
)
})}
</div>
)}
<div className="task-complete-section">
{hasProgression ? (
<div className="progression-input-group">
<input
type="number"
step="any"
value={progressionValue}
onChange={(e) => setProgressionValue(e.target.value)}
placeholder={`Значение (~${task.progression_base})`}
className="progression-input"
/>
{progressionValue.trim() && (
<button
onClick={handleComplete}
disabled={isCompleting}
className="complete-button"
>
</button>
)}
</div>
) : (
<button
onClick={handleComplete}
disabled={isCompleting || !canComplete}
className="complete-button full-width"
>
{isCompleting ? 'Выполнение...' : 'Выполнить'}
</button>
)}
</div>
</>
)}
</div>
</div>
</div>
)
}
export default TaskDetail