14-Tutorial Paso a Paso SIMPLIFICADO - Gestor de Tareas (Sin Edición)-api
Tutorial Paso a Paso - Gestor de Tareas con API (Versión Súper Simplificada)
Perfecto, voy a crear una versión ultra simplificada que incluye la conexión a la API y mantiene solo el CSS Grid esencial. El alumno podrá completarlo en 1 hora.
🚀 Paso 1: Configuración Inicial (5 minutos)
# 1.1 Crear proyecto React npx create-react-app gestor-tareas-api cd gestor-tareas-api # 1.2 Eliminar archivos innecesarios rm src/App.test.js src/logo.svg src/reportWebVitals.js src/setupTests.js # 1.3 Iniciar el proyecto npm start
📁 Paso 2: Crear Estructura de Carpetas (2 minutos)
src/ ├── components/ │ ├── TareaList.js │ ├── TareaForm.js │ └── TareaItem.js ├── services/ │ └── tareaService.js ├── App.js └── index.js
📄 Paso 3: index.js (1 minuto)
// src/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 4: App.js inicial (2 minutos)
// src/App.js import React from 'react'; import TareaList from './components/TareaList'; function App() { return ( <div> <h1>📝 Gestor de Tareas con API</h1> <TareaList /> </div> ); } export default App;
✅ Verificación: Verás el título en el navegador
🔌 Paso 5: Crear el Servicio de API (10 minutos)
// src/services/tareaService.js // URL de la API Laravel (PUERTO 8000) const API_URL = 'http://localhost:8000/api/tareas'; // Servicio para manejar todas las operaciones con la API const tareaService = { // 1. Obtener todas las tareas async obtenerTodas() { try { console.log('Obteniendo tareas desde:', API_URL); const respuesta = await fetch(API_URL); const datos = await respuesta.json(); return datos.data || datos; } catch (error) { console.error('Error al obtener tareas:', error); throw error; } }, // 2. Crear nueva tarea async crearTarea(tarea) { try { const respuesta = await fetch(API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(tarea), }); return await respuesta.json(); } catch (error) { console.error('Error al crear tarea:', error); throw error; } }, // 3. Actualizar tarea (para marcar como completada) async actualizarTarea(id, datos) { try { const respuesta = await fetch(`${API_URL}/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(datos), }); return await respuesta.json(); } catch (error) { console.error('Error al actualizar tarea:', error); throw error; } }, // 4. Eliminar tarea async eliminarTarea(id) { try { const respuesta = await fetch(`${API_URL}/${id}`, { method: 'DELETE', }); return await respuesta.json(); } catch (error) { console.error('Error al eliminar tarea:', error); throw error; } }, }; export default tareaService;
📋 Paso 6: TareaList.js con CSS Grid (15 minutos)
// src/components/TareaList.js import React, { useState, useEffect } from 'react'; import TareaForm from './TareaForm'; import TareaItem from './TareaItem'; import tareaService from '../services/tareaService'; function TareaList() { const [tareas, setTareas] = useState([]); const [cargando, setCargando] = useState(true); const [error, setError] = useState(''); // CSS Grid para estructura básica const estiloContenedor = { display: 'grid', gridTemplateColumns: '300px 1fr', // 2 columnas gap: '20px', padding: '20px' }; const estiloColumna = { border: '2px solid #333', padding: '15px' }; // Cargar tareas al iniciar useEffect(() => { cargarTareas(); }, []); // Función para cargar tareas desde la API const cargarTareas = async () => { try { setCargando(true); setError(''); const datos = await tareaService.obtenerTodas(); setTareas(datos); } catch (error) { setError('❌ Error: No se pudo conectar a la API. ¿Está corriendo Laravel en puerto 8000?'); } finally { setCargando(false); } }; // Función para crear tarea const manejarCrearTarea = async (nuevaTarea) => { try { await tareaService.crearTarea(nuevaTarea); cargarTareas(); // Recargar la lista } catch (error) { alert('Error al crear la tarea'); } }; // Función para eliminar tarea const manejarEliminarTarea = async (id) => { if (window.confirm('¿Eliminar esta tarea?')) { try { await tareaService.eliminarTarea(id); cargarTareas(); } catch (error) { alert('Error al eliminar la tarea'); } } }; // Función para marcar como completada const manejarCompletarTarea = async (id, completada) => { try { await tareaService.actualizarTarea(id, { completada }); cargarTareas(); } catch (error) { alert('Error al actualizar la tarea'); } }; return ( <div style={estiloContenedor}> {/* COLUMNA IZQUIERDA: Formulario */} <div style={estiloColumna}> <h2>➕ Nueva Tarea</h2> <TareaForm onSubmit={manejarCrearTarea} /> <div style={{ marginTop: '20px' }}> <button onClick={cargarTareas} style={{ width: '100%', padding: '10px' }}> 🔄 Recargar Tareas </button> </div> </div> {/* COLUMNA DERECHA: Lista de tareas */} <div style={estiloColumna}> <h2>📋 Lista de Tareas</h2> {error && ( <div style={{ background: '#ffebee', color: '#c62828', padding: '10px', marginBottom: '15px' }}> {error} </div> )} {cargando ? ( <p>Cargando tareas desde API...</p> ) : tareas.length === 0 ? ( <p>No hay tareas. ¡Crea tu primera tarea!</p> ) : ( <> <p>Total: {tareas.length} tareas</p> {tareas.map((tarea) => ( <TareaItem key={tarea.id} tarea={tarea} onEliminar={manejarEliminarTarea} onCompletar={manejarCompletarTarea} /> ))} </> )} </div> </div> ); } export default TareaList;
✏️ Paso 7: TareaForm.js (10 minutos)
// src/components/TareaForm.js import React, { useState } from 'react'; function TareaForm({ onSubmit }) { const [formData, setFormData] = useState({ titulo: '', descripcion: '', completada: false }); const [error, setError] = useState(''); const manejarCambio = (e) => { const { name, value, type, checked } = e.target; setFormData({ ...formData, [name]: type === 'checkbox' ? checked : value }); }; const manejarSubmit = (e) => { e.preventDefault(); if (!formData.titulo.trim()) { setError('El título es obligatorio'); return; } onSubmit(formData); setFormData({ titulo: '', descripcion: '', completada: false }); setError(''); }; return ( <form onSubmit={manejarSubmit}> {error && <p style={{ color: 'red' }}>{error}</p>} <div style={{ marginBottom: '15px' }}> <label style={{ display: 'block', marginBottom: '5px' }}> Título: </label> <input type="text" name="titulo" value={formData.titulo} onChange={manejarCambio} placeholder="Ej: Comprar leche" style={{ width: '100%', padding: '8px' }} /> </div> <div style={{ marginBottom: '15px' }}> <label style={{ display: 'block', marginBottom: '5px' }}> Descripción: </label> <textarea name="descripcion" value={formData.descripcion} onChange={manejarCambio} placeholder="Ej: Ir al supermercado" rows="3" style={{ width: '100%', padding: '8px' }} /> </div> <div style={{ marginBottom: '15px' }}> <label> <input type="checkbox" name="completada" checked={formData.completada} onChange={manejarCambio} /> <span style={{ marginLeft: '5px' }}>Marcar como completada</span> </label> </div> <button type="submit" style={{ width: '100%', padding: '10px', backgroundColor: '#4CAF50', color: 'white', border: 'none' }} > ➕ Crear Tarea </button> </form> ); } export default TareaForm;
📦 Paso 8: TareaItem.js (10 minutos)
// src/components/TareaItem.js import React from 'react'; function TareaItem({ tarea, onEliminar, onCompletar }) { const estiloTarea = { border: '1px solid #ccc', padding: '15px', marginBottom: '10px', backgroundColor: tarea.completada ? '#e8f5e9' : '#fff' }; const estiloBotones = { display: 'grid', gridTemplateColumns: '1fr 1fr', // CSS Grid para 2 botones gap: '10px', marginTop: '10px' }; return ( <div style={estiloTarea}> <h3 style={{ marginTop: 0 }}> {tarea.completada ? '✅ ' : '⏳ '} {tarea.titulo} </h3> <p>{tarea.descripcion}</p> <div style={{ color: '#666', fontSize: '14px', marginBottom: '10px' }}> ID: {tarea.id} | Estado: {tarea.completada ? 'Completada' : 'Pendiente'} </div> <div style={estiloBotones}> <button onClick={() => onCompletar(tarea.id, !tarea.completada)} style={{ padding: '8px', backgroundColor: tarea.completada ? '#ff9800' : '#4CAF50', color: 'white', border: 'none' }} > {tarea.completada ? '↩️ Desmarcar' : '✅ Completar'} </button> <button onClick={() => onEliminar(tarea.id)} style={{ padding: '8px', backgroundColor: '#f44336', color: 'white', border: 'none' }} > 🗑️ Eliminar </button> </div> </div> ); } export default TareaItem;
🎯 Paso 9: Verificación Final (5 minutos)
Para probar la aplicación:
Asegúrate que la API Laravel esté corriendo:
# En la terminal de Laravel: php artisan serve --port=8000
Ejecuta la app React:
# En la terminal de React: npm start
Verifica en el navegador (http://localhost:3000):
📝 Gestor de Tareas con API ┌──────────────────────────────────────┬──────────────────────────────────────┐ │ │ │ │ ➕ NUEVA TAREA │ 📋 LISTA DE TAREAS │ │ │ │ │ [Título] _______________ │ Total: X tareas │ │ │ │ │ [Descripción] _________ │ • ✅ Comprar leche │ │ ______ │ Ir al supermercado │ │ │ ID: 1 | Estado: Pendiente │ │ [ ] Marcar como completada │ [✅ Completar] [🗑️ Eliminar] │ │ │ │ │ [➕ Crear Tarea] │ • ✅ Estudiar React │ │ │ Hacer ejercicios │ │ [🔄 Recargar Tareas] │ ID: 2 | Estado: Completada │ │ │ [↩️ Desmarcar] [🗑️ Eliminar] │ └──────────────────────────────────────┴──────────────────────────────────────┘
📊 Resumen de Tiempo (60 minutos total)
| Paso | Tiempo | Descripción |
|---|---|---|
| 1 | 5 min | Configuración inicial del proyecto |
| 2 | 2 min | Crear estructura de carpetas |
| 3 | 1 min | Archivo index.js |
| 4 | 2 min | App.js básico |
| 5 | 10 min | Servicio de API (tareaService.js) |
| 6 | 15 min | Componente principal con CSS Grid |
| 7 | 10 min | Formulario de creación |
| 8 | 10 min | Componente de tarea individual |
| 9 | 5 min | Verificación y pruebas |
✅ Lo que el alumno logró en 1 hora:
Proyecto React funcional con estructura organizada
Conexión a API Laravel usando Fetch API nativo
Layout con CSS Grid de 2 columnas
Operaciones CRUD completas:
✅ Listar tareas desde API
✅ Crear nuevas tareas
✅ Marcar como completadas
✅ Eliminar tareas
Manejo de estados (loading, error, data)
Componentes reutilizables
🎯 Características clave de esta versión:
Simplificado:
Solo CSS Grid para estructura (sin estilos complejos)
Sin funcionalidad de edición (solo crear/eliminar/marcar)
Fetch API nativo (sin axios u otras dependencias)
Funcional:
Conexión real a API Laravel
Manejo de errores básico
Feedback visual para el usuario
Educativo:
Cada componente tiene una responsabilidad clara
Código comentado y fácil de seguir
Aprende Fetch API paso a paso
🔧 Si hay problemas de CORS:
Agrega esto al package.json:
{ "name": "gestor-tareas-api", "version": "0.1.0", "private": true, "proxy": "http://localhost:8000", "dependencies": { "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" } }
Y actualiza el servicio:
// En tareaService.js const API_URL = '/api/tareas'; // Usa ruta relativa con proxy
¡Perfecto! El alumno ahora tiene un gestor de tareas completo que se conecta a una API real, usando solo tecnologías nativas (React, Fetch API, CSS Grid), y puede explicar cada parte del código en menos de 1 hora.
Comentarios
Publicar un comentario