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, '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, '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, 'orden' => $orden, ]); } else { $pregunta = $evaluacion->preguntas()->create([ 'texto' => $pData['texto'], 'pregunta_tipo_id' => $pData['pregunta_tipo_id'], 'requerida' => $pData['requerida'] ?? true, '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'); } }