mirror of
https://github.com/lukaszraczylo/filepuff-mcp.git
synced 2026-06-06 22:33:42 +00:00
130 lines
3.4 KiB
TypeScript
130 lines
3.4 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
|
|
interface ButtonProps {
|
|
variant?: 'primary' | 'secondary';
|
|
disabled?: boolean;
|
|
onClick?: () => void;
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
/**
|
|
* A reusable button component with Tailwind CSS styling
|
|
*/
|
|
export const Button: React.FC<ButtonProps> = ({
|
|
variant = 'primary',
|
|
disabled = false,
|
|
onClick,
|
|
children
|
|
}) => {
|
|
const baseClasses = 'font-bold py-2 px-4 rounded transition-colors duration-200';
|
|
const variantClasses = {
|
|
primary: 'bg-blue-500 hover:bg-blue-700 text-white',
|
|
secondary: 'bg-gray-500 hover:bg-gray-700 text-white'
|
|
};
|
|
|
|
return (
|
|
<button
|
|
className={`${baseClasses} ${variantClasses[variant]} ${disabled ? 'opacity-50 cursor-not-allowed' : ''}`}
|
|
disabled={disabled}
|
|
onClick={onClick}
|
|
>
|
|
{children}
|
|
</button>
|
|
);
|
|
};
|
|
|
|
interface TodoItem {
|
|
id: number;
|
|
text: string;
|
|
completed: boolean;
|
|
}
|
|
|
|
/**
|
|
* Todo list component demonstrating React hooks and Tailwind
|
|
*/
|
|
export const TodoList: React.FC = () => {
|
|
const [todos, setTodos] = useState<TodoItem[]>([
|
|
{ id: 1, text: 'Learn React', completed: true },
|
|
{ id: 2, text: 'Learn TypeScript', completed: true },
|
|
{ id: 3, text: 'Build amazing apps', completed: false }
|
|
]);
|
|
const [inputValue, setInputValue] = useState('');
|
|
|
|
useEffect(() => {
|
|
console.log('Todos updated:', todos);
|
|
}, [todos]);
|
|
|
|
const addTodo = () => {
|
|
if (inputValue.trim()) {
|
|
const newTodo: TodoItem = {
|
|
id: Date.now(),
|
|
text: inputValue,
|
|
completed: false
|
|
};
|
|
setTodos([...todos, newTodo]);
|
|
setInputValue('');
|
|
}
|
|
};
|
|
|
|
const toggleTodo = (id: number) => {
|
|
setTodos(todos.map(todo =>
|
|
todo.id === id ? { ...todo, completed: !todo.completed } : todo
|
|
));
|
|
};
|
|
|
|
const deleteTodo = (id: number) => {
|
|
setTodos(todos.filter(todo => todo.id !== id));
|
|
};
|
|
|
|
return (
|
|
<div className="container mx-auto px-4 py-8 max-w-2xl">
|
|
<h1 className="text-3xl font-bold text-gray-800 mb-6">
|
|
My Todo List
|
|
</h1>
|
|
|
|
<div className="flex gap-2 mb-6">
|
|
<input
|
|
type="text"
|
|
value={inputValue}
|
|
onChange={(e) => setInputValue(e.target.value)}
|
|
onKeyPress={(e) => e.key === 'Enter' && addTodo()}
|
|
placeholder="Add a new todo..."
|
|
className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
/>
|
|
<Button onClick={addTodo}>Add</Button>
|
|
</div>
|
|
|
|
<ul className="space-y-2">
|
|
{todos.map(todo => (
|
|
<li
|
|
key={todo.id}
|
|
className="flex items-center gap-3 p-4 bg-white rounded-lg shadow-sm hover:shadow-md transition-shadow"
|
|
>
|
|
<input
|
|
type="checkbox"
|
|
checked={todo.completed}
|
|
onChange={() => toggleTodo(todo.id)}
|
|
className="w-5 h-5 text-blue-600 rounded focus:ring-2 focus:ring-blue-500"
|
|
/>
|
|
<span className={`flex-1 ${todo.completed ? 'line-through text-gray-400' : 'text-gray-700'}`}>
|
|
{todo.text}
|
|
</span>
|
|
<Button
|
|
variant="secondary"
|
|
onClick={() => deleteTodo(todo.id)}
|
|
>
|
|
Delete
|
|
</Button>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
{todos.length === 0 && (
|
|
<div className="text-center py-12 text-gray-400">
|
|
No todos yet. Add one above!
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|