forked from semi-23e/nextjs-todo-tutorial
phase4-2まで
This commit is contained in:
202
src/app/page.tsx
202
src/app/page.tsx
@@ -9,6 +9,8 @@ type Todo = {
|
|||||||
completed: boolean; // 完了したかどうかのフラグ。チェックボックスに対応。
|
completed: boolean; // 完了したかどうかのフラグ。チェックボックスに対応。
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type FilterType = 'all' | 'active' | 'completed'; // フィルターの種類。全て、未完了、完了の3種類。
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
// Todoリスト本体の状態管理
|
// Todoリスト本体の状態管理
|
||||||
const [todos, setTodos] = useState<Todo[]>([]); // Todo配列を保持
|
const [todos, setTodos] = useState<Todo[]>([]); // Todo配列を保持
|
||||||
@@ -31,7 +33,26 @@ export default function Home() {
|
|||||||
// 編集中のテキスト
|
// 編集中のテキスト
|
||||||
const [editText, setEditText] = useState('');
|
const [editText, setEditText] = useState('');
|
||||||
|
|
||||||
|
// フィルター状態の管理(全て、未完了、完了)
|
||||||
|
const [filter, setFilter] = useState<FilterType>('all');
|
||||||
|
|
||||||
|
// フィルターに応じたTodoの数をカウント
|
||||||
|
const activeCount = todos.filter(todo => !todo.completed).length;
|
||||||
|
const completedCount = todos.filter(todo => todo.completed).length;
|
||||||
|
|
||||||
|
|
||||||
|
// フィルターを変更する関数
|
||||||
|
const getFilteredTodos = () => { // フィルターに応じてTodoリストを返す関数
|
||||||
|
switch (filter) { // フィルターの種類に応じて処理を分岐
|
||||||
|
case 'active': // 未完了のTodoのみを返す
|
||||||
|
return todos.filter(todo => !todo.completed);
|
||||||
|
case 'completed': // 完了したTodoのみを返す
|
||||||
|
return todos.filter(todo => todo.completed);
|
||||||
|
default: // 'all'の場合は全てのTodoを返す
|
||||||
|
return todos; // 'all'の場合は全てのTodoを返す
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const startEdit = (todo: Todo) => { // 編集開始関数
|
const startEdit = (todo: Todo) => { // 編集開始関数
|
||||||
setEditingId(todo.id); // 編集対象のIDをセット
|
setEditingId(todo.id); // 編集対象のIDをセット
|
||||||
setEditText(todo.text); // 編集用のテキストをセット
|
setEditText(todo.text); // 編集用のテキストをセット
|
||||||
@@ -142,76 +163,127 @@ export default function Home() {
|
|||||||
追加
|
追加
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<div className="space-y-2">
|
<div className="flex justify-between items-center mb-4 p-4 bg-gray-100 rounded-lg"> {/* フィルターと削除ボタンのコンテナ */}
|
||||||
{todos.map((todo) => (
|
<div className="flex gap-2"> {/* フィルター用のボタン */}
|
||||||
<div key={todo.id} className="flex items-center p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition-colors">
|
<button
|
||||||
<input
|
onClick={() => setFilter('all')}
|
||||||
type="checkbox"
|
className={`px-4 py-2 rounded transition-colors ${
|
||||||
checked={todo.completed}
|
filter === 'all'
|
||||||
onChange={() => toggleTodo(todo.id)}
|
? 'bg-blue-500 text-white'
|
||||||
className="mr-3 h-5 w-5 cursor-pointer"
|
: 'bg-white text-gray-700 hover:bg-gray-200'
|
||||||
/>
|
}`}
|
||||||
|
>
|
||||||
|
すべて ({todos.length})
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setFilter('active')}
|
||||||
|
className={`px-4 py-2 rounded transition-colors ${
|
||||||
|
filter === 'active'
|
||||||
|
? 'bg-blue-500 text-white'
|
||||||
|
: 'bg-white text-gray-700 hover:bg-gray-200'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
未完了 ({activeCount})
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setFilter('completed')}
|
||||||
|
className={`px-4 py-2 rounded transition-colors ${
|
||||||
|
filter === 'completed'
|
||||||
|
? 'bg-blue-500 text-white'
|
||||||
|
: 'bg-white text-gray-700 hover:bg-gray-200'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
完了 ({completedCount})
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 完了済みTODOをすべて削除 */}
|
||||||
|
{completedCount > 0 && (
|
||||||
|
<button
|
||||||
|
onClick={() => setTodos(todos.filter(todo => !todo.completed))}
|
||||||
|
className="text-red-500 hover:text-red-700 px-3 py-1 rounded transition-colors"
|
||||||
|
>
|
||||||
|
完了済みを削除
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{editingId === todo.id ? (
|
<div className="space-y-2">
|
||||||
// 編集モード
|
{getFilteredTodos().length === 0 ? (
|
||||||
|
<p className="text-gray-500 text-center py-8">
|
||||||
|
{filter === 'active' && 'すべて完了しました!'}
|
||||||
|
{filter === 'completed' && '完了したTODOがありません'}
|
||||||
|
{filter === 'all' && 'TODOがありません'}
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
getFilteredTodos().map((todo) => (
|
||||||
|
<div key={todo.id} className="flex items-center p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition-colors">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="checkbox"
|
||||||
value={editText}
|
checked={todo.completed}
|
||||||
onChange={(e) => setEditText(e.target.value)}
|
onChange={() => toggleTodo(todo.id)}
|
||||||
onKeyDown={(e) => {
|
className="mr-3 h-5 w-5 cursor-pointer"
|
||||||
if (e.key === 'Enter') saveEdit();
|
|
||||||
if (e.key === 'Escape') cancelEdit();
|
|
||||||
}}
|
|
||||||
className="flex-1 border border-blue-500 p-2 rounded focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
||||||
autoFocus
|
|
||||||
/>
|
/>
|
||||||
) : (
|
{editingId === todo.id ? (
|
||||||
// 通常モード
|
// 編集モード
|
||||||
<span
|
<input
|
||||||
onClick={() => startEdit(todo)}
|
type="text"
|
||||||
className={`flex-1 cursor-pointer p-2 rounded hover:bg-gray-200 transition-colors ${
|
value={editText}
|
||||||
todo.completed ? 'line-through text-gray-500' : 'text-gray-800'
|
onChange={(e) => setEditText(e.target.value)}
|
||||||
}`}
|
onKeyDown={(e) => {
|
||||||
>
|
if (e.key === 'Enter') saveEdit();
|
||||||
{todo.text}
|
if (e.key === 'Escape') cancelEdit();
|
||||||
</span>
|
}}
|
||||||
)}
|
className="flex-1 border border-blue-500 p-2 rounded focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
autoFocus
|
||||||
{editingId === todo.id ? (
|
/>
|
||||||
// 編集モードのボタン
|
) : (
|
||||||
<div className="flex gap-2 ml-2">
|
// 通常モード
|
||||||
<button
|
<span
|
||||||
onClick={saveEdit}
|
|
||||||
className="text-green-600 hover:text-green-800 px-2 py-1 rounded transition-colors"
|
|
||||||
>
|
|
||||||
保存
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={cancelEdit}
|
|
||||||
className="text-gray-600 hover:text-gray-800 px-2 py-1 rounded transition-colors"
|
|
||||||
>
|
|
||||||
キャンセル
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
// 通常モードのボタン
|
|
||||||
<div className="flex gap-2 ml-2">
|
|
||||||
<button
|
|
||||||
onClick={() => startEdit(todo)}
|
onClick={() => startEdit(todo)}
|
||||||
className="text-blue-500 hover:text-blue-700 px-2 py-1 rounded transition-colors"
|
className={`flex-1 cursor-pointer p-2 rounded hover:bg-gray-200 transition-colors ${
|
||||||
|
todo.completed ? 'line-through text-gray-500' : 'text-gray-800'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
編集
|
{todo.text}
|
||||||
</button>
|
</span>
|
||||||
<button
|
)}
|
||||||
onClick={() => handleDeleteClick(todo.id)} // ←ここをdeleteTodoからhandleDeleteClickに修正
|
{editingId === todo.id ? (
|
||||||
className="text-red-500 hover:text-red-700 px-2 py-1 rounded transition-colors"
|
// 編集モードのボタン
|
||||||
>
|
<div className="flex gap-2 ml-2">
|
||||||
削除
|
<button
|
||||||
</button>
|
onClick={saveEdit}
|
||||||
</div>
|
className="text-green-600 hover:text-green-800 px-2 py-1 rounded transition-colors"
|
||||||
)}
|
>
|
||||||
</div>
|
保存
|
||||||
))}
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={cancelEdit}
|
||||||
|
className="text-gray-600 hover:text-gray-800 px-2 py-1 rounded transition-colors"
|
||||||
|
>
|
||||||
|
キャンセル
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
// 通常モードのボタン
|
||||||
|
<div className="flex gap-2 ml-2">
|
||||||
|
<button
|
||||||
|
onClick={() => startEdit(todo)}
|
||||||
|
className="text-blue-500 hover:text-blue-700 px-2 py-1 rounded transition-colors"
|
||||||
|
>
|
||||||
|
編集
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleDeleteClick(todo.id)}
|
||||||
|
className="text-red-500 hover:text-red-700 px-2 py-1 rounded transition-colors"
|
||||||
|
>
|
||||||
|
削除
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 確認ダイアログ */}
|
{/* 確認ダイアログ */}
|
||||||
|
|||||||
Reference in New Issue
Block a user