import { useState } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { motion, AnimatePresence } from 'framer-motion'
import { Plus, Check, Circle, Calendar, AlertTriangle, Undo2, Edit2 } from 'lucide-react'
import { format, parseISO, isToday, isTomorrow, isPast } from 'date-fns'
import { ru } from 'date-fns/locale'
import { tasksApi } from '../api/tasks'
import Navigation from '../components/Navigation'
import CreateTaskModal from '../components/CreateTaskModal'
import EditTaskModal from '../components/EditTaskModal'
import clsx from 'clsx'
const PRIORITY_LABELS = {
0: null,
1: { label: 'Низкий', class: 'bg-blue-100 text-blue-700' },
2: { label: 'Средний', class: 'bg-yellow-100 text-yellow-700' },
3: { label: 'Высокий', class: 'bg-red-100 text-red-700' },
}
function formatDueDate(dateStr) {
if (!dateStr) return null
const date = parseISO(dateStr)
if (isToday(date)) return 'Сегодня'
if (isTomorrow(date)) return 'Завтра'
return format(date, 'd MMM', { locale: ru })
}
export default function Tasks() {
const [showCreate, setShowCreate] = useState(false)
const [editingTask, setEditingTask] = useState(null)
const [filter, setFilter] = useState('active') // all, active, completed
const queryClient = useQueryClient()
const { data: tasks = [], isLoading } = useQuery({
queryKey: ['tasks', filter],
queryFn: () => {
if (filter === 'all') return tasksApi.list()
return tasksApi.list(filter === 'completed')
},
})
const completeMutation = useMutation({
mutationFn: (id) => tasksApi.complete(id),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['tasks'] })
queryClient.invalidateQueries({ queryKey: ['tasks-today'] })
},
})
const uncompleteMutation = useMutation({
mutationFn: (id) => tasksApi.uncomplete(id),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['tasks'] })
queryClient.invalidateQueries({ queryKey: ['tasks-today'] })
},
})
const handleToggle = (task) => {
if (task.completed) {
uncompleteMutation.mutate(task.id)
} else {
completeMutation.mutate(task.id)
}
}
const activeTasks = tasks.filter(t => !t.completed)
const completedTasks = tasks.filter(t => t.completed)
return (
Задачи
{/* Фильтры */}
{[
{ key: 'active', label: 'Активные' },
{ key: 'completed', label: 'Выполненные' },
{ key: 'all', label: 'Все' },
].map(({ key, label }) => (
))}
{isLoading ? (
{[1, 2, 3].map((i) => (
))}
) : tasks.length === 0 ? (
{filter === 'active' ? 'Нет активных задач' : filter === 'completed' ? 'Нет выполненных задач' : 'Нет задач'}
{filter === 'active' ? 'Добавь новую задачу или выбери другой фильтр' : 'Выполняй задачи и они появятся здесь'}
{filter === 'active' && (
)}
) : (
{tasks.map((task, index) => (
handleToggle(task)}
onEdit={() => setEditingTask(task)}
isLoading={completeMutation.isPending || uncompleteMutation.isPending}
/>
))}
)}
setShowCreate(false)}
/>
setEditingTask(null)}
task={editingTask}
/>
)
}
function TaskCard({ task, index, onToggle, onEdit, isLoading }) {
const [showConfetti, setShowConfetti] = useState(false)
const priorityInfo = PRIORITY_LABELS[task.priority]
const dueDateLabel = formatDueDate(task.due_date)
const isOverdue = task.due_date && isPast(parseISO(task.due_date)) && !isToday(parseISO(task.due_date)) && !task.completed
const handleCheck = (e) => {
e.stopPropagation()
if (isLoading) return
if (!task.completed) {
setShowConfetti(true)
setTimeout(() => setShowConfetti(false), 1000)
}
onToggle()
}
return (
{showConfetti && (
{[...Array(6)].map((_, i) => (
))}
)}
{task.completed ? (
) : (
{task.icon || '📋'}
)}
{task.title}
{task.description && (
{task.description}
)}
{dueDateLabel && (
{isOverdue && }
{dueDateLabel}
)}
{priorityInfo && (
{priorityInfo.label}
)}
{task.completed && (
)}
)
}