feat: módulo de evaluaciones para admin con constructor dinámico

- Modelos completos: Evaluacion, EvaluacionTipo, Pregunta, PreguntaTipo,
  PreguntaOpcion, EvaluacionSubmission, Respuesta (con $table explícito
  para nombres en español que Laravel pluraliza incorrectamente)
- Relaciones evaluaciones() añadidas a Plantel y Nivel
- EvaluacionBuilder (Livewire): constructor interactivo con preguntas
  de tipo texto libre, opción múltiple, escala 1-5/1-10 y sí/no;
  reorden con flechas, vista previa por tipo, selección de planteles
  y niveles, configuración de estado, fechas y flag de anónima
- EvaluacionController: CRUD completo + toggleStatus
- Vistas: index, create/edit (con Livewire builder), show con detalle
  de preguntas y cambio de estado
- Rutas resource /evaluacion + PATCH /evaluacion/{id}/status
- Migraciones: evaluaciones, preguntas, pregunta_opciones,
  evaluacion_submissions, respuestas, evaluacion_plantel, evaluacion_nivel
- Menú: enlace Evaluaciones en panel de Administrador

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-30 18:09:48 -06:00
parent f81237b23c
commit 2af36ea272
27 changed files with 1471 additions and 21 deletions
@@ -0,0 +1,21 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void {
Schema::create('evaluacion_tipos', function (Blueprint $table) {
$table->id();
$table->string('nombre');
$table->string('slug')->unique();
});
DB::table('evaluacion_tipos')->insert([
['nombre' => 'Docente', 'slug' => 'docente'],
['nombre' => 'Alumno', 'slug' => 'alumno'],
['nombre' => 'Personal administrativo', 'slug' => 'administrativo'],
]);
}
public function down(): void { Schema::dropIfExists('evaluacion_tipos'); }
};
@@ -0,0 +1,23 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void {
Schema::create('pregunta_tipos', function (Blueprint $table) {
$table->id();
$table->string('nombre');
$table->string('slug')->unique();
});
DB::table('pregunta_tipos')->insert([
['nombre' => 'Texto libre', 'slug' => 'texto_libre'],
['nombre' => 'Opcion multiple', 'slug' => 'opcion_multiple'],
['nombre' => 'Escala del 1 al 5', 'slug' => 'escala_5'],
['nombre' => 'Escala del 1 al 10', 'slug' => 'escala_10'],
['nombre' => 'Si o No', 'slug' => 'si_no'],
]);
}
public function down(): void { Schema::dropIfExists('pregunta_tipos'); }
};
@@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void {
Schema::create('evaluaciones', function (Blueprint $table) {
$table->id();
$table->string('titulo');
$table->text('descripcion')->nullable();
$table->unsignedBigInteger('evaluacion_tipo_id');
$table->enum('status', ['borrador', 'activa', 'cerrada'])->default('borrador');
$table->date('fecha_inicio')->nullable();
$table->date('fecha_fin')->nullable();
$table->boolean('anonima')->default(false);
$table->timestamps();
$table->foreign('evaluacion_tipo_id')->references('id')->on('evaluacion_tipos');
});
}
public function down(): void { Schema::dropIfExists('evaluaciones'); }
};
@@ -0,0 +1,21 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void {
Schema::create('preguntas', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('evaluacion_id');
$table->unsignedBigInteger('pregunta_tipo_id');
$table->text('texto');
$table->unsignedSmallInteger('orden')->default(0);
$table->boolean('requerida')->default(true);
$table->timestamps();
$table->foreign('evaluacion_id')->references('id')->on('evaluaciones')->onDelete('cascade');
$table->foreign('pregunta_tipo_id')->references('id')->on('pregunta_tipos');
});
}
public function down(): void { Schema::dropIfExists('preguntas'); }
};
@@ -0,0 +1,17 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void {
Schema::create('pregunta_opciones', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('pregunta_id');
$table->string('texto');
$table->unsignedSmallInteger('orden')->default(0);
$table->foreign('pregunta_id')->references('id')->on('preguntas')->onDelete('cascade');
});
}
public function down(): void { Schema::dropIfExists('pregunta_opciones'); }
};
@@ -0,0 +1,19 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void {
Schema::create('evaluacion_submissions', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('evaluacion_id');
$table->unsignedBigInteger('user_id');
$table->timestamps();
$table->unique(['evaluacion_id', 'user_id']);
$table->foreign('evaluacion_id')->references('id')->on('evaluaciones')->onDelete('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
}
public function down(): void { Schema::dropIfExists('evaluacion_submissions'); }
};
@@ -0,0 +1,21 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void {
Schema::create('respuestas', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('submission_id');
$table->unsignedBigInteger('pregunta_id');
$table->text('valor')->nullable();
$table->unsignedBigInteger('opcion_id')->nullable();
$table->timestamps();
$table->foreign('submission_id')->references('id')->on('evaluacion_submissions')->onDelete('cascade');
$table->foreign('pregunta_id')->references('id')->on('preguntas')->onDelete('cascade');
$table->foreign('opcion_id')->references('id')->on('pregunta_opciones')->nullOnDelete();
});
}
public function down(): void { Schema::dropIfExists('respuestas'); }
};
@@ -0,0 +1,18 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void {
Schema::dropIfExists('evaluacion_plantel');
Schema::create('evaluacion_plantel', function (Blueprint $table) {
$table->unsignedBigInteger('evaluacion_id');
$table->unsignedBigInteger('plantel_id');
$table->primary(['evaluacion_id', 'plantel_id']);
$table->foreign('evaluacion_id')->references('id')->on('evaluaciones')->onDelete('cascade');
$table->foreign('plantel_id')->references('id')->on('plantels');
});
}
public function down(): void { Schema::dropIfExists('evaluacion_plantel'); }
};
@@ -0,0 +1,18 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void {
Schema::dropIfExists('evaluacion_nivel');
Schema::create('evaluacion_nivel', function (Blueprint $table) {
$table->unsignedBigInteger('evaluacion_id');
$table->unsignedBigInteger('nivel_id');
$table->primary(['evaluacion_id', 'nivel_id']);
$table->foreign('evaluacion_id')->references('id')->on('evaluaciones')->onDelete('cascade');
$table->foreign('nivel_id')->references('id')->on('nivels');
});
}
public function down(): void { Schema::dropIfExists('evaluacion_nivel'); }
};