Add TodoList component

This commit is contained in:
EugeneTes
2026-08-15 11:56:12 +00:00
parent aa27ac1142
commit 77511a02af

View File

@@ -0,0 +1,105 @@
import { useEffect, useState, type FormEvent } from 'react';
import { api, type Todo } from '../lib/api';
import { supabase } from '../lib/supabase';
export function TodoList({ userEmail }: { userEmail: string }) {
const [todos, setTodos] = useState<Todo[]>([]);
const [newTitle, setNewTitle] = useState('');
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
async function refresh() {
try {
setTodos(await api.list());
} catch (e) {
setError(String(e));
}
}
useEffect(() => {
refresh().finally(() => setLoading(false));
}, []);
async function onAdd(e: FormEvent) {
e.preventDefault();
const title = newTitle.trim();
if (!title) return;
setError(null);
try {
const created = await api.create(title);
setTodos((prev) => [created, ...prev]);
setNewTitle('');
} catch (e) {
setError(String(e));
}
}
async function onToggle(todo: Todo) {
setError(null);
try {
const updated = await api.setCompleted(todo.id, !todo.completed);
setTodos((prev) => prev.map((t) => (t.id === updated.id ? updated : t)));
} catch (e) {
setError(String(e));
}
}
async function onDelete(todo: Todo) {
setError(null);
try {
await api.remove(todo.id);
setTodos((prev) => prev.filter((t) => t.id !== todo.id));
} catch (e) {
setError(String(e));
}
}
async function onSignOut() {
await supabase.auth.signOut();
}
return (
<div style={{ maxWidth: 520, margin: '2rem auto', fontFamily: 'sans-serif' }}>
<header style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
<h1>Todos</h1>
<span>
{userEmail}{' '}
<button type="button" onClick={onSignOut}>Sign out</button>
</span>
</header>
<form onSubmit={onAdd} style={{ display: 'flex', gap: 8, marginBottom: 16 }}>
<input
value={newTitle}
onChange={(e) => setNewTitle(e.target.value)}
placeholder="What needs doing?"
maxLength={500}
style={{ flex: 1, padding: 6 }}
/>
<button type="submit">Add</button>
</form>
{error && <p style={{ color: 'crimson' }}>{error}</p>}
{loading ? (
<p>Loading</p>
) : todos.length === 0 ? (
<p>No todos yet.</p>
) : (
<ul style={{ listStyle: 'none', padding: 0 }}>
{todos.map((t) => (
<li
key={t.id}
style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '4px 0' }}
>
<input type="checkbox" checked={t.completed} onChange={() => onToggle(t)} />
<span style={{ flex: 1, textDecoration: t.completed ? 'line-through' : 'none' }}>
{t.title}
</span>
<button type="button" onClick={() => onDelete(t)}>Delete</button>
</li>
))}
</ul>
)}
</div>
);
}