diff --git a/app/Http/Controllers/EntregaController.php b/app/Http/Controllers/EntregaController.php index 1e591d4e..167e3b26 100644 --- a/app/Http/Controllers/EntregaController.php +++ b/app/Http/Controllers/EntregaController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers; use App\Models\Alumno; +use App\Models\Campan; use App\Models\Entrega; use App\Models\Tarea; use Illuminate\Http\Request; @@ -16,22 +17,43 @@ class EntregaController extends Controller $alumno = Alumno::whereHas('alumnos', fn($q) => $q->where('id', Auth::id()))->first(); if (!$alumno) { - return view('tarea.mis_tareas', ['tareas' => collect(), 'entregasMap' => collect()]); + return view('tarea.mis_tareas', [ + 'tareasPendientes' => collect(), + 'tareasEntregadas' => collect(), + 'entregasMap' => collect(), + 'cicloActual' => null, + ]); } - $gruposIds = $alumno->grupos()->pluck('grupos.id'); + $cicloActual = Campan::where('status', 1)->first(); - $tareas = Tarea::whereIn('grupo_id', $gruposIds) + // Solo grupos del ciclo activo + $gruposActuales = $alumno->grupos() + ->when($cicloActual, fn($q) => $q->where('ciclo', $cicloActual->id)) + ->pluck('grupos.id'); + + $todasTareas = Tarea::whereIn('grupo_id', $gruposActuales) ->with(['material', 'grupo']) ->orderByDesc('created_at') ->get(); $entregasMap = Entrega::where('alumno_id', $alumno->id) - ->whereIn('tarea_id', $tareas->pluck('id')) + ->whereIn('tarea_id', $todasTareas->pluck('id')) ->get() ->keyBy('tarea_id'); - return view('tarea.mis_tareas', compact('tareas', 'entregasMap', 'alumno')); + // Separar: sin_subir = pendientes de entregar | pendiente/calificada = ya entregadas + $tareasPendientes = $todasTareas->filter( + fn($t) => ($entregasMap[$t->id]->status ?? 'sin_subir') === 'sin_subir' + ); + + $tareasEntregadas = $todasTareas->filter( + fn($t) => in_array($entregasMap[$t->id]->status ?? 'sin_subir', ['pendiente', 'calificada']) + ); + + return view('tarea.mis_tareas', compact( + 'tareasPendientes', 'tareasEntregadas', 'entregasMap', 'alumno', 'cicloActual' + )); } public function store(Request $request, Tarea $tarea) diff --git a/app/Http/Controllers/EvaluacionController.php b/app/Http/Controllers/EvaluacionController.php index 44817faf..ef6e5256 100644 --- a/app/Http/Controllers/EvaluacionController.php +++ b/app/Http/Controllers/EvaluacionController.php @@ -51,7 +51,13 @@ class EvaluacionController extends Controller { $evaluacion->delete(); - return redirect()->route('evaluacion.index')->with('info', 'Evaluación eliminada.'); + session()->flash('swal',[ + 'icon'=>'success', + 'title'=>'Eliminado!', + 'text'=>'Se ha eliminado el registro correctamente' + ]); + + return redirect()->route('evaluacion.index'); } public function toggleStatus(Request $request, Evaluacion $evaluacion) diff --git a/app/Http/Controllers/EvaluacionResponderController.php b/app/Http/Controllers/EvaluacionResponderController.php index 06e09330..62af8625 100644 --- a/app/Http/Controllers/EvaluacionResponderController.php +++ b/app/Http/Controllers/EvaluacionResponderController.php @@ -2,9 +2,162 @@ namespace App\Http\Controllers; +use App\Models\Alumno; +use App\Models\Docente; +use App\Models\Evaluacion; +use App\Models\EvaluacionSubmission; +use App\Models\Respuesta; use Illuminate\Http\Request; +use Illuminate\Routing\Controller; +use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\DB; class EvaluacionResponderController extends Controller { - // + public function misEvaluaciones() + { + $user = Auth::user(); + + $docente = Docente::whereHas('docentes', fn($q) => $q->where('id', $user->id))->first(); + $alumno = Alumno::whereHas('alumnos', fn($q) => $q->where('id', $user->id))->first(); + + $evaluaciones = collect(); + $respondidas = []; + + if ($docente) { + $grupos = $docente->grupos()->get(); + $plantelIds = $grupos->pluck('plantel')->unique()->filter()->values(); + $nivelIds = $grupos->pluck('nivel')->unique()->filter()->values(); + + $evaluaciones = Evaluacion::whereHas('tipo', fn($q) => $q->where('slug', 'docente')) + ->where('status', 'activa') + ->where(fn($q) => $q + ->whereHas('planteles', fn($p) => $p->whereIn('plantels.id', $plantelIds)) + ->orWhereDoesntHave('planteles') + ) + ->where(fn($q) => $q + ->whereHas('niveles', fn($n) => $n->whereIn('nivels.id', $nivelIds)) + ->orWhereDoesntHave('niveles') + ) + ->where(fn($q) => $q->whereNull('fecha_inicio')->orWhereDate('fecha_inicio', '<=', now())) + ->where(fn($q) => $q->whereNull('fecha_fin')->orWhereDate('fecha_fin', '>=', now())) + ->withCount('preguntas') + ->get(); + + } elseif ($alumno) { + $gruposIds = $alumno->grupos()->pluck('grupos.id'); + $plantelIds = \App\Models\Grupo::whereIn('id', $gruposIds)->pluck('plantel')->unique()->filter()->values(); + $nivelIds = \App\Models\Grupo::whereIn('id', $gruposIds)->pluck('nivel')->unique()->filter()->values(); + + $evaluaciones = Evaluacion::whereHas('tipo', fn($q) => $q->where('slug', 'alumno')) + ->where('status', 'activa') + ->where(fn($q) => $q + ->whereHas('planteles', fn($p) => $p->whereIn('plantels.id', $plantelIds)) + ->orWhereDoesntHave('planteles') + ) + ->where(fn($q) => $q + ->whereHas('niveles', fn($n) => $n->whereIn('nivels.id', $nivelIds)) + ->orWhereDoesntHave('niveles') + ) + ->where(fn($q) => $q->whereNull('fecha_inicio')->orWhereDate('fecha_inicio', '<=', now())) + ->where(fn($q) => $q->whereNull('fecha_fin')->orWhereDate('fecha_fin', '>=', now())) + ->withCount('preguntas') + ->get(); + } + + if ($evaluaciones->isNotEmpty()) { + $respondidas = EvaluacionSubmission::where('user_id', $user->id) + ->whereIn('evaluacion_id', $evaluaciones->pluck('id')) + ->pluck('evaluacion_id') + ->toArray(); + } + + return view('evaluacion.mis_evaluaciones', compact('evaluaciones', 'respondidas', 'docente', 'alumno')); + } + + public function show(Evaluacion $evaluacion) + { + abort_if($evaluacion->status !== 'activa', 403, 'Esta evaluación no está activa.'); + + if ($evaluacion->fecha_inicio && \Carbon\Carbon::parse($evaluacion->fecha_inicio)->isFuture()) { + abort(403, 'Esta evaluación aún no ha comenzado.'); + } + if ($evaluacion->fecha_fin && \Carbon\Carbon::parse($evaluacion->fecha_fin)->startOfDay()->isPast()) { + abort(403, 'Esta evaluación ya cerró.'); + } + + $yaRespondida = EvaluacionSubmission::where('evaluacion_id', $evaluacion->id) + ->where('user_id', Auth::id()) + ->exists(); + + $preguntas = $evaluacion->preguntas()->with(['tipo', 'opciones'])->get(); + $tipoSlug = $evaluacion->tipo->slug; + + // Para tipo 'alumno': obtener docentes del alumno para preguntas es_docente + $docentesParaEvaluar = collect(); + if ($tipoSlug === 'alumno' && $preguntas->where('es_docente', true)->count() > 0) { + $alumno = Alumno::whereHas('alumnos', fn($q) => $q->where('id', Auth::id()))->first(); + if ($alumno) { + $gruposIds = $alumno->grupos()->pluck('grupos.id'); + $docentesParaEvaluar = Docente::whereHas('grupos', fn($q) => $q->whereIn('grupos.id', $gruposIds)) + ->with('docentes') + ->get() + ->unique('id') + ->values(); + } + } + + return view('evaluacion.responder', compact( + 'evaluacion', 'preguntas', 'docentesParaEvaluar', 'tipoSlug', 'yaRespondida' + )); + } + + public function store(Request $request, Evaluacion $evaluacion) + { + abort_if($evaluacion->status !== 'activa', 403); + + if (EvaluacionSubmission::where('evaluacion_id', $evaluacion->id) + ->where('user_id', Auth::id())->exists()) { + return back()->with('error', 'Ya respondiste esta evaluación.'); + } + + DB::transaction(function () use ($request, $evaluacion) { + $submission = EvaluacionSubmission::create([ + 'evaluacion_id' => $evaluacion->id, + 'user_id' => Auth::id(), + ]); + + foreach ($evaluacion->preguntas()->with('tipo')->get() as $pregunta) { + $tipoSlug = $pregunta->tipo->slug; + $esOpcionMultiple = $tipoSlug === 'opcion_multiple'; + + if ($pregunta->es_docente) { + $valores = $request->input("resp.{$pregunta->id}", []); + foreach ($valores as $docenteId => $valor) { + if (!filled($valor)) continue; + Respuesta::create([ + 'submission_id' => $submission->id, + 'pregunta_id' => $pregunta->id, + 'docente_id' => $docenteId, + 'valor' => $esOpcionMultiple ? null : $valor, + 'opcion_id' => $esOpcionMultiple ? $valor : null, + ]); + } + } else { + $valor = $request->input("resp.{$pregunta->id}"); + if (!filled($valor)) continue; + Respuesta::create([ + 'submission_id' => $submission->id, + 'pregunta_id' => $pregunta->id, + 'docente_id' => null, + 'valor' => $esOpcionMultiple ? null : $valor, + 'opcion_id' => $esOpcionMultiple ? $valor : null, + ]); + } + } + }); + + return redirect()->route('evaluacion.mis_evaluaciones') + ->with('info', '¡Evaluación enviada correctamente! Gracias por tu participación.'); + } } diff --git a/app/Http/Controllers/GrupoController.php b/app/Http/Controllers/GrupoController.php index 861264e7..a190359f 100644 --- a/app/Http/Controllers/GrupoController.php +++ b/app/Http/Controllers/GrupoController.php @@ -188,12 +188,8 @@ class GrupoController extends Controller public function update(Request $request,Grupo $grupo) { - $nivel = Plan::where('id',$request->plan_id)->first(); - $grupo = Grupo::update([ - 'plan_id' => $request->plan_id, - 'ciclo' => 1, - 'nivel' => $nivel->nivel, - 'plantel' => $request->plantel, + $grupo->update([ + 'ciclo' => $request->ciclo, 'turno' => $request->turno, 'status'=>1 ]); diff --git a/app/Http/Controllers/TareaController.php b/app/Http/Controllers/TareaController.php index ba27c322..fdf5242c 100644 --- a/app/Http/Controllers/TareaController.php +++ b/app/Http/Controllers/TareaController.php @@ -127,6 +127,24 @@ class TareaController extends Controller ->with('info', 'Tarea eliminada.'); } + public function acta(Grupo $grupo, Material $material) + { + $tareas = Tarea::where('grupo_id', $grupo->id) + ->where('material_id', $material->id) + ->orderBy('created_at') + ->get(); + + $alumnos = $grupo->alumnos()->with('alumnos')->get(); + + // [alumno_id][tarea_id] => Entrega + $entregas = Entrega::whereIn('tarea_id', $tareas->pluck('id')) + ->get() + ->groupBy('alumno_id') + ->map(fn($rows) => $rows->keyBy('tarea_id')); + + return view('tarea.acta', compact('grupo', 'material', 'tareas', 'alumnos', 'entregas')); + } + public function calificar(Request $request, Tarea $tarea, Alumno $alumno) { $request->validate([ diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index a38be054..6430e882 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -2,11 +2,14 @@ namespace App\Http\Controllers; +use App\Models\Alumno; use App\Models\AreaCode; +use App\Models\Docente; use App\Models\Plantel; use App\Models\User; use Illuminate\Http\Request; use Illuminate\Routing\Controller; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Password; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; @@ -58,6 +61,8 @@ class UserController extends Controller $user->plantelUsuarios()->sync($request->plantels); + $this->syncAlumnoDocente($user, $request->roles ?? []); + $token = Password::createToken($user); $user->notify(new ResetPassword($token)); @@ -82,12 +87,14 @@ class UserController extends Controller $user->update($request->except(['plantels'])); $user->roles()->sync($request->roles); $user->plantelUsuarios()->sync($request->plantels); + $this->syncAlumnoDocente($user, $request->roles ?? []); return redirect()->route('user.edit',compact('user'))->with('info','Se modifico el usuario correctamente'); } public function destroy(User $user) { + $this->detachAlumnoDocente($user); $user->plantelUsuarios()->detach(); $user->roles()->detach(); $user->delete(); @@ -102,4 +109,65 @@ class UserController extends Controller public function profile(){ return view('profile.show'); } + + private function syncAlumnoDocente(User $user, array $roleIds): void + { + $roleNames = Role::whereIn('id', $roleIds)->pluck('name')->toArray(); + + // ── Alumno ────────────────────────────────────────────────── + if (in_array('Alumno', $roleNames)) { + $alumno = Alumno::whereHas('alumnos', fn($q) => $q->where('id', $user->id))->first(); + + if (!$alumno) { + $alumno = Alumno::create([ + 'nivel' => 0, + 'plan_id' => 0, + 'ciclo' => 0, + 'turno' => 0, + 'medio' => 0, + 'usuario_registro' => Auth::id(), + ]); + } + + $alumno->alumnos()->syncWithoutDetaching([$user->id]); + } else { + $this->detachFromAlumno($user); + } + + // ── Docente ───────────────────────────────────────────────── + if (in_array('Docente', $roleNames)) { + $docente = Docente::whereHas('docentes', fn($q) => $q->where('id', $user->id))->first(); + + if (!$docente) { + $docente = Docente::create([ + 'usuario_registro' => Auth::id(), + 'status' => 1, + ]); + } + + $docente->docentes()->syncWithoutDetaching([$user->id]); + } else { + $this->detachFromDocente($user); + } + } + + private function detachAlumnoDocente(User $user): void + { + $this->detachFromAlumno($user); + $this->detachFromDocente($user); + } + + private function detachFromAlumno(User $user): void + { + Alumno::whereHas('alumnos', fn($q) => $q->where('id', $user->id)) + ->get() + ->each(fn($a) => $a->alumnos()->detach($user->id)); + } + + private function detachFromDocente(User $user): void + { + Docente::whereHas('docentes', fn($q) => $q->where('id', $user->id)) + ->get() + ->each(fn($d) => $d->docentes()->detach($user->id)); + } } diff --git a/app/Livewire/EvaluacionBuilder.php b/app/Livewire/EvaluacionBuilder.php index 96232d56..ab1917ce 100644 --- a/app/Livewire/EvaluacionBuilder.php +++ b/app/Livewire/EvaluacionBuilder.php @@ -58,6 +58,7 @@ class EvaluacionBuilder extends Component '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, @@ -74,6 +75,7 @@ class EvaluacionBuilder extends Component 'texto' => '', 'pregunta_tipo_id' => (string) $defaultTipo, 'requerida' => true, + 'es_docente' => false, 'opciones' => [], ]; } @@ -164,6 +166,7 @@ class EvaluacionBuilder extends Component 'texto' => $pData['texto'], 'pregunta_tipo_id' => $pData['pregunta_tipo_id'], 'requerida' => $pData['requerida'] ?? true, + 'es_docente' => $pData['es_docente'] ?? false, 'orden' => $orden, ]); } else { @@ -171,6 +174,7 @@ class EvaluacionBuilder extends Component 'texto' => $pData['texto'], 'pregunta_tipo_id' => $pData['pregunta_tipo_id'], 'requerida' => $pData['requerida'] ?? true, + 'es_docente' => $pData['es_docente'] ?? false, 'orden' => $orden, ]); } diff --git a/database/migrations/2026_05_05_000001_add_es_docente_to_preguntas_table.php b/database/migrations/2026_05_05_000001_add_es_docente_to_preguntas_table.php new file mode 100644 index 00000000..5a479466 --- /dev/null +++ b/database/migrations/2026_05_05_000001_add_es_docente_to_preguntas_table.php @@ -0,0 +1,22 @@ +boolean('es_docente')->default(false)->after('requerida'); + }); + } + + public function down(): void + { + Schema::table('preguntas', function (Blueprint $table) { + $table->dropColumn('es_docente'); + }); + } +}; diff --git a/database/migrations/2026_05_05_000002_add_docente_id_to_respuestas_table.php b/database/migrations/2026_05_05_000002_add_docente_id_to_respuestas_table.php new file mode 100644 index 00000000..0f7e149c --- /dev/null +++ b/database/migrations/2026_05_05_000002_add_docente_id_to_respuestas_table.php @@ -0,0 +1,24 @@ +unsignedBigInteger('docente_id')->nullable()->after('opcion_id'); + $table->foreign('docente_id')->references('id')->on('docentes')->nullOnDelete(); + }); + } + + public function down(): void + { + Schema::table('respuestas', function (Blueprint $table) { + $table->dropForeign(['docente_id']); + $table->dropColumn('docente_id'); + }); + } +}; diff --git a/resources/views/evaluacion/_input_pregunta.blade.php b/resources/views/evaluacion/_input_pregunta.blade.php new file mode 100644 index 00000000..4b6c88f4 --- /dev/null +++ b/resources/views/evaluacion/_input_pregunta.blade.php @@ -0,0 +1,93 @@ +{{-- + Variables esperadas: + $pregunta - Pregunta model + $tipoSlug - string (slug del tipo) + $fieldName - string (name del input, ej: "resp[1]" o "resp[1][5]") + $fieldId - string (id único del input) +--}} + +@if($tipoSlug === 'texto_libre') + + +@elseif($tipoSlug === 'opcion_multiple') +
| Título | @@ -52,11 +51,11 @@@php $slug = $evaluacion->tipo?->slug; @endphp @if($slug === 'docente') - Docentes + Docentes @elseif($slug === 'alumno') - Alumnos + Alumnos @elseif($slug === 'administrativo') - Administrativos + Administrativos @else — @endif @@ -86,34 +85,32 @@ | - + - + | ||||
|---|---|---|---|---|---|---|
| - - No hay evaluaciones registradas. - | -||||||
+ {{ Str::limit($evaluacion->descripcion, 120) }} +
+ @endif + +Tu participación ha sido registrada. ¡Gracias!
+ + Volver a mis evaluaciones + +{{ $pregunta->texto }}
{{ $pregunta->tipo?->nombre }}| # | +Alumno | + @foreach ($tareas as $tarea) +
+
+ {{ Str::limit($tarea->titulo, 25) }}
+
+ |
+ @endforeach
+ + Promedio Final + | +
|---|---|---|---|
| {{ $fila++ }} | +{{ $nombre }} | + @foreach ($tareas as $tarea) + @php $data = $calificaciones[$tarea->id]; @endphp ++ @if ($data['valor'] !== null) + + {{ number_format($data['valor'], 1) }} + + @elseif ($data['status'] === 'pendiente') + + + + @elseif ($data['status'] === 'sin_subir') + — + @endif + | + @endforeach ++ @if ($promedio !== null) + + {{ number_format($promedio, 1) }} + + @else + S/C + @endif + | +
| Tarea | +Fecha límite | +Entregar | +
|---|---|---|
|
+ {{ $tarea->titulo }}
+ @if ($tarea->descripcion)
+ + {{ Str::limit($tarea->descripcion, 100) }} + + @endif + |
+
+ @if ($tarea->fecha_entrega)
+ {{ \Carbon\Carbon::parse($tarea->fecha_entrega)->format('d/m/Y') }}
+ @if (\Carbon\Carbon::parse($tarea->fecha_entrega)->isPast())
+ Vencida + @else + Activa + @endif + @else + Sin fecha + @endif + |
+ + + | +
| Tarea | -Materia | -Grupo | -Fecha límite | -Status | -Calificación | -Tu entrega / Acción | -
|---|---|---|---|---|---|---|
|
- {{ $tarea->titulo }}
- @if ($tarea->descripcion)
- {{ Str::limit($tarea->descripcion, 80) }} +
+
+
+
+
+
+
+ {{ $material->name }}+ Grupo: {{ $grupo->clave }} +
+ @if ($pendientes > 0)
+
+ {{ $pendientes }} por calificar
+
@endif
- |
- {{ $tarea->material->name }} | -{{ $tarea->grupo->clave }} | -- @if ($tarea->fecha_entrega) - {{ \Carbon\Carbon::parse($tarea->fecha_entrega)->format('d/m/Y') }} - @if (\Carbon\Carbon::parse($tarea->fecha_entrega)->isPast()) - Vencida - @else - Activa - @endif - @else - Sin fecha + @if ($calificadas > 0) + + {{ $calificadas }} calificada(s) + @endif - | -- @if ($status === 'sin_subir') - Sin subir - @elseif ($status === 'pendiente') - Pendiente - @else - Calificada - @endif - | -
- @if ($status === 'calificada' && $entrega)
- {{ number_format($entrega->calificacion, 1) }}
- @if ($entrega->comentario)
- {{ $entrega->comentario }} - @endif - @else - — - @endif - |
-
- @if ($status === 'sin_subir')
- {{-- Formulario de entrega: texto y/o archivo --}}
-
-
- @elseif ($status === 'pendiente' || $status === 'calificada')
- {{-- Mostrar lo que entregó --}}
- @if ($entrega?->texto)
-
- {{ $entrega->texto }}
-
- @endif
- @if ($entrega?->archivo)
-
- Ver archivo entregado
-
- @endif
- @if (!$entrega?->texto && !$entrega?->archivo)
- Sin contenido registrado
- @endif
- @if ($status === 'pendiente')
- Esperando calificación - @endif - @endif - |
-
| Tarea | +Status | +Calificación | +Lo que entregaste | +
|---|---|---|---|
|
+ {{ $tarea->titulo }}
+ @if ($tarea->descripcion)
+ + {{ Str::limit($tarea->descripcion, 80) }} + + @endif + |
+ + @if ($status === 'pendiente') + + Pendiente + + @else + + Calificada + + @endif + | +
+ @if ($status === 'calificada' && $entrega)
+
+ {{ number_format($entrega->calificacion, 1) }}
+
+ @if ($entrega->comentario)
+ {{ $entrega->comentario }} + @endif + @else + Esperando... + @endif + |
+
+ @if ($entrega?->texto)
+
+ {{ $entrega->texto }}
+
+ @endif
+ @if ($entrega?->archivo)
+
+ Ver archivo
+
+ @endif
+ @if (!$entrega?->texto && !$entrega?->archivo)
+ Sin contenido registrado
+ @endif
+ |
+