Files
todo/frontend/src/components/TodoList.jsx
almazlar 281004434d
All checks were successful
Release and Build Docker Images / release-and-build (push) Successful in 1m29s
feat: Implement responsive styling for various screen sizes and refactor the main todo list container class.
2026-02-21 11:03:18 +03:00

130 lines
4.3 KiB
JavaScript

import React, { useState, useEffect } from 'react';
import { getTodos, createTodo, updateTodo, deleteTodo } from '../services/api';
const TodoList = () => {
const [todos, setTodos] = useState([]);
const [newTitle, setNewTitle] = useState('');
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetchTodos();
}, []);
const fetchTodos = async () => {
try {
setLoading(true);
const data = await getTodos();
// Sort by active first, then completed. Secondary sort by id descending
setTodos(data.sort((a, b) => {
if (a.completed === b.completed) {
return b.id - a.id;
}
return a.completed ? 1 : -1;
}));
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
const handleCreate = async (e) => {
e.preventDefault();
if (!newTitle.trim()) return;
try {
const created = await createTodo({
title: newTitle.trim(),
description: '',
completed: false
});
setTodos([created, ...todos]);
setNewTitle('');
} catch (err) {
setError(err.message);
}
};
const handleToggle = async (todo) => {
try {
const updated = await updateTodo(todo.id, {
...todo,
completed: !todo.completed
});
setTodos(todos.map(t => t.id === todo.id ? updated : t).sort((a, b) => {
if (a.id === updated.id) a = updated;
if (b.id === updated.id) b = updated;
if (a.completed === b.completed) return b.id - a.id;
return a.completed ? 1 : -1;
}));
} catch (err) {
setError(err.message);
}
};
const handleDelete = async (id) => {
try {
await deleteTodo(id);
setTodos(todos.filter(t => t.id !== id));
} catch (err) {
setError(err.message);
}
};
return (
<div className="todo-wrapper">
<h1 className="title">Tasks</h1>
<form className="todo-form" onSubmit={handleCreate}>
<input
type="text"
className="todo-input"
placeholder="What needs to be done?"
value={newTitle}
onChange={(e) => setNewTitle(e.target.value)}
autoFocus
/>
<button type="submit" className="add-btn" disabled={!newTitle.trim()}>
Add Task
</button>
</form>
{error ? (
<div className="error">{error}</div>
) : loading ? (
<div className="loading">Loading tasks...</div>
) : todos.length === 0 ? (
<div className="empty">All caught up! No active tasks.</div>
) : (
<ul className="todo-list">
{todos.map(todo => (
<li key={todo.id} className="todo-item">
<label className="todo-content">
<input
type="checkbox"
className="checkbox"
checked={todo.completed}
onChange={() => handleToggle(todo)}
/>
<span className={`todo-text ${todo.completed ? 'completed' : ''}`}>
{todo.title}
</span>
</label>
<button
className="delete-btn"
onClick={() => handleDelete(todo.id)}
aria-label="Delete"
>
</button>
</li>
))}
</ul>
)}
</div>
);
};
export default TodoList;