Files
Sistema-Educativo-Laravel/resources/views/livewire/evaluacion-builder.blade.php

434 lines
23 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<div>
{{-- Mensajes de error globales --}}
@if($errors->any())
<div class="alert alert-danger alert-dismissible fade show mb-3">
<strong>Por favor corrige los siguientes errores:</strong>
<ul class="mb-0 mt-1">
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
<button type="button" class="close" data-dismiss="alert"><span>&times;</span></button>
</div>
@endif
<div class="row">
{{-- ===================== COLUMNA PRINCIPAL ===================== --}}
<div class="col-lg-8">
{{-- Info general --}}
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-info-circle mr-1"></i> Información general
</h6>
</div>
<div class="card-body">
<div class="form-group">
<label>Título <span class="text-danger">*</span></label>
<input type="text"
wire:model="titulo"
class="form-control @error('titulo') is-invalid @enderror"
placeholder="Ej: Evaluación docente 1er semestre 2026">
@error('titulo') <div class="invalid-feedback">{{ $message }}</div> @enderror
</div>
<div class="form-group">
<label>Descripción / Instrucciones</label>
<textarea wire:model="descripcion"
class="form-control"
rows="3"
placeholder="Instrucciones opcionales para los evaluadores…"></textarea>
</div>
</div>
</div>
{{-- ================== CONSTRUCTOR DE PREGUNTAS ================== --}}
<div class="card shadow mb-4">
<div class="card-header py-3 d-flex align-items-center justify-content-between">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-list-ol mr-1"></i>
Preguntas <span class="badge badge-primary ml-1">{{ count($preguntas) }}</span>
</h6>
<button type="button" wire:click="addPregunta"
class="btn btn-sm btn-outline-primary">
<i class="fas fa-plus mr-1"></i> Agregar pregunta
</button>
</div>
<div class="card-body">
@error('preguntas')
<div class="alert alert-warning py-2">{{ $message }}</div>
@enderror
@forelse($preguntas as $i => $pregunta)
<div wire:key="pregunta-{{ $i }}"
class="card mb-3"
style="border-left: 4px solid #4e73df;">
{{-- Header de la pregunta --}}
<div class="card-header py-2 d-flex align-items-center justify-content-between bg-light">
<span class="font-weight-bold text-primary small">Pregunta {{ $i + 1 }}</span>
<div class="btn-group btn-group-sm">
<button type="button"
wire:click="movePregunta({{ $i }}, 'up')"
class="btn btn-outline-secondary"
title="Subir" {{ $i === 0 ? 'disabled' : '' }}>
<i class="fas fa-chevron-up"></i>
</button>
<button type="button"
wire:click="movePregunta({{ $i }}, 'down')"
class="btn btn-outline-secondary"
title="Bajar" {{ $i === count($preguntas) - 1 ? 'disabled' : '' }}>
<i class="fas fa-chevron-down"></i>
</button>
<button type="button"
class="btn btn-outline-danger"
title="Eliminar"
onclick="confirmarEliminarPregunta({{ $i }}, this)">
<i class="fas fa-times"></i>
</button>
</div>
</div>
<div class="card-body py-3">
@php
$tipoAlumnoId = (string)(collect($tipos)->firstWhere('slug', 'alumno')['id'] ?? 0);
$esEvalAlumno = $evaluacionTipoId === $tipoAlumnoId;
@endphp
<div class="row">
{{-- Texto de la pregunta --}}
<div class="col-md-7">
<div class="form-group mb-2">
<label class="small font-weight-bold">Pregunta <span class="text-danger">*</span></label>
<input type="text"
wire:model="preguntas.{{ $i }}.texto"
class="form-control form-control-sm @error("preguntas.{$i}.texto") is-invalid @enderror"
placeholder="Escribe la pregunta…">
@error("preguntas.{$i}.texto")
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
{{-- Tipo de respuesta --}}
<div class="col-md-{{ $esEvalAlumno ? '3' : '4' }}">
<div class="form-group mb-2">
<label class="small font-weight-bold">Tipo de respuesta</label>
<select wire:model.live="preguntas.{{ $i }}.pregunta_tipo_id"
class="form-control form-control-sm @error("preguntas.{$i}.pregunta_tipo_id") is-invalid @enderror">
@foreach($preguntaTipos as $tipo)
<option value="{{ $tipo['id'] }}">{{ $tipo['nombre'] }}</option>
@endforeach
</select>
@error("preguntas.{$i}.pregunta_tipo_id")
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
</div>
{{-- Obligatoria --}}
<div class="col-md-1 d-flex align-items-end pb-3">
<div class="form-check" title="Obligatoria">
<input type="checkbox"
wire:model="preguntas.{{ $i }}.requerida"
class="form-check-input"
id="req-{{ $i }}">
<label class="form-check-label small" for="req-{{ $i }}">*</label>
</div>
</div>
{{-- Pregunta de docente (solo en evaluaciones de tipo Alumno) --}}
@if($esEvalAlumno)
<div class="col-md-1 d-flex align-items-end pb-3">
<div class="form-check"
title="Marcar si esta pregunta evalúa a un docente específico">
<input type="checkbox"
wire:model="preguntas.{{ $i }}.es_docente"
class="form-check-input"
id="esd-{{ $i }}">
<label class="form-check-label small text-primary" for="esd-{{ $i }}">
<i class="fas fa-chalkboard-teacher"></i>
</label>
</div>
</div>
@endif
</div>
{{-- Indicador visual si es pregunta de docente --}}
@if($esEvalAlumno && !empty($pregunta['es_docente']))
<div class="alert alert-info py-1 px-2 mb-2 d-flex align-items-center" style="font-size:.82rem;">
<i class="fas fa-chalkboard-teacher mr-2"></i>
Esta pregunta se repetirá por cada docente que imparte clase al alumno.
</div>
@endif
{{-- Vista previa y opciones según tipo --}}
@php
$tipoSlug = collect($preguntaTipos)
->firstWhere('id', (int)($pregunta['pregunta_tipo_id'] ?? 0))['slug'] ?? '';
@endphp
{{-- Opciones múltiples --}}
@if($tipoSlug === 'opcion_multiple')
<div class="mt-2 p-3 bg-light rounded">
<label class="small font-weight-bold mb-2 d-block">
Opciones de respuesta
</label>
@foreach($pregunta['opciones'] as $j => $opcion)
<div wire:key="opcion-{{ $i }}-{{ $j }}"
class="input-group input-group-sm mb-2">
<div class="input-group-prepend">
<span class="input-group-text">{{ $j + 1 }}</span>
</div>
<input type="text"
wire:model="preguntas.{{ $i }}.opciones.{{ $j }}.texto"
class="form-control @error("preguntas.{$i}.opciones.{$j}.texto") is-invalid @enderror"
placeholder="Opción {{ $j + 1 }}">
<div class="input-group-append">
<button type="button"
wire:click="removeOpcion({{ $i }}, {{ $j }})"
class="btn btn-outline-danger">
<i class="fas fa-times"></i>
</button>
</div>
@error("preguntas.{$i}.opciones.{$j}.texto")
<div class="invalid-feedback d-block">{{ $message }}</div>
@enderror
</div>
@endforeach
<button type="button"
wire:click="addOpcion({{ $i }})"
class="btn btn-outline-primary btn-sm mt-1">
<i class="fas fa-plus mr-1"></i> Agregar opción
</button>
@if(empty($pregunta['opciones']))
<p class="text-muted small mt-2 mb-0">
<i class="fas fa-info-circle"></i>
Agrega al menos una opción de respuesta.
</p>
@endif
</div>
{{-- Vista previa: / No --}}
@elseif($tipoSlug === 'si_no')
<div class="mt-2 p-2 bg-light rounded d-flex align-items-center">
<span class="badge badge-success mr-2 px-3 py-2"></span>
<span class="badge badge-danger px-3 py-2">No</span>
<small class="text-muted ml-3">Vista previa</small>
</div>
{{-- Vista previa: Escala 1-5 --}}
@elseif($tipoSlug === 'escala_5')
<div class="mt-2 p-2 bg-light rounded">
@for($k = 1; $k <= 5; $k++)
<span class="badge badge-light border mr-1 px-3 py-2">{{ $k }}</span>
@endfor
<small class="text-muted ml-2">Escala del 1 al 5 Vista previa</small>
</div>
{{-- Vista previa: Escala 1-10 --}}
@elseif($tipoSlug === 'escala_10')
<div class="mt-2 p-2 bg-light rounded">
@for($k = 1; $k <= 10; $k++)
<span class="badge badge-light border mr-1 px-2 py-2">{{ $k }}</span>
@endfor
<small class="text-muted ml-2">Escala del 1 al 10 Vista previa</small>
</div>
{{-- Vista previa: Texto libre --}}
@elseif($tipoSlug === 'texto_libre')
<div class="mt-2 p-2 bg-light rounded">
<input type="text" class="form-control form-control-sm" disabled
placeholder="El evaluador escribirá su respuesta aquí…">
</div>
@endif
</div>
</div>
@empty
<div class="text-center py-5 text-muted">
<i class="fas fa-question-circle fa-3x mb-3 d-block text-primary" style="opacity:.4"></i>
<p class="mb-0">No hay preguntas todavía.</p>
<p class="small">Haz clic en <strong>"Agregar pregunta"</strong> para comenzar a construir la evaluación.</p>
</div>
@endforelse
@if(count($preguntas) > 0)
<div class="text-center mt-3">
<button type="button" wire:click="addPregunta"
class="btn btn-outline-primary btn-sm">
<i class="fas fa-plus mr-1"></i> Agregar otra pregunta
</button>
</div>
@endif
</div>
</div>
</div>
{{-- ===================== COLUMNA LATERAL ===================== --}}
<div class="col-lg-4">
{{-- Guardar --}}
<div class="card shadow mb-4">
<div class="card-body">
<button type="button" wire:click="guardar"
wire:loading.attr="disabled"
class="btn btn-success btn-block btn-lg">
<span wire:loading.remove>
<i class="fas fa-save mr-1"></i>
{{ $evaluacionId ? 'Guardar cambios' : 'Crear evaluación' }}
</span>
<span wire:loading>
<i class="fas fa-spinner fa-spin mr-1"></i> Guardando…
</span>
</button>
<a href="{{ route('evaluacion.index') }}"
class="btn btn-outline-secondary btn-block btn-sm mt-2">
Cancelar
</a>
</div>
</div>
{{-- Configuración --}}
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-cog mr-1"></i> Configuración
</h6>
</div>
<div class="card-body">
<div class="form-group">
<label class="small font-weight-bold">
Dirigida a <span class="text-danger">*</span>
</label>
<select wire:model="evaluacionTipoId"
class="form-control form-control-sm @error('evaluacionTipoId') is-invalid @enderror">
<option value=""> Selecciona </option>
@foreach($tipos as $tipo)
<option value="{{ $tipo['id'] }}">{{ $tipo['nombre'] }}</option>
@endforeach
</select>
@error('evaluacionTipoId')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group">
<label class="small font-weight-bold">Estado</label>
<select wire:model="status" class="form-control form-control-sm">
<option value="borrador">Borrador</option>
<option value="activa">Activa</option>
<option value="cerrada">Cerrada</option>
</select>
</div>
<div class="row">
<div class="col-6">
<div class="form-group">
<label class="small font-weight-bold">Fecha inicio</label>
<input type="date" wire:model="fechaInicio"
class="form-control form-control-sm">
</div>
</div>
<div class="col-6">
<div class="form-group">
<label class="small font-weight-bold">Fecha fin</label>
<input type="date" wire:model="fechaFin"
class="form-control form-control-sm">
</div>
</div>
</div>
<div class="form-check">
<input type="checkbox" wire:model="anonima"
class="form-check-input" id="anonima">
<label class="form-check-label small" for="anonima">
<i class="fas fa-user-secret mr-1"></i> Evaluación anónima
</label>
</div>
</div>
</div>
{{-- Planteles --}}
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-school mr-1"></i> Planteles
<small class="text-muted font-weight-normal">(opcional)</small>
</h6>
</div>
<div class="card-body py-2" style="max-height:180px; overflow-y:auto;">
@forelse($planteles as $plantel)
<div class="form-check py-1">
<input type="checkbox"
wire:model="selectedPlanteles"
value="{{ $plantel['id'] }}"
class="form-check-input"
id="plantel-{{ $plantel['id'] }}">
<label class="form-check-label small" for="plantel-{{ $plantel['id'] }}">
{{ $plantel['name'] }}
</label>
</div>
@empty
<p class="text-muted small mb-0">No hay planteles registrados.</p>
@endforelse
</div>
</div>
{{-- Niveles --}}
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">
<i class="fas fa-layer-group mr-1"></i> Niveles educativos
<small class="text-muted font-weight-normal">(opcional)</small>
</h6>
</div>
<div class="card-body py-2" style="max-height:180px; overflow-y:auto;">
@forelse($niveles as $nivel)
<div class="form-check py-1">
<input type="checkbox"
wire:model="selectedNiveles"
value="{{ $nivel['id'] }}"
class="form-check-input"
id="nivel-{{ $nivel['id'] }}">
<label class="form-check-label small" for="nivel-{{ $nivel['id'] }}">
{{ $nivel['name'] }}
</label>
</div>
@empty
<p class="text-muted small mb-0">No hay niveles registrados.</p>
@endforelse
</div>
</div>
</div>{{-- fin columna lateral --}}
</div>{{-- fin row --}}
</div>
@push('scripts')
<script>
function confirmarEliminarPregunta(index, el) {
Swal.fire({
title: '¿Eliminar pregunta?',
text: 'Esta acción no se puede deshacer.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Sí, eliminar',
cancelButtonText: 'Cancelar',
confirmButtonColor: '#e74a3b',
cancelButtonColor: '#6c757d',
}).then(result => {
if (result.isConfirmed) {
const wireEl = el.closest('[wire\\:id]');
Livewire.find(wireEl.getAttribute('wire:id')).call('removePregunta', index);
}
});
}
</script>
@endpush