/
nsstnc
/
lab3
Обзор
Документация
Войти
/
nsstnc
/
lab3
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/components/TaskList.tsx
124 строки
4 KB
nsstnc
Поправил таск-лист
25 ноя 2025, 20:05
25 ноя 2025, 20:05
87880bb
Код
Авторство
О чём код?
import {useState} from 'react' import {useQuery, useMutation, useQueryClient} from '@tanstack/react-query' import {getUserTasks, createTask, closeTask, assignTaskToCurrentUser} from '@/api/tasks' import {useCurrentUser} from '@/hooks/useCurrentUser' import TaskDetail from '@/components/TaskDetail' export default function TaskList() { const [page, setPage] = useState(0) const [selectedTaskId, setSelectedTaskId] = useState<number | null>(null) const [newTitle, setNewTitle] = useState('') const {user} = useCurrentUser() const queryClient = useQueryClient() const {data: tasksData, isLoading} = useQuery({ queryKey: ['tasks', user?.id, page], queryFn: () => getUserTasks(user!.id, page, 10), enabled: !!user?.id, }) const createMutation = useMutation({ mutationFn: async (title: string) => { const taskId = await createTask(title) await assignTaskToCurrentUser(taskId, user!.id) // сразу назначаем себе return taskId }, onSuccess: () => { queryClient.invalidateQueries({queryKey: ['tasks']}) setNewTitle('') }, }) const closeMutation = useMutation({ mutationFn: closeTask, onSuccess: () => { queryClient.invalidateQueries({queryKey: ['tasks']}) queryClient.invalidateQueries({queryKey: ['task']}) }, }) if (!user) return null return ( <div className="space-y-6"> <div className="bg-white p-6 rounded-xl shadow"> <div className="flex gap-3"> <input type="text" placeholder="Название новой задачи..." value={newTitle} onChange={(e) => setNewTitle(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && newTitle.trim() && createMutation.mutate(newTitle.trim())} className="flex-1 px-4 py-3 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" /> <button onClick={() => newTitle.trim() && createMutation.mutate(newTitle.trim())} disabled={createMutation.isPending || !newTitle.trim()} className="bg-green-600 hover:bg-green-700 text-white px-6 py-3 rounded-lg disabled:opacity-50 transition" > {createMutation.isPending ? 'Создаём...' : 'Создать'} </button> </div> </div> {isLoading ? ( <div className="text-center py-10">Загрузка задач...</div> ) : ( <div className="grid gap-4"> {tasksData?.data?.map((task: any) => ( <div key={task.taskId} className="bg-white p-5 rounded-xl shadow hover:shadow-lg transition cursor-pointer flex justify-between items-center" onClick={() => setSelectedTaskId(task.taskId)} > <div> <h3 className="text-lg font-semibold">{task.title}</h3> <p className="text-sm text-gray-500"> ID: {task.taskId} </p> </div> {task.completedAt == null && ( <button onClick={(e) => { e.stopPropagation() closeMutation.mutate(task.taskId) }} disabled={closeMutation.isPending} className="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded transition disabled:opacity-50" > {closeMutation.isPending ? 'Закрываем...' : 'Закрыть'} </button> )} </div> ))} </div> )} <div className="flex justify-center gap-4 mt-8"> <button onClick={() => setPage(p => Math.max(0, p - 1))} disabled={page === 0} className="px-5 py-2 bg-gray-300 rounded disabled:opacity-50" > Назад </button> <span className="py-2 px-4">Страница {page + 1}</span> <button onClick={() => setPage(p => p + 1)} disabled={(tasksData?.data?.length || 0) < 10} className="px-5 py-2 bg-gray-300 rounded disabled:opacity-50" > Вперед </button> </div> {selectedTaskId && ( <TaskDetail taskId={selectedTaskId} onClose={() => setSelectedTaskId(null)} /> )} </div> ) }