Files
Sistema-Educativo-Laravel/app/Livewire/EvaluacionBuilder.php
T

218 lines
9.3 KiB
PHP

<?php
namespace App\Livewire;
use App\Models\Evaluacion;
use App\Models\EvaluacionTipo;
use App\Models\Nivel;
use App\Models\Plantel;
use App\Models\Pregunta;
use App\Models\PreguntaOpcion;
use App\Models\PreguntaTipo;
use Illuminate\Support\Facades\DB;
use Livewire\Component;
class EvaluacionBuilder extends Component
{
public ?int $evaluacionId = null;
public string $titulo = '';
public string $descripcion = '';
public string $evaluacionTipoId = '';
public string $status = 'borrador';
public string $fechaInicio = '';
public string $fechaFin = '';
public bool $anonima = false;
public array $selectedPlanteles = [];
public array $selectedNiveles = [];
public array $preguntas = [];
// Catálogos cargados en mount (solo lectura en la vista)
public array $tipos = [];
public array $preguntaTipos = [];
public array $planteles = [];
public array $niveles = [];
public function mount($evaluacion = null): void
{
$this->tipos = EvaluacionTipo::all(['id', 'nombre', 'slug'])->toArray();
$this->preguntaTipos = PreguntaTipo::all(['id', 'nombre', 'slug'])->toArray();
$this->planteles = Plantel::orderBy('name')->get(['id', 'name'])->toArray();
$this->niveles = Nivel::orderBy('name')->get(['id', 'name'])->toArray();
if ($evaluacion instanceof Evaluacion && $evaluacion->exists) {
$this->evaluacionId = $evaluacion->id;
$this->titulo = $evaluacion->titulo;
$this->descripcion = $evaluacion->descripcion ?? '';
$this->evaluacionTipoId = (string) $evaluacion->evaluacion_tipo_id;
$this->status = $evaluacion->status;
$this->fechaInicio = $evaluacion->fecha_inicio?->format('Y-m-d') ?? '';
$this->fechaFin = $evaluacion->fecha_fin?->format('Y-m-d') ?? '';
$this->anonima = (bool) $evaluacion->anonima;
$this->selectedPlanteles = $evaluacion->planteles->pluck('id')->map(fn($v) => (string) $v)->toArray();
$this->selectedNiveles = $evaluacion->niveles->pluck('id')->map(fn($v) => (string) $v)->toArray();
$this->preguntas = $evaluacion->preguntas->map(fn($p) => [
'id' => $p->id,
'texto' => $p->texto,
'pregunta_tipo_id' => (string) $p->pregunta_tipo_id,
'requerida' => (bool) $p->requerida,
'es_docente' => (bool) $p->es_docente,
'opciones' => $p->opciones->map(fn($o) => [
'id' => $o->id,
'texto' => $o->texto,
])->toArray(),
])->toArray();
}
}
public function addPregunta(): void
{
$defaultTipo = $this->preguntaTipos[0]['id'] ?? '';
$this->preguntas[] = [
'id' => null,
'texto' => '',
'pregunta_tipo_id' => (string) $defaultTipo,
'requerida' => true,
'es_docente' => false,
'opciones' => [],
];
}
public function removePregunta(int $index): void
{
array_splice($this->preguntas, $index, 1);
$this->preguntas = array_values($this->preguntas);
}
public function movePregunta(int $index, string $direction): void
{
$swap = $direction === 'up' ? $index - 1 : $index + 1;
if ($swap < 0 || $swap >= count($this->preguntas)) {
return;
}
[$this->preguntas[$index], $this->preguntas[$swap]] = [$this->preguntas[$swap], $this->preguntas[$index]];
}
public function addOpcion(int $preguntaIndex): void
{
$this->preguntas[$preguntaIndex]['opciones'][] = ['id' => null, 'texto' => ''];
}
public function removeOpcion(int $preguntaIndex, int $opcionIndex): void
{
array_splice($this->preguntas[$preguntaIndex]['opciones'], $opcionIndex, 1);
$this->preguntas[$preguntaIndex]['opciones'] = array_values($this->preguntas[$preguntaIndex]['opciones']);
}
public function guardar(): void
{
$this->validate([
'titulo' => 'required|string|max:255',
'descripcion' => 'nullable|string',
'evaluacionTipoId' => 'required|exists:evaluacion_tipos,id',
'status' => 'required|in:borrador,activa,cerrada',
'fechaInicio' => 'nullable|date',
'fechaFin' => 'nullable|date',
'selectedPlanteles' => 'array',
'selectedNiveles' => 'array',
'preguntas' => 'required|array|min:1',
'preguntas.*.texto' => 'required|string|max:1000',
'preguntas.*.pregunta_tipo_id' => 'required|exists:pregunta_tipos,id',
'preguntas.*.opciones.*.texto' => 'required|string|max:500',
], [
'titulo.required' => 'El título es obligatorio.',
'evaluacionTipoId.required' => 'Selecciona el tipo de evaluación.',
'preguntas.required' => 'Agrega al menos una pregunta.',
'preguntas.min' => 'Agrega al menos una pregunta.',
'preguntas.*.texto.required' => 'El texto de la pregunta es obligatorio.',
'preguntas.*.pregunta_tipo_id.required' => 'Selecciona el tipo de respuesta.',
'preguntas.*.opciones.*.texto.required' => 'El texto de la opción es obligatorio.',
]);
DB::transaction(function () {
$data = [
'titulo' => $this->titulo,
'descripcion' => $this->descripcion ?: null,
'evaluacion_tipo_id' => $this->evaluacionTipoId,
'status' => $this->status,
'fecha_inicio' => $this->fechaInicio ?: null,
'fecha_fin' => $this->fechaFin ?: null,
'anonima' => $this->anonima,
];
if ($this->evaluacionId) {
$evaluacion = Evaluacion::findOrFail($this->evaluacionId);
$evaluacion->update($data);
} else {
$evaluacion = Evaluacion::create($data);
$this->evaluacionId = $evaluacion->id;
}
$evaluacion->planteles()->sync($this->selectedPlanteles);
$evaluacion->niveles()->sync($this->selectedNiveles);
// Eliminar preguntas removidas
$keptIds = collect($this->preguntas)->pluck('id')->filter()->values()->toArray();
$evaluacion->preguntas()->whereNotIn('id', $keptIds)->delete();
foreach ($this->preguntas as $orden => $pData) {
if (!empty($pData['id'])) {
$pregunta = Pregunta::findOrFail($pData['id']);
$pregunta->update([
'texto' => $pData['texto'],
'pregunta_tipo_id' => $pData['pregunta_tipo_id'],
'requerida' => $pData['requerida'] ?? true,
'es_docente' => $pData['es_docente'] ?? false,
'orden' => $orden,
]);
} else {
$pregunta = $evaluacion->preguntas()->create([
'texto' => $pData['texto'],
'pregunta_tipo_id' => $pData['pregunta_tipo_id'],
'requerida' => $pData['requerida'] ?? true,
'es_docente' => $pData['es_docente'] ?? false,
'orden' => $orden,
]);
}
$tipoSlug = collect($this->preguntaTipos)
->firstWhere('id', (int) $pData['pregunta_tipo_id'])['slug'] ?? '';
if ($tipoSlug === 'opcion_multiple') {
$keptOpcionIds = collect($pData['opciones'] ?? [])
->pluck('id')->filter()->values()->toArray();
$pregunta->opciones()->whereNotIn('id', $keptOpcionIds)->delete();
foreach ($pData['opciones'] ?? [] as $opOrden => $opData) {
if (!empty($opData['id'])) {
PreguntaOpcion::findOrFail($opData['id'])->update([
'texto' => $opData['texto'],
'orden' => $opOrden,
]);
} else {
$pregunta->opciones()->create([
'texto' => $opData['texto'],
'orden' => $opOrden,
]);
}
}
} else {
$pregunta->opciones()->delete();
}
}
});
session()->flash('info', 'Evaluación guardada correctamente.');
$this->redirect(route('evaluacion.index'));
}
public function render()
{
return view('livewire.evaluacion-builder');
}
}