WorkRunner Documentation

Todo List

A complete todo list Construct with all the essentials.

Complete Code

"use client";

import { useState, useEffect } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox";
import { Label } from "@/components/ui/label";
import { Badge } from "@/components/ui/badge";
import { Plus, Trash2 } from "lucide-react";

interface Todo {
  id: string;
  text: string;
  completed: boolean;
}

const STORAGE_KEY = "work-construct-todo-list";

export default function App() {
  const [todos, setTodos] = useState<Todo[]>(() => {
    if (typeof window === "undefined") return [];
    const saved = localStorage.getItem(STORAGE_KEY);
    return saved ? JSON.parse(saved) : [];
  });
  const [newTodo, setNewTodo] = useState("");

  useEffect(() => {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(todos));
  }, [todos]);

  const addTodo = () => {
    if (!newTodo.trim()) return;
    setTodos([
      ...todos,
      { id: crypto.randomUUID(), text: newTodo.trim(), completed: false },
    ]);
    setNewTodo("");
  };

  const toggleTodo = (id: string) => {
    setTodos(todos.map((t) =>
      t.id === id ? { ...t, completed: !t.completed } : t
    ));
  };

  const deleteTodo = (id: string) => {
    setTodos(todos.filter((t) => t.id !== id));
  };

  const completedCount = todos.filter((t) => t.completed).length;

  return (
    <div className="min-h-screen bg-background p-4 sm:p-6">
      <Card className="mx-auto max-w-md">
        <CardHeader>
          <div className="flex items-center justify-between">
            <CardTitle>Todo List</CardTitle>
            {todos.length > 0 && (
              <Badge variant="secondary">
                {completedCount}/{todos.length}
              </Badge>
            )}
          </div>
        </CardHeader>
        <CardContent className="space-y-4">
          <div className="flex gap-2">
            <Input
              placeholder="Add a task..."
              value={newTodo}
              onChange={(e) => setNewTodo(e.target.value)}
              onKeyDown={(e) => e.key === "Enter" && addTodo()}
              aria-label="New task"
            />
            <Button onClick={addTodo} aria-label="Add task">
              <Plus className="h-4 w-4" />
            </Button>
          </div>

          <div className="space-y-2">
            {todos.map((todo) => (
              <div
                key={todo.id}
                className="flex items-center gap-3 rounded-lg border p-3"
              >
                <Checkbox
                  id={todo.id}
                  checked={todo.completed}
                  onCheckedChange={() => toggleTodo(todo.id)}
                />
                <Label
                  htmlFor={todo.id}
                  className={`flex-1 ${
                    todo.completed ? "line-through text-muted-foreground" : ""
                  }`}
                >
                  {todo.text}
                </Label>
                <Button
                  variant="ghost"
                  size="icon"
                  onClick={() => deleteTodo(todo.id)}
                  aria-label="Delete task"
                >
                  <Trash2 className="h-4 w-4" />
                </Button>
              </div>
            ))}
          </div>

          {todos.length === 0 && (
            <p className="text-center text-sm text-muted-foreground py-8">
              No tasks yet. Add one above!
            </p>
          )}

          {completedCount > 0 && (
            <Button
              variant="outline"
              size="sm"
              className="w-full"
              onClick={() => setTodos(todos.filter((t) => !t.completed))}
            >
              Clear {completedCount} completed
            </Button>
          )}
        </CardContent>
      </Card>
    </div>
  );
}

Key Features

  • • Add, complete, and delete tasks
  • • Progress badge showing completion count
  • • Keyboard support (Enter to add)
  • • Clear completed tasks in one click
  • • Persists to localStorage
  • • Responsive design
  • • Full accessibility support