Aplicaci贸n de Tareas con Laravel, Inertia y Jetstream
Aplicaci贸n de Tareas con Laravel, Inertia y Jetstream
馃搵 Descripci贸n del Proyecto
Crearemos una aplicaci贸n de tareas sencilla que permita:
Agregar m煤ltiples tareas
Ver lista de tareas
Almacenar en un arreglo en la sesi贸n
Sin base de datos, sin tablas, sin modelos
馃殌 1. INSTALACI脫N COMPLETA DESDE CERO
1.1 Requisitos Previos
Antes de comenzar, aseg煤rate de tener instalado:
# Verificar PHP (necesitas PHP 8.0 o superior)
php -v
# Verificar Composer
composer --version
# Verificar Node.js y NPM
node -v
npm -v1.2 Crear el Proyecto Laravel
# 1. Navegar al directorio de proyectos
cd C:\xampp\htdocs # En Windows con XAMPP
# o
cd /var/www/html # En Linux
# o
cd ~/Projects # En Mac
# 2. Crear proyecto Laravel (versi贸n 10 para compatibilidad)
composer create-project laravel/laravel:^10.0 tareas-app
composer create-project laravel/laravel tareas-app
# 3. Entrar al directorio del proyecto
cd tareas-app
# 4. Verificar que todo est茅 bien
php artisan --version
# Deber铆as ver: Laravel Framework 10.x.x1.3 Configurar Variables de Entorno
# 1. Copiar archivo de entorno
copy .env.example .env # En Windows
# o
cp .env.example .env # En Linux/Mac
# 2. Generar clave de aplicaci贸n
php artisan key:generate1.4 Configurar Base de Datos (M铆nimo necesario)
NO necesitamos base de datos para las tareas, pero Jetstream necesita una para los usuarios:
# 1. Crear archivo SQLite
# En Windows:
type nul > database/database.sqlite
# En Linux/Mac:
touch database/database.sqliteEditar .env:
DB_CONNECTION=sqlite
DB_DATABASE=C:\xampp\htdocs\tareas-app\database\database.sqlite
# O en Linux/Mac:
# DB_DATABASE=/ruta/absoluta/a/tareas-app/database/database.sqlite1.5 Instalar Jetstream
# 1. Instalar Jetstream
composer require laravel/jetstream
# 2. Instalar Jetstream con Inertia (SIN TEAMS - m谩s sencillo)
php artisan jetstream:install inertia
# 3. Migrar las tablas de usuarios
php artisan migrate1.6 Instalar Dependencias Frontend
# 1. Instalar paquetes NPM
npm install
# 2. Compilar assets para desarrollo
npm run dev
# 3. En otra terminal, mantener assets compil谩ndose
# npm run dev (dejar corriendo)馃搧 2. ESTRUCTURA DE ARCHIVOS
馃搧 tareas-app/
├── 馃搧 app/
│ └── 馃搧 Http/
│ └── 馃搧 Controllers/
│ └── TareaController.php
├── 馃搧 routes/
│ └── web.php
├── 馃搧 resources/
│ └── 馃搧 js/
│ └── 馃搧 Pages/
│ └── 馃搧 Tareas/
│ └── Index.vue
├── 馃搧 database/
│ └── database.sqlite
├── .env
├── composer.json
└── package.json馃幆 3. CONFIGURAR RUTAS
routes/web.php
<?php
use App\Http\Controllers\TareaController;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;
// Ruta principal
Route::get('/', function () {
return Inertia::render('Welcome');
});
// Ruta dashboard (ya viene con Jetstream)
Route::get('/dashboard', function () {
return Inertia::render('Dashboard');
})->middleware(['auth:sanctum', 'verified'])->name('dashboard');
// RUTAS DE TAREAS (protegidas con autenticaci贸n)
Route::middleware(['auth:sanctum', 'verified'])->group(function () {
// Ver todas las tareas
Route::get('/tareas', [TareaController::class, 'index'])->name('tareas.index');
// Agregar nueva tarea
Route::post('/tareas', [TareaController::class, 'store'])->name('tareas.store');
// Eliminar tarea
Route::delete('/tareas/{id}', [TareaController::class, 'destroy'])->name('tareas.destroy');
// Marcar/Desmarcar tarea completada
Route::put('/tareas/{id}/toggle', [TareaController::class, 'toggle'])->name('tareas.toggle');
// Eliminar todas las tareas
Route::delete('/tareas/clear', [TareaController::class, 'clear'])->name('tareas.clear');
});馃幃 4. CREAR CONTROLADOR
app/Http/Controllers/TareaController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Inertia\Inertia;
class TareaController extends Controller
{
// Obtener tareas del arreglo en sesi贸n
private function getTareas()
{
if (!session()->has('tareas')) {
session()->put('tareas', []);
}
return session()->get('tareas');
}
// Guardar tareas en sesi贸n
private function saveTareas($tareas)
{
session()->put('tareas', $tareas);
}
// Mostrar todas las tareas
public function index()
{
return Inertia::render('Tareas/Index', [
'tareas' => $this->getTareas()
]);
}
// Guardar nueva tarea
public function store(Request $request)
{
// Validar
$request->validate([
'tarea' => 'required|string|max:255'
]);
// Obtener tareas actuales
$tareas = $this->getTareas();
// Agregar nueva tarea al arreglo
$tareas[] = [
'id' => uniqid(), // ID 煤nico
'texto' => $request->tarea, // Texto de la tarea
'completada' => false, // Estado
'fecha' => date('d/m/Y H:i') // Fecha de creaci贸n
];
// Guardar el arreglo actualizado
$this->saveTareas($tareas);
// Redirigir (Inertia har谩 el resto)
return redirect()->route('tareas.index');
}
// Eliminar una tarea espec铆fica
public function destroy($id)
{
$tareas = $this->getTareas();
// Filtrar para eliminar la tarea con el ID dado
$tareas = array_values(array_filter($tareas, function($t) use ($id) {
return $t['id'] != $id;
}));
$this->saveTareas($tareas);
return redirect()->route('tareas.index');
}
// Marcar/Desmarcar tarea como completada
public function toggle($id)
{
$tareas = $this->getTareas();
// Buscar y cambiar estado
foreach ($tareas as &$tarea) {
if ($tarea['id'] == $id) {
$tarea['completada'] = !$tarea['completada'];
break;
}
}
$this->saveTareas($tareas);
return redirect()->route('tareas.index');
}
// Limpiar todas las tareas
public function clear()
{
session()->forget('tareas');
return redirect()->route('tareas.index');
}
}馃帹 5. CREAR COMPONENTE VUE
resources/js/Pages/Tareas/Index.vue
<template>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-xl sm:rounded-lg p-6">
<!-- Encabezado -->
<div class="flex justify-between items-center mb-6">
<h1 class="text-2xl font-bold">馃搵 Mis Tareas</h1>
<div class="flex gap-2">
<!-- Bot贸n limpiar todo -->
<button
v-if="tareas.length > 0"
@click="clearTareas"
class="px-3 py-1 text-sm text-red-500 hover:text-red-700 hover:bg-red-50 rounded transition"
>
馃棏️ Limpiar todo
</button>
<!-- Contador de tareas -->
<span class="px-3 py-1 text-sm bg-gray-100 rounded">
{{ tareas.length }} tareas
</span>
</div>
</div>
<!-- Formulario para agregar tarea -->
<form @submit.prevent="guardarTarea" class="mb-6">
<div class="flex gap-2">
<input
type="text"
v-model="form.tarea"
placeholder="Escribe una nueva tarea..."
class="flex-1 px-4 py-2 rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
:disabled="form.processing"
/>
<button
type="submit"
class="px-6 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition disabled:opacity-50 disabled:cursor-not-allowed"
:disabled="form.processing || !form.tarea.trim()"
>
<span v-if="form.processing">⏳ Guardando...</span>
<span v-else>➕ Agregar</span>
</button>
</div>
<!-- Errores de validaci贸n -->
<div v-if="form.errors.tarea" class="text-red-500 text-sm mt-1">
{{ form.errors.tarea }}
</div>
</form>
<!-- Lista de tareas -->
<div v-if="tareas.length > 0">
<!-- Estad铆sticas -->
<div class="grid grid-cols-3 gap-2 mb-4 text-sm">
<div class="bg-blue-50 p-2 rounded text-center">
<span class="text-blue-600 font-bold">{{ tareas.length }}</span>
<span class="text-gray-600 ml-1">Total</span>
</div>
<div class="bg-yellow-50 p-2 rounded text-center">
<span class="text-yellow-600 font-bold">{{ tareas.filter(t => !t.completada).length }}</span>
<span class="text-gray-600 ml-1">Pendientes</span>
</div>
<div class="bg-green-50 p-2 rounded text-center">
<span class="text-green-600 font-bold">{{ tareas.filter(t => t.completada).length }}</span>
<span class="text-gray-600 ml-1">Completadas</span>
</div>
</div>
<!-- Lista -->
<div class="space-y-3">
<div
v-for="tarea in tareas"
:key="tarea.id"
class="flex items-center justify-between p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition group"
>
<div class="flex items-center gap-3 flex-1 min-w-0">
<!-- Checkbox -->
<input
type="checkbox"
:checked="tarea.completada"
@change="toggleTarea(tarea.id)"
class="w-4 h-4 rounded border-gray-300 text-indigo-600 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
:disabled="form.processing"
/>
<!-- Texto de la tarea -->
<span
class="flex-1 truncate"
:class="{
'line-through text-gray-500': tarea.completada,
'text-gray-800': !tarea.completada
}"
>
{{ tarea.texto }}
</span>
<!-- Fecha -->
<span class="text-xs text-gray-400 flex-shrink-0">
馃晲 {{ tarea.fecha }}
</span>
</div>
<!-- Bot贸n eliminar -->
<button
@click="eliminarTarea(tarea.id)"
class="ml-2 text-sm text-red-400 hover:text-red-700 opacity-0 group-hover:opacity-100 transition"
:disabled="form.processing"
>
❌
</button>
</div>
</div>
</div>
<!-- Mensaje vac铆o -->
<div v-else class="text-center py-12">
<div class="text-6xl mb-4">馃幆</div>
<h3 class="text-lg font-medium text-gray-600 mb-2">¡No hay tareas a煤n!</h3>
<p class="text-gray-400">Agrega tu primera tarea usando el formulario de arriba.</p>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { useForm } from '@inertiajs/vue3';
// Props recibidas del controlador
const props = defineProps({
tareas: {
type: Array,
default: () => []
}
});
// Formulario para nueva tarea
const form = useForm({
tarea: ''
});
// Guardar nueva tarea
function guardarTarea() {
form.post('/tareas', {
preserveState: true,
onSuccess: () => {
form.reset();
}
});
}
// Eliminar tarea
function eliminarTarea(id) {
if (confirm('¿Est谩s seguro de eliminar esta tarea?')) {
form.delete(`/tareas/${id}`, {
preserveState: true
});
}
}
// Cambiar estado completada
function toggleTarea(id) {
form.put(`/tareas/${id}/toggle`, {
preserveState: true
});
}
// Limpiar todas las tareas
function clearTareas() {
if (confirm('¿Eliminar todas las tareas?')) {
form.delete('/tareas/clear', {
preserveState: true
});
}
}
</script>馃彔 6. AGREGAR AL MEN脷 DE NAVEGACI脫N
Editar resources/js/Layouts/AppLayout.vue
Busca la secci贸n de navegaci贸n y agrega:
<!-- Dentro de la navegaci贸n de Jetstream -->
<NavLink :href="route('tareas.index')" :active="route().current('tareas.*')">
馃搵 Tareas
</NavLink>Ejemplo completo de la secci贸n de navegaci贸n:
<div class="hidden sm:flex sm:space-x-8 sm:ml-10">
<NavLink :href="route('dashboard')" :active="route().current('dashboard')">
Dashboard
</NavLink>
<NavLink :href="route('tareas.index')" :active="route().current('tareas.*')">
馃搵 Tareas
</NavLink>
</div>馃殌 7. EJECUTAR LA APLICACI脫N
Terminal 1 - Servidor Laravel
# Iniciar servidor de desarrollo
php artisan serve
# Ver谩s: Server running on http://localhost:8000Terminal 2 - Compilar Assets
# Compilar assets en modo desarrollo
npm run dev
# Dejar corriendo para detectar cambiosTerminal 3 - (Opcional) Base de datos
# Si usas SQLite, no necesitas hacer nada m谩s馃搳 8. VERIFICAR FUNCIONAMIENTO
Paso 1: Abrir el navegador
http://localhost:8000Paso 2: Registrarse
Click en "Register"
Completar datos
Click en "Register"
Paso 3: Ir a Tareas
Click en "馃搵 Tareas" en el men煤
Paso 4: Probar funcionalidades
Agregar tarea: Escribir y click en "Agregar"
Ver lista: Se muestran todas las tareas
Marcar completada: Click en checkbox
Eliminar: Hover sobre tarea y click en ❌
Limpiar todo: Click en "Limpiar todo"
馃幆 9. RESPUESTAS A POSIBLES ERRORES
Error: "Class 'Inertia\Inertia' not found"
composer require inertiajs/inertia-laravelError: "Vite manifest not found"
npm install
npm run buildError: "Jetstream not found"
composer require laravel/jetstream
php artisan jetstream:install inertiaError: "Could not open input file: artisan"
# Aseg煤rate de estar en el directorio correcto
cd tareas-app
dir artisan # Verificar que existe馃搵 10. COMANDOS 脷TILES
# Verificar instalaci贸n
php artisan about
# Limpiar cache
php artisan cache:clear
php artisan config:clear
php artisan view:clear
# Recompilar assets
npm run build
# Reinstalar dependencias
composer install
npm install
# Migrar base de datos (si usas BD)
php artisan migrate
# Crear controlador
php artisan make:controller NombreController
# Crear middleware
php artisan make:middleware NombreMiddleware✅ 11. RESUMEN DE FUNCIONALIDADES
| Funci贸n | M茅todo HTTP | Ruta | Acci贸n |
|---|---|---|---|
| Ver tareas | GET | /tareas | Muestra todas las tareas |
| Agregar | POST | /tareas | A帽ade nueva tarea |
| Eliminar | DELETE | /tareas/{id} | Elimina una tarea |
| Completar | PUT | /tareas/{id}/toggle | Cambia estado completada |
| Limpiar | DELETE | /tareas/clear | Elimina todas |
馃挕 12. VENTAJAS DE ESTE ENFOQUE
✅ Sin base de datos - no necesitas configurar MySQL/PostgreSQL
✅ Sin modelos - solo arreglos simples
✅ R谩pido - sesi贸n en memoria
✅ Portable - funciona en cualquier entorno
✅ F谩cil - ideal para prototipos y pruebas
✅ Autenticaci贸n - Jetstream maneja usuarios
馃帗 13. POSIBLES MEJORAS FUTURAS
Persistencia - Migrar a base de datos
Editar tareas - Agregar endpoint PUT
Prioridades - Campo de prioridad
Categor铆as - Etiquetas para organizar
Fechas l铆mite - Recordatorios
Compartir - Tareas con otros usuarios
馃帀 ¡FELICITACIONES!
Has creado una aplicaci贸n completa de tareas usando:
✅ Laravel 10
✅ Jetstream
✅ Inertia.js
✅ Vue 3
✅ Solo arreglos (sin base de datos)
✅ Autenticaci贸n de usuarios
✅ Dise帽o moderno con Tailwind CSS
¡Todo desde cero!
Comentarios
Publicar un comentario