react-datosTutorial Paso a Paso COMPLETO - Gestor de Tareas con edicion
Tutorial Minimalista de Gestor de Tareas React
Aquí tienes la versión más minimalista del CSS para que el estudiante pueda copiarlo en una hora:
🚀 Paso 1: Configuración Inicial
npx create-react-app gestor-tareas
cd gestor-tareas
npm startEliminar: App.test.js, logo.svg, reportWebVitals.js, setupTests.js
Estructura:
src/
├── components/
│ ├── TareaList.js
│ ├── TareaForm.js
│ └── TareaItem.js
├── App.js
└── index.js📄 Paso 2: index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);📦 Paso 3: App.js Básico
import React from 'react';
import TareaList from './components/TareaList';
function App() {
return (
<div style={{ padding: '20px', maxWidth: '1000px', margin: '0 auto' }}>
<h1 style={{ textAlign: 'center' }}>📝 Gestor de Tareas</h1>
<TareaList />
</div>
);
}
export default App;📋 Paso 4: TareaList.js
import React, { useState } from 'react';
import TareaForm from './TareaForm';
import TareaItem from './TareaItem';
function TareaList() {
const [tareas, setTareas] = useState([
{ id: 1, titulo: 'Comprar leche', descripcion: 'Ir al supermercado', completada: false },
{ id: 2, titulo: 'Estudiar React', descripcion: 'Hacer ejercicios', completada: true },
]);
const [editando, setEditando] = useState(null);
const crearTarea = (datos) => {
const nuevaTarea = {
...datos,
id: Date.now(),
};
setTareas([...tareas, nuevaTarea]);
};
const actualizarTarea = (id, datos) => {
setTareas(tareas.map(t => t.id === id ? { ...t, ...datos } : t));
setEditando(null);
};
const eliminarTarea = (id) => {
if (window.confirm('¿Eliminar tarea?')) {
setTareas(tareas.filter(t => t.id !== id));
}
};
const completarTarea = (id, completada) => {
setTareas(tareas.map(t => t.id === id ? { ...t, completada } : t));
};
return (
<div style={{ display: 'grid', gridTemplateColumns: '300px 1fr', gap: '20px' }}>
<TareaForm
onSubmit={editando ? (datos) => actualizarTarea(editando.id, datos) : crearTarea}
initialData={editando}
onCancel={() => setEditando(null)}
/>
<div style={{ border: '1px solid #ccc', padding: '20px' }}>
<h2>Lista de Tareas ({tareas.length})</h2>
{tareas.map(tarea => (
<TareaItem
key={tarea.id}
tarea={tarea}
onEdit={setEditando}
onDelete={eliminarTarea}
onToggle={completarTarea}
/>
))}
</div>
</div>
);
}
export default TareaList;✏️ Paso 5: TareaForm.js
import React, { useState } from 'react';
function TareaForm({ onSubmit, initialData = null, onCancel }) {
const [formData, setFormData] = useState({
titulo: initialData?.titulo || '',
descripcion: initialData?.descripcion || '',
completada: initialData?.completada || false,
});
const [error, setError] = useState('');
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
setFormData(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value,
}));
};
const handleSubmit = (e) => {
e.preventDefault();
if (!formData.titulo.trim()) {
setError('El título es obligatorio');
return;
}
onSubmit(formData);
if (!initialData) {
setFormData({ titulo: '', descripcion: '', completada: false });
}
setError('');
};
return (
<div style={{ border: '1px solid #ccc', padding: '20px' }}>
<h2>{initialData ? 'Editar Tarea' : 'Nueva Tarea'}</h2>
{error && <p style={{ color: 'red' }}>{error}</p>}
<form onSubmit={handleSubmit}>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px' }}>Título:</label>
<input
name="titulo"
value={formData.titulo}
onChange={handleChange}
placeholder="Título de la tarea"
style={{ width: '100%', padding: '8px', border: '1px solid #ccc' }}
/>
</div>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px' }}>Descripción:</label>
<textarea
name="descripcion"
value={formData.descripcion}
onChange={handleChange}
placeholder="Descripción"
rows="3"
style={{ width: '100%', padding: '8px', border: '1px solid #ccc' }}
/>
</div>
<div style={{ marginBottom: '15px' }}>
<label>
<input
type="checkbox"
name="completada"
checked={formData.completada}
onChange={handleChange}
/>
Completada
</label>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px' }}>
<button type="submit">
{initialData ? 'Guardar' : 'Crear'}
</button>
{onCancel && (
<button type="button" onClick={onCancel}>
Cancelar
</button>
)}
</div>
</form>
</div>
);
}
export default TareaForm;📦 Paso 6: TareaItem.js
import React from 'react';
function TareaItem({ tarea, onEdit, onDelete, onToggle }) {
return (
<div style={{ border: '1px solid #ccc', padding: '15px', marginBottom: '10px' }}>
<h3>{tarea.titulo}</h3>
<p>{tarea.descripcion}</p>
<p>Estado: {tarea.completada ? '✅ Completada' : '⏳ Pendiente'}</p>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '10px', marginTop: '10px' }}>
<button onClick={() => onToggle(tarea.id, !tarea.completada)}>
{tarea.completada ? 'Desmarcar' : 'Completar'}
</button>
<button onClick={() => onEdit(tarea)}>Editar</button>
<button onClick={() => onDelete(tarea.id)}>Eliminar</button>
</div>
</div>
);
}
export default TareaItem;🎯 CSS Grid Resumen (Incluido en el código):
Layout principal:
grid-template-columns: '300px 1fr'(2 columnas)Botones del formulario:
grid-template-columns: '1fr 1fr'(2 columnas iguales)Botones de tarea:
grid-template-columns: 'repeat(3, 1fr)'(3 columnas iguales)
¡Listo! El estudiante tiene un gestor de tareas funcional con CSS Grid en menos de 1 hora.
Comentarios
Publicar un comentario