view react_games/src/TODO/main.tsx @ 279:b3b547563ec7

Add Google connector service and agent wiki Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code. Co-authored-by: Copilot <[email protected]> Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
author MrJuneJune <me@mrjunejune.com>
date Mon, 17 Aug 2026 22:22:36 -0700
parents fb9bcd3145cb
children
line wrap: on
line source

/*
 * 📝 React Todo App Challenge (1 Hour)
 * ❓Problem
 * Build a simple Todo application using React. The app should allow users to:
 * 
 *  - Add a new todo item.
 * 
 *  - Mark a todo as completed.
 * 
 *  - Delete a todo.
 * 
 * Filter todos by:
 * 
 *   -  All
 *   -  Active
 *   -  Completed
 * 
 * 💡Bonus (if time allows)
 * Persist todos in localStorage.
 * 
 * Use React Context + useReducer to manage global state.
 * 
 * Add a clear completed button.
 * 
 * 📦 Requirements
 * Use functional components + hooks only.
 *
 *
 * 
 * No styling is required, but if you have time, make it readable.
 * 
 * Don’t use external state management libraries like Redux.
 *
 * Components
 *
 *   TODOs
 *   Inputs
 *   buttons filtering
 *
 * TODO States 
 *   - Active 
 *   - Completed
 *   - Deleted
 */
import React, { CSSProperties, Dispatch, InputHTMLAttributes, memo, useMemo, useRef, useState } from "react";

enum TodoState {
  ACTIVE="active",
  COMPLETED="completed",
  DELETED="deleted",
}

interface Todo {
  id: string; 
  value: string;
  state: TodoState;
}

const INITIAL_TODOS: Todo[] = [
  {id: "1", value: "I want to buy pizza", state: TodoState.ACTIVE},
  {id: "2", value: "I want to buy pizza completed", state: TodoState.COMPLETED},
  {id: "3", value: "I want to buy pizza deleted", state: TodoState.DELETED},
]

function changeStateTodo(
  todoId: string,
  todoState: TodoState,
  setTodoCallback: Dispatch<React.SetStateAction<Todo[]>>,
): void {
  setTodoCallback(
    (todos: Todo[]) => todos.map((todo: Todo) => {
      if (todo.id === todoId) {
        return {
          ...todo,
          state: todoState
        };
      }
      return todo;
    })
  )
}

interface TodoComponentProp {
  todo: Todo;
  setTodos: Dispatch<React.SetStateAction<Todo[]>>;
}

const TodoComponent = memo(({todo, setTodos}: TodoComponentProp) => {
  const tdStyle: CSSProperties = {
    padding: "0.5rem",
    borderBottom: "1px solid #ddd",
  };
  
  const rowStyle: CSSProperties = {
    backgroundColor: "#fff",
  };

  return (
      <tr key={todo.id} style={rowStyle}>
        <td style={tdStyle}>{todo.value}</td>
        <td style={tdStyle}>
          <span style={{ textTransform: "capitalize" }}>{todo.state}</span>
        </td>
        <td style={tdStyle}>
          {todo.state !== TodoState.COMPLETED && (
            <button onClick={() => changeStateTodo(todo.id, TodoState.COMPLETED, setTodos)}>

            </button>
          )}
          {todo.state !== TodoState.DELETED && (
            <button onClick={() => changeStateTodo(todo.id, TodoState.DELETED, setTodos)}>
              🗑️
            </button>
          )}
          {todo.state !== TodoState.ACTIVE && (
            <button onClick={() => changeStateTodo(todo.id, TodoState.ACTIVE, setTodos)}>
              🟢
            </button>
          )}
        </td>
      </tr>
  )
})

const TODO = () => {
  const [todos, setTodos] = useState<Todo[]>(INITIAL_TODOS);
  const [todoFilter, setTodoFilters] = useState<TodoState | null>(null);
  const inputRef =  useRef<HTMLInputElement>(null);

  const style: CSSProperties = {
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    height: "100vh",
    width: "100vw",
  }

  const filterTodos = useMemo(() => {
    return todoFilter ? todos.filter((t) => t.state === todoFilter) : todos;
  }, [todos, todoFilter]);

  const handleCreateTodo = (event: React.FormEvent) => {
    event.preventDefault();
    const input = inputRef.current;
    if (!input || !input.value.trim()) return;

    const newValue = input.value.trim();
    setTodos(
      (todos) => [
        ...todos,
        {
          value: newValue, 
          id: crypto.randomUUID(),
          state: TodoState.ACTIVE
        }
      ]
    );
    input.value = "";
  }

  const thStyle: CSSProperties = {
    textAlign: "left",
    padding: "0.5rem",
    backgroundColor: "#f0f0f0",
    borderBottom: "2px solid #ddd",
  };

  const buttonStyle = (isActive: boolean): CSSProperties => {
    return {
      padding: "0.5rem 1rem",
      borderRadius: "6px",
      border: "1px solid #ccc",
      backgroundColor: isActive ? "#efefef" : "#fff",
      marginBottom: "1rem",
    }
  };

  return (
    <div style={style}> 
      <div>
        <h1> TODOs: </h1>
        <h2> Filter by: {todoFilter != null ? todoFilter : "All"}  </h2>
        <div style={{display: "flex", width: "100%", justifyContent: "space-between"}}>
          <button style={buttonStyle(todoFilter===TodoState.ACTIVE)} onClick={() => setTodoFilters(TodoState.ACTIVE)}> Active </button>
          <button style={buttonStyle(todoFilter===TodoState.DELETED)} onClick={() => setTodoFilters(TodoState.DELETED)}> Deleted </button>
          <button style={buttonStyle(todoFilter===TodoState.COMPLETED)} onClick={() => setTodoFilters(TodoState.COMPLETED)}> Completed </button>
          <button style={buttonStyle(todoFilter===null)} onClick={() => setTodoFilters(null)}> ALL </button>
        </div>
        <table style={{ borderCollapse: "collapse", width: "100%", marginBottom: "1rem" }}>
          <thead>
            <tr>
              <th style={thStyle}>Task</th>
              <th style={thStyle}>Status</th>
              <th style={thStyle}>Action</th>
            </tr>
          </thead>
          <tbody>
          {
            filterTodos.map((todo) => (
              <TodoComponent key={todo.id} todo={todo} setTodos={setTodos}/>
            ))
          }
          </tbody>
        </table>
        <form onSubmit={handleCreateTodo}>
          <input ref={inputRef} placeholder={"Add a new todo..."} style={{
            padding: "0.5rem",
            border: "1px solid #ccc",
            borderRadius: "4px",
            width: "300px",
            marginRight: "0.5rem",            
          }}
          />
          <button type="submit" style={{ padding: "0.5rem 1rem", borderRadius: "4px" }}>Add</button>
        </form>
      </div>
    </div>
  );
}

export {
  TODO
}