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') +
+ @foreach($pregunta->opciones as $opcion) +
+ requerida ? 'required' : '' }}> + +
+ @endforeach +
+ +@elseif($tipoSlug === 'si_no') +
+
+ requerida ? 'required' : '' }}> + +
+
+ requerida ? 'required' : '' }}> + +
+
+ +@elseif($tipoSlug === 'escala_5') +
+ @for($k = 1; $k <= 5; $k++) +
+ requerida ? 'required' : '' }}> + +
+ @endfor + 1 = Muy malo   5 = Excelente +
+ +@elseif($tipoSlug === 'escala_10') +
+ @for($k = 1; $k <= 10; $k++) +
+ requerida ? 'required' : '' }}> + +
+ @endfor + 1 = Muy malo   10 = Excelente +
+@endif diff --git a/resources/views/evaluacion/index.blade.php b/resources/views/evaluacion/index.blade.php index 0a45c87f..1403e0fa 100644 --- a/resources/views/evaluacion/index.blade.php +++ b/resources/views/evaluacion/index.blade.php @@ -16,7 +16,7 @@
- Evaluaciones + Evaluaciones
Nueva evaluación @@ -25,8 +25,7 @@
-
- +
@@ -52,11 +51,11 @@ @empty - - - @endforelse
Título @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 @@ - + - +
+ method="POST" class="d-inline delete-form"> @csrf @method('DELETE')
- - No hay evaluaciones registradas. -
-
@endsection + + +@section('scripts') + @include('layouts._partials.tablaScript') + @include('layouts._partials.delete') +@endsection diff --git a/resources/views/evaluacion/mis_evaluaciones.blade.php b/resources/views/evaluacion/mis_evaluaciones.blade.php new file mode 100644 index 00000000..bb6b0b88 --- /dev/null +++ b/resources/views/evaluacion/mis_evaluaciones.blade.php @@ -0,0 +1,98 @@ +@extends('layouts.landing') + +@section('title', 'Mis Evaluaciones') + +@section('content') +
+ + @if(session('info')) +
+ {{ session('info') }} + +
+ @endif + +
+
+
+ Mis Evaluaciones +
+
+
+
+
+@endsection diff --git a/resources/views/evaluacion/responder.blade.php b/resources/views/evaluacion/responder.blade.php new file mode 100644 index 00000000..0847a851 --- /dev/null +++ b/resources/views/evaluacion/responder.blade.php @@ -0,0 +1,151 @@ +@extends('layouts.landing') + +@section('title', $evaluacion->titulo) + +@section('content') +
+ +
+
+
+ {{ $evaluacion->titulo }} +
+ @if($evaluacion->descripcion) + {{ $evaluacion->descripcion }} + @endif +
+
+ Regresar + +
+ + @if(session('error')) +
{{ session('error') }}
+ @endif + + @if($yaRespondida) + {{-- Ya respondida --}} +
+
+ +
Ya respondiste esta evaluación
+

Tu participación ha sido registrada. ¡Gracias!

+ + Volver a mis evaluaciones + +
+
+ + @else + {{-- Formulario --}} +
+ @csrf + + @php + $numPregunta = 1; + $totalPreguntas = $preguntas->count(); + // For docente questions: multiply by number of teachers + if($docentesParaEvaluar->isNotEmpty()) { + $esDocenteCount = $preguntas->where('es_docente', true)->count(); + $normalCount = $totalPreguntas - $esDocenteCount; + $totalPreguntas = $normalCount + ($esDocenteCount * $docentesParaEvaluar->count()); + } + @endphp + + {{-- Info de la evaluación --}} +
+ +
+ {{ $totalPreguntas }} pregunta(s) en esta evaluación. + @if($evaluacion->anonima) +   + Anónima + + @endif + @if($evaluacion->fecha_fin) +   Cierra el + {{ \Carbon\Carbon::parse($evaluacion->fecha_fin)->format('d/m/Y') }}. + @endif +
+
+ + @foreach($preguntas as $pregunta) + @php $tipoSlug = $pregunta->tipo?->slug; @endphp + + @if($pregunta->es_docente && $docentesParaEvaluar->isNotEmpty()) + {{-- ═══ PREGUNTA DE DOCENTE: una vez por cada docente ═══ --}} + @foreach($docentesParaEvaluar as $docente) + @php + $user = $docente->docentes->first(); + $fotoUrl = $user?->profile_photo_url; + $nombreDocente = $user?->name ?? 'Docente'; + @endphp + +
+
+ {{-- Foto del docente --}} + {{ $nombreDocente }} +
+
{{ $nombreDocente }}
+ + + Pregunta {{ $numPregunta++ }} + @if($pregunta->requerida) + * + @endif + +
+
+
+

{{ $pregunta->texto }}

+ + @include('evaluacion._input_pregunta', [ + 'pregunta' => $pregunta, + 'tipoSlug' => $tipoSlug, + 'fieldName' => "resp[{$pregunta->id}][{$docente->id}]", + 'fieldId' => "resp_{$pregunta->id}_{$docente->id}", + ]) +
+
+ @endforeach + + @else + {{-- ═══ PREGUNTA NORMAL ═══ --}} +
+
+ + Pregunta {{ $numPregunta++ }} + @if($pregunta->requerida) + * + @endif + +

{{ $pregunta->texto }}

+ + @include('evaluacion._input_pregunta', [ + 'pregunta' => $pregunta, + 'tipoSlug' => $tipoSlug, + 'fieldName' => "resp[{$pregunta->id}]", + 'fieldId' => "resp_{$pregunta->id}", + ]) +
+
+ @endif + @endforeach + +
+
+ +
+
+
+ @endif + +
+@endsection diff --git a/resources/views/evaluacion/show.blade.php b/resources/views/evaluacion/show.blade.php index a4c74483..fe2b13a4 100644 --- a/resources/views/evaluacion/show.blade.php +++ b/resources/views/evaluacion/show.blade.php @@ -150,12 +150,19 @@
- - Pregunta {{ $i + 1 }} - @if($pregunta->requerida) - * +
+ + Pregunta {{ $i + 1 }} + @if($pregunta->requerida) + * + @endif + + @if($pregunta->es_docente) + + Por docente + @endif - +

{{ $pregunta->texto }}

{{ $pregunta->tipo?->nombre }}
diff --git a/resources/views/layouts/_partials/menu.blade.php b/resources/views/layouts/_partials/menu.blade.php index 8d6be78e..c3a53568 100644 --- a/resources/views/layouts/_partials/menu.blade.php +++ b/resources/views/layouts/_partials/menu.blade.php @@ -202,6 +202,12 @@ + + @endrole @hasrole(['Docente']) @@ -211,6 +217,12 @@ Mis grupos + + @endrole diff --git a/resources/views/livewire/evaluacion-builder.blade.php b/resources/views/livewire/evaluacion-builder.blade.php index 05ee8c93..c669f181 100644 --- a/resources/views/livewire/evaluacion-builder.blade.php +++ b/resources/views/livewire/evaluacion-builder.blade.php @@ -94,6 +94,10 @@
+ @php + $tipoAlumnoId = (string)(collect($tipos)->firstWhere('slug', 'alumno')['id'] ?? 0); + $esEvalAlumno = $evaluacionTipoId === $tipoAlumnoId; + @endphp
{{-- Texto de la pregunta --}}
@@ -110,7 +114,7 @@
{{-- Tipo de respuesta --}} -
+
+ +
+
+ @endif
+ {{-- Indicador visual si es pregunta de docente --}} + @if($esEvalAlumno && !empty($pregunta['es_docente'])) +
+ + Esta pregunta se repetirá por cada docente que imparte clase al alumno. +
+ @endif + {{-- Vista previa y opciones según tipo --}} @php $tipoSlug = collect($preguntaTipos) diff --git a/resources/views/livewire/grupo-docente.blade.php b/resources/views/livewire/grupo-docente.blade.php index bb755f45..7ac32741 100644 --- a/resources/views/livewire/grupo-docente.blade.php +++ b/resources/views/livewire/grupo-docente.blade.php @@ -143,7 +143,7 @@ @empty - Sin materias en este plan. + Sin materias en este plan. @endforelse diff --git a/resources/views/tarea/acta.blade.php b/resources/views/tarea/acta.blade.php new file mode 100644 index 00000000..c85782d0 --- /dev/null +++ b/resources/views/tarea/acta.blade.php @@ -0,0 +1,213 @@ +@extends('layouts.landing') + +@section('title', 'Acta de Calificaciones') + +@section('head') + +@endsection + +@section('content') +
+
+
+
+
+
+ Acta de Calificaciones +
+ + {{ $material->name }} · Grupo: {{ $grupo->clave }} + +
+
+ + Regresar + + +
+
+
+ +
+ + {{-- Resumen --}} +
+
+
+
+
+ Total de Tareas +
+
{{ $tareas->count() }}
+
+
+
+
+
+
+
+ Total de Alumnos +
+
{{ $alumnos->count() }}
+
+
+
+
+
+
+
+ Materia +
+
{{ $material->name }}
+
+
+
+
+
+
+
+ Generado +
+
+ {{ \Carbon\Carbon::now()->format('d/m/Y') }} +
+
+
+
+
+ + @if ($tareas->isEmpty()) +
No hay tareas registradas para esta materia.
+ @elseif ($alumnos->isEmpty()) +
No hay alumnos inscritos en este grupo.
+ @else +
+ + + + + + @foreach ($tareas as $tarea) + + @endforeach + + + + + @php $fila = 1; @endphp + @foreach ($alumnos as $alumno) + @php + $user = $alumno->alumnos->first(); + $nombre = $user?->name ?? 'Sin nombre'; + $calificaciones = []; + $sumaCal = 0; + $countCal = 0; + + foreach ($tareas as $tarea) { + $entrega = $entregas[$alumno->id][$tarea->id] ?? null; + $cal = ($entrega && $entrega->status === 'calificada') + ? $entrega->calificacion + : null; + $calificaciones[$tarea->id] = [ + 'valor' => $cal, + 'status' => $entrega?->status ?? 'sin_subir', + ]; + if ($cal !== null) { + $sumaCal += $cal; + $countCal++; + } + } + + $promedio = $countCal > 0 ? round($sumaCal / $countCal, 1) : null; + @endphp + + + + @foreach ($tareas as $tarea) + @php $data = $calificaciones[$tarea->id]; @endphp + + @endforeach + + + @endforeach + +
#Alumno +
+ {{ Str::limit($tarea->titulo, 25) }} +
+
+ Promedio Final +
{{ $fila++ }}{{ $nombre }} + @if ($data['valor'] !== null) + + {{ number_format($data['valor'], 1) }} + + @elseif ($data['status'] === 'pendiente') + + + + @elseif ($data['status'] === 'sin_subir') + + @endif + + @if ($promedio !== null) + + {{ number_format($promedio, 1) }} + + @else + S/C + @endif +
+
+ +
+ + + El promedio final se calcula únicamente sobre las tareas ya calificadas. + = sin entregar  |  + = entregada, pendiente de calificación  |  + S/C = sin calificaciones registradas. + +
+ + {{-- Firma para impresión --}} +
+
+
+ Firma del Docente +
+
+
+
+ Firma del Director +
+
+
+
+ Sello de la Institución +
+
+
+ @endif + +
+
+
+@endsection diff --git a/resources/views/tarea/index.blade.php b/resources/views/tarea/index.blade.php index f862e2a3..445203e2 100644 --- a/resources/views/tarea/index.blade.php +++ b/resources/views/tarea/index.blade.php @@ -24,6 +24,10 @@ Regresar + + Generar Acta + Nueva Tarea diff --git a/resources/views/tarea/mis_tareas.blade.php b/resources/views/tarea/mis_tareas.blade.php index b128d937..329020de 100644 --- a/resources/views/tarea/mis_tareas.blade.php +++ b/resources/views/tarea/mis_tareas.blade.php @@ -1,147 +1,315 @@ @extends('layouts.landing') -@section('head') - @include('layouts._partials.tablaStyle') -@endsection - @section('title', 'Mis Tareas') @section('content')
-
-
-
Mis Tareas
+ + {{-- Alertas de sesión --}} + @if (session('info')) +
+ {{ session('info') }} + +
+ @endif + @if (session('error')) +
+ {{ session('error') }} + +
+ @endif + @if ($errors->has('entrega')) +
+ {{ $errors->first('entrega') }} + +
+ @endif + + {{-- Sin ciclo activo --}} + @if (!$cicloActual) +
+ + No hay un ciclo escolar activo en este momento. Contacta a tu coordinador. +
+ @else + + {{-- ══════════════════════════════════════════════════ + SECCIÓN 1: TAREAS POR ENTREGAR (ciclo actual) + ══════════════════════════════════════════════════ --}} +
+
+
+ Tareas por Entregar +
+ {{ $cicloActual->name }} +
+
+ @if ($tareasPendientes->isEmpty()) +
+ ¡Estás al corriente! No tienes tareas pendientes de entrega. +
+ @else + @php $pendientesAgrupadas = $tareasPendientes->groupBy('material_id'); @endphp + + @foreach ($pendientesAgrupadas as $materialId => $grupoTareas) + @php + $material = $grupoTareas->first()->material; + $grupo = $grupoTareas->first()->grupo; + $vencidas = $grupoTareas->filter(fn($t) => + $t->fecha_entrega && \Carbon\Carbon::parse($t->fecha_entrega)->isPast() + )->count(); + @endphp + +
+
+
+ +
+
{{ $material->name }}
+ Grupo: {{ $grupo->clave }} +
+
+
+ @if ($vencidas > 0) + + {{ $vencidas }} vencida(s) + + @endif + + {{ $grupoTareas->count() }} por entregar + + +
+
+ +
+
+
+ + + + + + + + + + @foreach ($grupoTareas as $tarea) + + + + + + @endforeach + +
TareaFecha límiteEntregar
+ {{ $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 +
+
+ @csrf +
+ +
+
+
+ + +
+
+ +
+
+
+
+
+
+ @endforeach + @endif +
-
+ {{-- ══════════════════════════════════════════════════ + SECCIÓN 2: TAREAS YA ENTREGADAS + ══════════════════════════════════════════════════ --}} +
+
+
+ Tareas Entregadas +
+ {{ $tareasEntregadas->count() }} +
+
+ @if ($tareasEntregadas->isEmpty()) +
+ Aún no has entregado ninguna tarea en este ciclo. +
+ @else + @php $entregadasAgrupadas = $tareasEntregadas->groupBy('material_id'); @endphp - @if (session('info')) -
{{ session('info') }}
- @endif - @if (session('error')) -
{{ session('error') }}
- @endif - @if ($errors->has('entrega')) -
{{ $errors->first('entrega') }}
- @endif + @foreach ($entregadasAgrupadas as $materialId => $grupoTareas) + @php + $material = $grupoTareas->first()->material; + $grupo = $grupoTareas->first()->grupo; + $calificadas = $grupoTareas->filter( + fn($t) => ($entregasMap[$t->id]->status ?? '') === 'calificada' + )->count(); + $pendientes = $grupoTareas->count() - $calificadas; + @endphp - @if (!isset($tareas) || $tareas->isEmpty()) -
No tienes tareas asignadas en tus grupos.
- @else -
- - - - - - - - - - - - - - @foreach ($tareas as $tarea) - @php $entrega = $entregasMap[$tarea->id] ?? null; $status = $entrega?->status ?? 'sin_subir'; @endphp - - - - - - - - - - @endforeach - -
TareaMateriaGrupoFecha límiteStatusCalificaciónTu entrega / Acción
- {{ $tarea->titulo }} - @if ($tarea->descripcion) -

{{ Str::limit($tarea->descripcion, 80) }}

+
+
{{ $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 --}} -
- @csrf -
- -
-
-
- - -
-
- -
- - @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 -
-
- @endif + + {{ $grupoTareas->count() }} total + + +
+
+
+
+
+ + + + + + + + + + + @foreach ($grupoTareas as $tarea) + @php + $entrega = $entregasMap[$tarea->id] ?? null; + $status = $entrega?->status ?? 'sin_subir'; + @endphp + + + + + + + @endforeach + +
TareaStatusCalificaciónLo 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 +
+
+
+
+
+ @endforeach + @endif +
-
+ + @endif {{-- fin @if $cicloActual --}} +
@endsection @section('scripts') - @include('layouts._partials.tablaScript') @endsection diff --git a/routes/web.php b/routes/web.php index 94483226..eb34bec6 100644 --- a/routes/web.php +++ b/routes/web.php @@ -18,6 +18,7 @@ use App\Http\Controllers\ConceptoController; use App\Http\Controllers\DocenteController; use App\Http\Controllers\EntregaController; use App\Http\Controllers\EvaluacionController; +use App\Http\Controllers\EvaluacionResponderController; use App\Http\Controllers\TareaController; use App\Http\Controllers\DocumentoController; use App\Http\Controllers\DocumentoTipoController; @@ -88,10 +89,15 @@ Route::middleware(['auth:sanctum',config('jetstream.auth_session'),'verified', Route::get('/search', UserSearchController::class)->name('users.search'); - // Evaluaciones + // Evaluaciones (administración) Route::resource('/evaluacion', EvaluacionController::class); Route::patch('/evaluacion/{evaluacion}/status', [EvaluacionController::class, 'toggleStatus'])->name('evaluacion.toggleStatus'); + // Evaluaciones (responder — docentes y alumnos) + Route::get('/mis-evaluaciones', [EvaluacionResponderController::class, 'misEvaluaciones'])->name('evaluacion.mis_evaluaciones'); + Route::get('/evaluacion/{evaluacion}/responder', [EvaluacionResponderController::class, 'show'])->name('evaluacion.responder.show'); + Route::post('/evaluacion/{evaluacion}/responder', [EvaluacionResponderController::class, 'store'])->name('evaluacion.responder.store'); + // Anuncios (gestión: solo Admin) Route::resource('/anuncio', AnuncioController::class); Route::patch('/anuncio/{anuncio}/toggle', [AnuncioController::class, 'toggleActive'])->name('anuncio.toggleActive'); @@ -111,6 +117,7 @@ Route::middleware(['auth:sanctum',config('jetstream.auth_session'),'verified', Route::get('/grupo/{grupo}/material/{material}/tareas', [TareaController::class, 'index'])->name('grupo.material.tareas'); Route::get('/grupo/{grupo}/material/{material}/tareas/create', [TareaController::class, 'create'])->name('grupo.material.tareas.create'); Route::post('/grupo/{grupo}/material/{material}/tareas', [TareaController::class, 'store'])->name('grupo.material.tareas.store'); + Route::get('/grupo/{grupo}/material/{material}/tareas/acta', [TareaController::class, 'acta'])->name('grupo.material.tareas.acta'); Route::get('/grupo/{grupo}/material/{material}/tareas/{tarea}', [TareaController::class, 'show'])->name('grupo.material.tareas.show'); Route::get('/grupo/{grupo}/material/{material}/tareas/{tarea}/edit', [TareaController::class, 'edit'])->name('grupo.material.tareas.edit'); Route::put('/grupo/{grupo}/material/{material}/tareas/{tarea}', [TareaController::class, 'update'])->name('grupo.material.tareas.update');