From 964ed4bbd285c550e4dd0b03ea2cbe76d51463d5 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 6 May 2026 18:33:18 -0600 Subject: [PATCH] =?UTF-8?q?Modificaci=C3=B3n=20del=20de=20vista=20de=20dop?= =?UTF-8?q?cumentos=20del=20alumno?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/settings.local.json | 4 +- app/Http/Controllers/AlumnoController.php | 6 +- app/Http/Controllers/EntregaController.php | 43 +- .../EvaluacionReporteController.php | 222 ++++++++ .../EvaluacionResponderController.php | 35 +- app/Http/Controllers/GrupoController.php | 2 +- app/Http/Controllers/ProspectoController.php | 10 + app/Livewire/GrupoDocente.php | 19 + app/Models/Anuncio.php | 2 +- composer.json | 1 + composer.lock | 524 +++++++++++++++++- database/seeders/RoleSeeder.php | 27 +- database/seeders/UserSeeder.php | 2 +- resources/views/alumno/edit.blade.php | 131 +++++ resources/views/alumno/index.blade.php | 56 +- .../evaluacion/_input_pregunta.blade.php | 18 +- resources/views/evaluacion/reporte.blade.php | 242 ++++++++ .../views/evaluacion/reporte_pdf.blade.php | 355 ++++++++++++ .../views/evaluacion/responder.blade.php | 27 +- resources/views/evaluacion/show.blade.php | 7 +- .../views/layouts/_partials/menu.blade.php | 408 ++++++-------- .../views/layouts/_partials/scripts.blade.php | 19 + resources/views/livewire/documentos.blade.php | 39 +- .../livewire/evaluacion-builder.blade.php | 25 +- resources/views/profile/show.blade.php | 21 - resources/views/tarea/mis_tareas.blade.php | 12 +- routes/web.php | 3 + 27 files changed, 1921 insertions(+), 339 deletions(-) create mode 100644 app/Http/Controllers/EvaluacionReporteController.php create mode 100644 app/Http/Controllers/ProspectoController.php create mode 100644 resources/views/evaluacion/reporte.blade.php create mode 100644 resources/views/evaluacion/reporte_pdf.blade.php diff --git a/.claude/settings.local.json b/.claude/settings.local.json index cfa11400..181dfe6d 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -18,7 +18,9 @@ "Bash(php -l app/Http/Controllers/GrupoController.php)", "Bash(php -l app/Models/Tarea.php)", "Bash(php -l app/Models/Entrega.php)", - "Bash(php -l app/Livewire/GrupoDocente.php)" + "Bash(php -l app/Livewire/GrupoDocente.php)", + "Bash(composer require *)", + "Bash(Select-String -Pattern \"entrega|Entrega\" -Context 0,0)" ] } } diff --git a/app/Http/Controllers/AlumnoController.php b/app/Http/Controllers/AlumnoController.php index a3817dd1..10fc230a 100644 --- a/app/Http/Controllers/AlumnoController.php +++ b/app/Http/Controllers/AlumnoController.php @@ -51,9 +51,7 @@ class AlumnoController extends Controller }) ->get(); - } - - if ($user->hasRole('Control escolar')) { + }else{ $plantelesUsuario = $user->plantelUsuarios->pluck('id'); $alumnos = Alumno::with(['alumnos.plantelUsuarios','statusAlumno']) @@ -65,6 +63,8 @@ class AlumnoController extends Controller } + + return view('alumno.index', compact('alumnos')); } diff --git a/app/Http/Controllers/EntregaController.php b/app/Http/Controllers/EntregaController.php index 167e3b26..e33c1116 100644 --- a/app/Http/Controllers/EntregaController.php +++ b/app/Http/Controllers/EntregaController.php @@ -9,6 +9,7 @@ use App\Models\Tarea; use Illuminate\Http\Request; use Illuminate\Routing\Controller; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\DB; class EntregaController extends Controller { @@ -25,12 +26,23 @@ class EntregaController extends Controller ]); } - $cicloActual = Campan::where('status', 1)->first(); + // Ciclo más alto del alumno = semestre en curso + $maxCiclo = DB::table('alumno_grupo') + ->join('grupos', 'grupos.id', '=', 'alumno_grupo.grupo_id') + ->where('alumno_grupo.alumno_id', $alumno->id) + ->max('grupos.ciclo'); - // Solo grupos del ciclo activo - $gruposActuales = $alumno->grupos() - ->when($cicloActual, fn($q) => $q->where('ciclo', $cicloActual->id)) - ->pluck('grupos.id'); + // Campan para mostrar el nombre en la vista (busca por id = ciclo, o cae al activo) + $cicloActual = $maxCiclo ? Campan::find($maxCiclo) ?? Campan::where('status', 1)->first() : null; + + // Grupos del alumno en ese ciclo + $gruposActuales = $maxCiclo + ? DB::table('alumno_grupo') + ->join('grupos', 'grupos.id', '=', 'alumno_grupo.grupo_id') + ->where('alumno_grupo.alumno_id', $alumno->id) + ->where('grupos.ciclo', $maxCiclo) + ->pluck('alumno_grupo.grupo_id') + : collect(); $todasTareas = Tarea::whereIn('grupo_id', $gruposActuales) ->with(['material', 'grupo']) @@ -59,15 +71,21 @@ class EntregaController extends Controller public function store(Request $request, Tarea $tarea) { $request->validate([ - 'archivo' => 'nullable|file|max:20480', + 'archivo' => 'nullable|file|max:2048', 'texto' => 'nullable|string|max:5000', ]); if (!$request->hasFile('archivo') && !filled($request->texto)) { - return back()->withErrors(['entrega' => 'Debes escribir un texto o adjuntar un archivo.']); + return redirect()->route('mis.tareas') + ->withErrors(['entrega' => 'Debes escribir un texto o adjuntar un archivo.']); } - $alumno = Alumno::whereHas('alumnos', fn($q) => $q->where('id', Auth::id()))->firstOrFail(); + $alumno = Alumno::whereHas('alumnos', fn($q) => $q->where('id', Auth::id()))->first(); + + if (!$alumno) { + return redirect()->route('mis.tareas') + ->with('error', 'No se encontró un perfil de alumno para tu cuenta.'); + } $entrega = Entrega::where('tarea_id', $tarea->id) ->where('alumno_id', $alumno->id) @@ -82,12 +100,12 @@ class EntregaController extends Controller } if ($entrega->status !== 'sin_subir') { - return back()->with('error', 'Ya entregaste esta tarea y no puedes volver a subirla.'); + return redirect()->route('mis.tareas') + ->with('error', 'Ya entregaste esta tarea y no puedes volver a subirla.'); } - $path = null; + $path = $entrega->archivo; if ($request->hasFile('archivo')) { - // Almacena en disco local (privado) igual que el FileController espera $path = $request->file('archivo')->store("entregas/{$tarea->id}"); } @@ -97,6 +115,7 @@ class EntregaController extends Controller 'status' => 'pendiente', ]); - return back()->with('info', 'Tarea enviada correctamente. Queda pendiente de calificación.'); + return redirect()->route('mis.tareas') + ->with('info', 'Tarea enviada correctamente. Queda pendiente de calificación.'); } } diff --git a/app/Http/Controllers/EvaluacionReporteController.php b/app/Http/Controllers/EvaluacionReporteController.php new file mode 100644 index 00000000..f14960f1 --- /dev/null +++ b/app/Http/Controllers/EvaluacionReporteController.php @@ -0,0 +1,222 @@ +buildDatos($evaluacion); + $totalSubmissions = $evaluacion->submissions()->count(); + + return view('evaluacion.reporte', compact('evaluacion', 'datos', 'totalSubmissions')); + } + + public function pdf(Evaluacion $evaluacion) + { + $datos = $this->buildDatos($evaluacion); + $totalSubmissions = $evaluacion->submissions()->count(); + + // Fetch chart images from QuickChart and embed as base64 + foreach ($datos as &$item) { + if ($item['tipo'] === 'texto_libre') continue; + + if ($item['es_docente']) { + foreach ($item['por_docente'] as &$pd) { + if (isset($pd['chart_config'])) { + $pd['chart_img'] = $this->fetchChartBase64($pd['chart_config']); + } + } + } else { + if (isset($item['chart_config'])) { + $item['chart_img'] = $this->fetchChartBase64($item['chart_config']); + } + } + } + + $pdf = Pdf::loadView('evaluacion.reporte_pdf', compact('evaluacion', 'datos', 'totalSubmissions')) + ->setPaper('a4', 'portrait'); + + return $pdf->download('reporte-' . $evaluacion->id . '.pdf'); + } + + // ─── Data builder ─────────────────────────────────────────────────────────── + + private function buildDatos(Evaluacion $evaluacion): array + { + $preguntas = $evaluacion->preguntas() + ->with(['tipo', 'opciones', 'respuestas.opcion']) + ->get(); + + $datos = []; + + foreach ($preguntas as $pregunta) { + $tipoSlug = $pregunta->tipo->slug; + $respuestas = $pregunta->respuestas; + + if ($pregunta->es_docente) { + $docenteIds = $respuestas->pluck('docente_id')->unique()->filter(); + $porDocente = []; + + foreach ($docenteIds as $docenteId) { + $rsp = $respuestas->where('docente_id', $docenteId); + $docente = Docente::with('docentes')->find($docenteId); + $user = $docente?->docentes->first(); + $nombre = $user + ? trim("{$user->apellidoPaterno} {$user->apellidoMaterno} {$user->name}") + : "Docente #{$docenteId}"; + + $chart = $this->procesarRespuestas($rsp, $tipoSlug, $pregunta); + + $porDocente[] = [ + 'docente_nombre' => $nombre, + 'chart_config' => $chart['config'] ?? null, + 'textos' => $chart['textos'] ?? [], + 'promedio' => $chart['promedio'] ?? null, + 'total' => $rsp->count(), + ]; + } + + $datos[] = [ + 'pregunta' => $pregunta, + 'tipo' => $tipoSlug, + 'es_docente' => true, + 'por_docente' => $porDocente, + ]; + } else { + $chart = $this->procesarRespuestas($respuestas, $tipoSlug, $pregunta); + + $datos[] = [ + 'pregunta' => $pregunta, + 'tipo' => $tipoSlug, + 'es_docente' => false, + 'chart_config' => $chart['config'] ?? null, + 'textos' => $chart['textos'] ?? [], + 'promedio' => $chart['promedio'] ?? null, + 'total' => $respuestas->count(), + ]; + } + } + + return $datos; + } + + private function procesarRespuestas($respuestas, string $tipoSlug, $pregunta): array + { + switch ($tipoSlug) { + + case 'si_no': + $si = $respuestas->where('valor', 'si')->count(); + $no = $respuestas->where('valor', 'no')->count(); + return ['config' => [ + 'type' => 'doughnut', + 'data' => [ + 'labels' => ['Sí', 'No'], + 'datasets' => [[ + 'data' => [$si, $no], + 'backgroundColor' => ['#1cc88a', '#e74a3b'], + 'borderWidth' => 2, + ]], + ], + 'options' => [ + 'plugins' => [ + 'legend' => ['position' => 'bottom'], + 'datalabels' => ['display' => false], + ], + ], + ]]; + + case 'opcion_multiple': + $labels = []; + $counts = []; + $colors = ['#4e73df','#1cc88a','#36b9cc','#f6c23e','#e74a3b','#858796','#5a5c69']; + foreach ($pregunta->opciones as $idx => $opcion) { + $labels[] = $opcion->texto; + $counts[] = $respuestas->where('opcion_id', $opcion->id)->count(); + } + return ['config' => [ + 'type' => 'bar', + 'data' => [ + 'labels' => $labels, + 'datasets' => [[ + 'label' => 'Respuestas', + 'data' => $counts, + 'backgroundColor' => array_slice(array_merge($colors, $colors), 0, count($labels)), + 'borderRadius' => 4, + ]], + ], + 'options' => [ + 'plugins' => ['legend' => ['display' => false]], + 'scales' => ['y' => ['beginAtZero' => true, 'ticks' => ['stepSize' => 1]]], + ], + ]]; + + case 'escala_5': + case 'escala_10': + $max = $tipoSlug === 'escala_5' ? 5 : 10; + $labels = range(1, $max); + $counts = []; + foreach ($labels as $val) { + $counts[] = $respuestas->filter(fn($r) => (int)$r->valor === $val)->count(); + } + $total = $respuestas->count(); + $suma = $respuestas->sum(fn($r) => (int)$r->valor); + $prom = $total > 0 ? round($suma / $total, 2) : null; + + return [ + 'config' => [ + 'type' => 'bar', + 'data' => [ + 'labels' => $labels, + 'datasets' => [[ + 'label' => 'Respuestas', + 'data' => $counts, + 'backgroundColor' => '#36b9cc', + 'borderRadius' => 4, + ]], + ], + 'options' => [ + 'plugins' => ['legend' => ['display' => false]], + 'scales' => ['y' => ['beginAtZero' => true, 'ticks' => ['stepSize' => 1]]], + ], + ], + 'promedio' => $prom, + ]; + + case 'texto_libre': + return ['textos' => $respuestas->pluck('valor')->filter()->values()->toArray()]; + + default: + return []; + } + } + + // ─── QuickChart helper ─────────────────────────────────────────────────────── + + private function fetchChartBase64(array $config): ?string + { + try { + $response = Http::timeout(15)->post('https://quickchart.io/chart', [ + 'chart' => $config, + 'width' => 500, + 'height' => 280, + 'backgroundColor' => 'white', + 'format' => 'png', + ]); + + if ($response->successful()) { + return 'data:image/png;base64,' . base64_encode($response->body()); + } + } catch (\Throwable) { + // QuickChart no disponible; la imagen simplemente no aparece + } + + return null; + } +} diff --git a/app/Http/Controllers/EvaluacionResponderController.php b/app/Http/Controllers/EvaluacionResponderController.php index 62af8625..1e9e4629 100644 --- a/app/Http/Controllers/EvaluacionResponderController.php +++ b/app/Http/Controllers/EvaluacionResponderController.php @@ -98,12 +98,35 @@ class EvaluacionResponderController extends Controller 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(); + // Ciclo más alto del alumno = semestre actual + $cicloActual = DB::table('alumno_grupo') + ->join('grupos', 'grupos.id', '=', 'alumno_grupo.grupo_id') + ->where('alumno_grupo.alumno_id', $alumno->id) + ->max('grupos.ciclo'); + + if ($cicloActual) { + // Grupos del alumno en ese ciclo + $gruposIds = DB::table('alumno_grupo') + ->join('grupos', 'grupos.id', '=', 'alumno_grupo.grupo_id') + ->where('alumno_grupo.alumno_id', $alumno->id) + ->where('grupos.ciclo', $cicloActual) + ->pluck('alumno_grupo.grupo_id') + ->toArray(); + + // Docentes asignados a esos grupos cuya materia tenga el mismo ciclo que el grupo + $docenteIds = DB::table('docente_grupo') + ->join('materials', 'materials.id', '=', 'docente_grupo.materia_id') + ->whereIn('docente_grupo.grupo_id', $gruposIds) + ->where('materials.ciclo', $cicloActual) + ->distinct() + ->pluck('docente_grupo.docente_id') + ->toArray(); + + $docentesParaEvaluar = Docente::whereIn('id', $docenteIds) + ->with('docentes') + ->get() + ->values(); + } } } diff --git a/app/Http/Controllers/GrupoController.php b/app/Http/Controllers/GrupoController.php index a190359f..eca77703 100644 --- a/app/Http/Controllers/GrupoController.php +++ b/app/Http/Controllers/GrupoController.php @@ -27,7 +27,7 @@ class GrupoController extends Controller { $user = Auth::user(); - if ($user->hasRole('Admin')) { + if ($user->hasRole('Administrador')) { $grupos = Grupo::all(); } diff --git a/app/Http/Controllers/ProspectoController.php b/app/Http/Controllers/ProspectoController.php new file mode 100644 index 00000000..fd1beed1 --- /dev/null +++ b/app/Http/Controllers/ProspectoController.php @@ -0,0 +1,10 @@ +get(); } + private function refreshMateriasSelect(): void + { + $grupo = $this->grupo; + $materiasAsignadas = DB::table('docente_grupo') + ->where('grupo_id', $grupo->id) + ->pluck('materia_id') + ->toArray(); + + $this->materiasSelect = Material::whereHas('carreraMaterial', function ($query) use ($grupo) { + $query->where('carreras.id', $grupo->plan->carrera); + }) + ->where('ciclo', $grupo->ciclo) + ->when(!empty($materiasAsignadas), fn($q) => $q->whereNotIn('id', $materiasAsignadas)) + ->get(); + } + public function guardar() { $this->validate([ @@ -71,6 +87,7 @@ class GrupoDocente extends Component ]); $this->reset(['docente_id', 'materia_id']); + $this->refreshMateriasSelect(); $this->dispatch('close-modal-docente'); $this->dispatch('swal', icon: 'success', title: '¡Listo!', @@ -96,6 +113,8 @@ class GrupoDocente extends Component $this->grupo->docentes()->wherePivot('materia_id', $materiaId)->detach($docenteId); + $this->refreshMateriasSelect(); + $this->dispatch('swal', icon: 'success', title: 'Eliminado', text: 'Docente y sus tareas removidos correctamente', diff --git a/app/Models/Anuncio.php b/app/Models/Anuncio.php index c3fff5e0..7cbdd5e5 100644 --- a/app/Models/Anuncio.php +++ b/app/Models/Anuncio.php @@ -31,7 +31,7 @@ class Anuncio extends Model 'all' => 'Todos', 'Alumno' => 'Alumnos', 'Docente' => 'Docentes', - 'Admin' => 'Administradores', + 'Administrador' => 'Administradores', 'Control escolar' => 'Control escolar', 'Coordinación académica' => 'Coordinación académica', 'Programador' => 'Programadores', diff --git a/composer.json b/composer.json index 51a92533..81033f35 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,7 @@ "license": "MIT", "require": { "php": "^8.2", + "barryvdh/laravel-dompdf": "^3.1", "laravel/cashier-paddle": "^2.6", "laravel/framework": "^12.0", "laravel/jetstream": "^5.4", diff --git a/composer.lock b/composer.lock index 02975c53..91b988cc 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "28550ccb2083e2f7896811098a56fc72", + "content-hash": "a61dc0d9947c0fec7f5e1704c31eb732", "packages": [ { "name": "bacon/bacon-qr-code", @@ -61,6 +61,83 @@ }, "time": "2025-11-19T17:15:36+00:00" }, + { + "name": "barryvdh/laravel-dompdf", + "version": "v3.1.2", + "source": { + "type": "git", + "url": "https://github.com/barryvdh/laravel-dompdf.git", + "reference": "ee3b72b19ccdf57d0243116ecb2b90261344dedc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/ee3b72b19ccdf57d0243116ecb2b90261344dedc", + "reference": "ee3b72b19ccdf57d0243116ecb2b90261344dedc", + "shasum": "" + }, + "require": { + "dompdf/dompdf": "^3.0", + "illuminate/support": "^9|^10|^11|^12|^13.0", + "php": "^8.1" + }, + "require-dev": { + "larastan/larastan": "^2.7|^3.0", + "orchestra/testbench": "^7|^8|^9.16|^10|^11.0", + "phpro/grumphp": "^2.5", + "squizlabs/php_codesniffer": "^3.5" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "PDF": "Barryvdh\\DomPDF\\Facade\\Pdf", + "Pdf": "Barryvdh\\DomPDF\\Facade\\Pdf" + }, + "providers": [ + "Barryvdh\\DomPDF\\ServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Barryvdh\\DomPDF\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Barry vd. Heuvel", + "email": "barryvdh@gmail.com" + } + ], + "description": "A DOMPDF Wrapper for Laravel", + "keywords": [ + "dompdf", + "laravel", + "pdf" + ], + "support": { + "issues": "https://github.com/barryvdh/laravel-dompdf/issues", + "source": "https://github.com/barryvdh/laravel-dompdf/tree/v3.1.2" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2026-02-21T08:51:10+00:00" + }, { "name": "brick/math", "version": "0.14.1", @@ -612,6 +689,161 @@ ], "time": "2024-02-05T11:56:58+00:00" }, + { + "name": "dompdf/dompdf", + "version": "v3.1.5", + "source": { + "type": "git", + "url": "https://github.com/dompdf/dompdf.git", + "reference": "f11ead23a8a76d0ff9bbc6c7c8fd7e05ca328496" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/dompdf/zipball/f11ead23a8a76d0ff9bbc6c7c8fd7e05ca328496", + "reference": "f11ead23a8a76d0ff9bbc6c7c8fd7e05ca328496", + "shasum": "" + }, + "require": { + "dompdf/php-font-lib": "^1.0.0", + "dompdf/php-svg-lib": "^1.0.0", + "ext-dom": "*", + "ext-mbstring": "*", + "masterminds/html5": "^2.0", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "ext-gd": "*", + "ext-json": "*", + "ext-zip": "*", + "mockery/mockery": "^1.3", + "phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "^3.5", + "symfony/process": "^4.4 || ^5.4 || ^6.2 || ^7.0" + }, + "suggest": { + "ext-gd": "Needed to process images", + "ext-gmagick": "Improves image processing performance", + "ext-imagick": "Improves image processing performance", + "ext-zlib": "Needed for pdf stream compression" + }, + "type": "library", + "autoload": { + "psr-4": { + "Dompdf\\": "src/" + }, + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "authors": [ + { + "name": "The Dompdf Community", + "homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md" + } + ], + "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter", + "homepage": "https://github.com/dompdf/dompdf", + "support": { + "issues": "https://github.com/dompdf/dompdf/issues", + "source": "https://github.com/dompdf/dompdf/tree/v3.1.5" + }, + "time": "2026-03-03T13:54:37+00:00" + }, + { + "name": "dompdf/php-font-lib", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/dompdf/php-font-lib.git", + "reference": "a6e9a688a2a80016ac080b97be73d3e10c444c9a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/a6e9a688a2a80016ac080b97be73d3e10c444c9a", + "reference": "a6e9a688a2a80016ac080b97be73d3e10c444c9a", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11 || ^12" + }, + "type": "library", + "autoload": { + "psr-4": { + "FontLib\\": "src/FontLib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "The FontLib Community", + "homepage": "https://github.com/dompdf/php-font-lib/blob/master/AUTHORS.md" + } + ], + "description": "A library to read, parse, export and make subsets of different types of font files.", + "homepage": "https://github.com/dompdf/php-font-lib", + "support": { + "issues": "https://github.com/dompdf/php-font-lib/issues", + "source": "https://github.com/dompdf/php-font-lib/tree/1.0.2" + }, + "time": "2026-01-20T14:10:26+00:00" + }, + { + "name": "dompdf/php-svg-lib", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/dompdf/php-svg-lib.git", + "reference": "8259ffb930817e72b1ff1caef5d226501f3dfeb1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/8259ffb930817e72b1ff1caef5d226501f3dfeb1", + "reference": "8259ffb930817e72b1ff1caef5d226501f3dfeb1", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^7.1 || ^8.0", + "sabberworm/php-css-parser": "^8.4 || ^9.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11" + }, + "type": "library", + "autoload": { + "psr-4": { + "Svg\\": "src/Svg" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "The SvgLib Community", + "homepage": "https://github.com/dompdf/php-svg-lib/blob/master/AUTHORS.md" + } + ], + "description": "A library to read, parse and export to PDF SVG files.", + "homepage": "https://github.com/dompdf/php-svg-lib", + "support": { + "issues": "https://github.com/dompdf/php-svg-lib/issues", + "source": "https://github.com/dompdf/php-svg-lib/tree/1.0.2" + }, + "time": "2026-01-02T16:01:13+00:00" + }, { "name": "dragonmantank/cron-expression", "version": "v3.6.0", @@ -2728,6 +2960,73 @@ ], "time": "2026-01-23T05:41:38+00:00" }, + { + "name": "masterminds/html5", + "version": "2.10.0", + "source": { + "type": "git", + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "fcf91eb64359852f00d921887b219479b4f21251" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251", + "reference": "fcf91eb64359852f00d921887b219479b4f21251", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Masterminds\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", + "keywords": [ + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" + ], + "support": { + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.10.0" + }, + "time": "2025-07-25T09:04:22+00:00" + }, { "name": "mobiledetect/mobiledetectlib", "version": "4.8.09", @@ -5121,6 +5420,86 @@ ], "time": "2024-06-11T12:45:25+00:00" }, + { + "name": "sabberworm/php-css-parser", + "version": "v9.3.0", + "source": { + "type": "git", + "url": "https://github.com/MyIntervals/PHP-CSS-Parser.git", + "reference": "88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949", + "reference": "88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": "^7.2.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "thecodingmachine/safe": "^1.3 || ^2.5 || ^3.4" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "1.4.0", + "phpstan/extension-installer": "1.4.3", + "phpstan/phpstan": "1.12.32 || 2.1.32", + "phpstan/phpstan-phpunit": "1.4.2 || 2.0.8", + "phpstan/phpstan-strict-rules": "1.6.2 || 2.0.7", + "phpunit/phpunit": "8.5.52", + "rawr/phpunit-data-provider": "3.3.1", + "rector/rector": "1.2.10 || 2.2.8", + "rector/type-perfect": "1.0.0 || 2.1.0", + "squizlabs/php_codesniffer": "4.0.1", + "thecodingmachine/phpstan-safe-rule": "1.2.0 || 1.4.1" + }, + "suggest": { + "ext-mbstring": "for parsing UTF-8 CSS" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "9.4.x-dev" + } + }, + "autoload": { + "files": [ + "src/Rule/Rule.php", + "src/RuleSet/RuleContainer.php" + ], + "psr-4": { + "Sabberworm\\CSS\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Raphael Schweikert" + }, + { + "name": "Oliver Klee", + "email": "github@oliverklee.de" + }, + { + "name": "Jake Hotson", + "email": "jake.github@qzdesign.co.uk" + } + ], + "description": "Parser for CSS Files written in PHP", + "homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser", + "keywords": [ + "css", + "parser", + "stylesheet" + ], + "support": { + "issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues", + "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v9.3.0" + }, + "time": "2026-03-03T17:31:43+00:00" + }, { "name": "spatie/laravel-permission", "version": "6.24.0", @@ -7964,6 +8343,149 @@ ], "time": "2025-10-27T20:36:44+00:00" }, + { + "name": "thecodingmachine/safe", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/thecodingmachine/safe.git", + "reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/705683a25bacf0d4860c7dea4d7947bfd09eea19", + "reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpstan/phpstan": "^2", + "phpunit/phpunit": "^10", + "squizlabs/php_codesniffer": "^3.2" + }, + "type": "library", + "autoload": { + "files": [ + "lib/special_cases.php", + "generated/apache.php", + "generated/apcu.php", + "generated/array.php", + "generated/bzip2.php", + "generated/calendar.php", + "generated/classobj.php", + "generated/com.php", + "generated/cubrid.php", + "generated/curl.php", + "generated/datetime.php", + "generated/dir.php", + "generated/eio.php", + "generated/errorfunc.php", + "generated/exec.php", + "generated/fileinfo.php", + "generated/filesystem.php", + "generated/filter.php", + "generated/fpm.php", + "generated/ftp.php", + "generated/funchand.php", + "generated/gettext.php", + "generated/gmp.php", + "generated/gnupg.php", + "generated/hash.php", + "generated/ibase.php", + "generated/ibmDb2.php", + "generated/iconv.php", + "generated/image.php", + "generated/imap.php", + "generated/info.php", + "generated/inotify.php", + "generated/json.php", + "generated/ldap.php", + "generated/libxml.php", + "generated/lzf.php", + "generated/mailparse.php", + "generated/mbstring.php", + "generated/misc.php", + "generated/mysql.php", + "generated/mysqli.php", + "generated/network.php", + "generated/oci8.php", + "generated/opcache.php", + "generated/openssl.php", + "generated/outcontrol.php", + "generated/pcntl.php", + "generated/pcre.php", + "generated/pgsql.php", + "generated/posix.php", + "generated/ps.php", + "generated/pspell.php", + "generated/readline.php", + "generated/rnp.php", + "generated/rpminfo.php", + "generated/rrd.php", + "generated/sem.php", + "generated/session.php", + "generated/shmop.php", + "generated/sockets.php", + "generated/sodium.php", + "generated/solr.php", + "generated/spl.php", + "generated/sqlsrv.php", + "generated/ssdeep.php", + "generated/ssh2.php", + "generated/stream.php", + "generated/strings.php", + "generated/swoole.php", + "generated/uodbc.php", + "generated/uopz.php", + "generated/url.php", + "generated/var.php", + "generated/xdiff.php", + "generated/xml.php", + "generated/xmlrpc.php", + "generated/yaml.php", + "generated/yaz.php", + "generated/zip.php", + "generated/zlib.php" + ], + "classmap": [ + "lib/DateTime.php", + "lib/DateTimeImmutable.php", + "lib/Exceptions/", + "generated/Exceptions/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHP core functions that throw exceptions instead of returning FALSE on error", + "support": { + "issues": "https://github.com/thecodingmachine/safe/issues", + "source": "https://github.com/thecodingmachine/safe/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://github.com/OskarStark", + "type": "github" + }, + { + "url": "https://github.com/shish", + "type": "github" + }, + { + "url": "https://github.com/silasjoisten", + "type": "github" + }, + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2026-02-04T18:08:13+00:00" + }, { "name": "tijsverkoyen/css-to-inline-styles", "version": "v2.4.0", diff --git a/database/seeders/RoleSeeder.php b/database/seeders/RoleSeeder.php index fb117268..67c7ef31 100644 --- a/database/seeders/RoleSeeder.php +++ b/database/seeders/RoleSeeder.php @@ -11,7 +11,7 @@ class RoleSeeder extends Seeder public function run(): void { - $role1 = Role::create(['name'=>'Admin']); + $role1 = Role::create(['name'=>'Administrador']); $role2 = Role::create(['name'=>'Programador']); $role3 = Role::create(['name'=>'Promotor']); $role4 = Role::create(['name'=>'Alumno']); @@ -74,6 +74,11 @@ class RoleSeeder extends Seeder Permission::create(['name'=>'alumno.edit','description'=>'Editar alumnos'])->syncRoles([$role1,$role3,$role5]); Permission::create(['name'=>'alumno.destroy','description'=>'Eliminar alumnos'])->syncRoles([$role1]); + Permission::create(['name'=>'prospecto.index','description'=>'Ver prospectos'])->syncRoles([$role1,$role3]); + Permission::create(['name'=>'prospecto.create','description'=>'Crear prospectos'])->syncRoles([$role1,$role3]); + Permission::create(['name'=>'prospecto.edit','description'=>'Editar prospectos'])->syncRoles([$role1,$role3]); + Permission::create(['name'=>'prospecto.destroy','description'=>'Eliminar prospectos'])->syncRoles([$role1]); + Permission::create(['name'=>'area.index','description'=>'Ver areas'])->syncRoles([$role1]); Permission::create(['name'=>'area.create','description'=>'Crear areas'])->syncRoles([$role1]); Permission::create(['name'=>'area.edit','description'=>'Editar areas'])->syncRoles([$role1]); @@ -98,6 +103,7 @@ class RoleSeeder extends Seeder Permission::create(['name'=>'plane.create','description'=>'Crear planes de pago'])->syncRoles([$role1]); Permission::create(['name'=>'plane.edit','description'=>'Editar planes de pago'])->syncRoles([$role1]); Permission::create(['name'=>'plane.destroy','description'=>'Eliminar planes de pago'])->syncRoles([$role1]); + Permission::create(['name'=>'pagar','description'=>'Pagar'])->syncRoles([$role4]); Permission::create(['name'=>'concepto.index','description'=>'Ver conceptos'])->syncRoles([$role1]); Permission::create(['name'=>'concepto.create','description'=>'Crear conceptos'])->syncRoles([$role1]); @@ -126,7 +132,7 @@ class RoleSeeder extends Seeder Permission::create(['name'=>'pago.edit','description'=>'Editar pagos'])->syncRoles([$role1]); Permission::create(['name'=>'pago.destroy','description'=>'Eliminar pagos'])->syncRoles([$role1,$role3]); - Permission::create(['name'=>'grupo.index','description'=>'Ver grupos'])->syncRoles([$role1,$role4,$role5,$role6,$role7]); + Permission::create(['name'=>'grupo.index','description'=>'Ver grupos'])->syncRoles([$role1,$role5,$role6,$role7]); Permission::create(['name'=>'grupo.create','description'=>'Crear grupos'])->syncRoles([$role1,$role5]); Permission::create(['name'=>'grupo.edit','description'=>'Editar grupos'])->syncRoles([$role1,$role5,$role6]); Permission::create(['name'=>'grupo.destroy','description'=>'Eliminar grupos'])->syncRoles([$role1,$role5]); @@ -141,6 +147,23 @@ class RoleSeeder extends Seeder Permission::create(['name'=>'documentoTipo.edit','description'=>'Editar documentación'])->syncRoles([$role1]); Permission::create(['name'=>'documentoTipo.destroy','description'=>'Eliminar documentación'])->syncRoles([$role1]); + Permission::create(['name'=>'evaluaciones.index','description'=>'Ver evaluaciones'])->syncRoles([$role1,$role7,$role4]); + Permission::create(['name'=>'evaluaciones.create','description'=>'Crear evaluaciones'])->syncRoles([$role1]); + Permission::create(['name'=>'evaluaciones.edit','description'=>'Editar evaluaciones'])->syncRoles([$role1]); + Permission::create(['name'=>'evaluaciones.destroy','description'=>'Eliminar evaluaciones'])->syncRoles([$role1]); + Permission::create(['name'=>'evaluaciones.responder','description'=>'responder evaluaciones'])->syncRoles([$role4,$role7]); + + Permission::create(['name'=>'anuncios.index','description'=>'Ver anuncios'])->syncRoles([$role1]); + Permission::create(['name'=>'anuncios.create','description'=>'Crear anuncios'])->syncRoles([$role1]); + Permission::create(['name'=>'anuncios.edit','description'=>'Editar anuncios'])->syncRoles([$role1]); + Permission::create(['name'=>'anuncios.destroy','description'=>'Eliminar anuncios'])->syncRoles([$role1]); + + Permission::create(['name'=>'tareas.index','description'=>'Ver tareas'])->syncRoles([$role7]); + Permission::create(['name'=>'tareas.create','description'=>'Crear tareas'])->syncRoles([$role7]); + Permission::create(['name'=>'tareas.edit','description'=>'Editar tareas'])->syncRoles([$role7]); + Permission::create(['name'=>'tareas.destroy','description'=>'Eliminar tareas'])->syncRoles([$role7]); + Permission::create(['name'=>'tareas.entrega','description'=>'Entregar tareas'])->syncRoles([$role4]); + Permission::create(['name'=>'comunidad','description'=>'Comunidad'])->syncRoles([$role1]); Permission::create(['name'=>'amigos','description'=>'Amistades'])->syncRoles([$role1]); Permission::create(['name'=>'mensajes','description'=>'Mensajería'])->syncRoles([$role1]); diff --git a/database/seeders/UserSeeder.php b/database/seeders/UserSeeder.php index 5bc237cc..62c5b03e 100644 --- a/database/seeders/UserSeeder.php +++ b/database/seeders/UserSeeder.php @@ -23,7 +23,7 @@ class UserSeeder extends Seeder 'password' => bcrypt('6686'), 'code'=>1, 'status'=>1 - ])->assignRole('Admin'); + ])->assignRole('Administrador'); $user->plantelUsuarios()->sync(3); diff --git a/resources/views/alumno/edit.blade.php b/resources/views/alumno/edit.blade.php index 91f50bc4..13e3fa81 100644 --- a/resources/views/alumno/edit.blade.php +++ b/resources/views/alumno/edit.blade.php @@ -27,7 +27,138 @@ + + +
+ +
+
+
+
+
Expediente del alumno
+
+
+ +
+ +
+ + @php + $nivel = $alumno->nivelRel ?? null; + + $tipos = $nivel + ? $nivel->documentosRequeridos + : collect(); + + $documentos = collect($alumno?->documentos)->keyBy('tipo'); + @endphp + + @foreach($tipos as $tipo) + + @php + $doc = $documentos[$tipo->slug] ?? null; + @endphp + +
+ + @if($doc) + + @if($doc->status == 1) +
+ {{ strtoupper($tipo->name) }} : Validado + + +
+ + @elseif($doc->status == 2) +
+ {{ strtoupper($tipo->name) }} : Rechazado + + +
+ + @else + +
+ + {{ strtoupper($tipo->name) }} : Pendiente de revisión + + + +
+ + @endif + + + + @else +
+ {{ strtoupper($tipo->name) }} : Faltante +
+ @endif + +
+ @endforeach + +
+ +
+ +
+
+ +
+
+
+
+
Asignación de beca
+
+
+ +
+
+ +
+
+ + + + +
+ + + + + @endsection diff --git a/resources/views/alumno/index.blade.php b/resources/views/alumno/index.blade.php index 12020695..0f01384b 100644 --- a/resources/views/alumno/index.blade.php +++ b/resources/views/alumno/index.blade.php @@ -11,22 +11,23 @@
- @hasrole('Promotor') -
Prospectos
- @endrole - @hasrole('Control escolar') -
Alumnos
- @endrole - @can('alumno.create') + @if (Auth::user()->roles->contains('name', 'Promotor')) +
Prospectos
- @endcan + + @else +
Alumnos
+ + @endif + +
- @hasrole('Promotor') + @if (Auth::user()->roles->contains('name', 'Promotor'))
@forelse ($alumnos as $alumno) @@ -64,14 +65,16 @@
@livewire('modal-prospecto') - @endrole - @hasrole('Control escolar') + + @else + + @@ -86,10 +89,18 @@ + + @@ -118,24 +129,20 @@ - @@ -151,8 +158,7 @@
IdFoto Alumno Campus Status{{ $alumno->id }} + @foreach($alumno->alumnos as $user) + + @endforeach + @foreach($alumno->alumnos as $user) - {{ $user->name }} + {{ $user->name }} {{ $user->apellidoPaterno }} {{ $user->apellidoMaterno }} @endforeach + @can('alumno.edit') - @endcan @can('alumno.destroy') -
@csrf @method('DELETE')
-
@endcan
- @endrole - + @endif
@@ -192,7 +198,6 @@ function verDocumento(url, docId) { Livewire.dispatch('set-documento', { id: docId }); - // 🔥 guardar instancia global visorModalInstance = new bootstrap.Modal( document.getElementById('visorModal') ); @@ -208,12 +213,21 @@ document.addEventListener('livewire:init', () => { visorModalInstance.hide(); } - // 🔥 limpiar iframe document.getElementById('visorFrame').src = ""; }); }); + +function verFoto(url,nombre){ + Swal.fire({ + text: nombre, + imageUrl: url, + imageWidth: 400, + imageHeight: 400, +}); +} + @endsection diff --git a/resources/views/evaluacion/_input_pregunta.blade.php b/resources/views/evaluacion/_input_pregunta.blade.php index 4b6c88f4..4953ed63 100644 --- a/resources/views/evaluacion/_input_pregunta.blade.php +++ b/resources/views/evaluacion/_input_pregunta.blade.php @@ -58,7 +58,7 @@
@elseif($tipoSlug === 'escala_5') -
+
@for($k = 1; $k <= 5; $k++)
requerida ? 'required' : '' }}> -
@endfor @@ -75,7 +75,7 @@
@elseif($tipoSlug === 'escala_10') -
+
@for($k = 1; $k <= 10; $k++)
requerida ? 'required' : '' }}> -
@endfor 1 = Muy malo   10 = Excelente
@endif + +@once + +@endonce diff --git a/resources/views/evaluacion/reporte.blade.php b/resources/views/evaluacion/reporte.blade.php new file mode 100644 index 00000000..a2a1509c --- /dev/null +++ b/resources/views/evaluacion/reporte.blade.php @@ -0,0 +1,242 @@ +@extends('layouts.landing') + +@section('title', 'Reporte — ' . $evaluacion->titulo) + +@section('content') +
+ + {{-- Header --}} +
+
+
+ {{ $evaluacion->titulo }} +
+ Reporte de resultados +
+ +
+ + {{-- Tarjetas de resumen --}} +
+
+
+
+
+
+
Participantes
+
{{ $totalSubmissions }}
+
+
+
+
+
+
+
+
+
+
+
+
Preguntas
+
{{ count($datos) }}
+
+
+
+
+
+
+
+
+
+
+
+
Estado
+
{{ ucfirst($evaluacion->status) }}
+
+
+
+
+
+
+
+
+
+
+
+
Dirigida a
+
{{ $evaluacion->tipo?->nombre }}
+
+
+
+
+
+
+
+ + {{-- Sin respuestas --}} + @if($totalSubmissions === 0) +
+ + Esta evaluación aún no tiene respuestas registradas. +
+ @endif + + {{-- Preguntas --}} + @foreach($datos as $i => $item) + @php $pregunta = $item['pregunta']; @endphp + +
+
+
+ {{ $i + 1 }}. {{ $pregunta->texto }} + @if($pregunta->es_docente) + + Por docente + + @endif + @if($pregunta->requerida) + * + @endif +
+
+ {{ $pregunta->tipo?->nombre }} + @if(!$item['es_docente'] && isset($item['promedio'])) + + Promedio: {{ $item['promedio'] }} + + @endif +
+
+
+ + {{-- Texto libre --}} + @if($item['tipo'] === 'texto_libre') + + @if(!$item['es_docente']) + @forelse($item['textos'] as $txt) +
+ {{ $txt }} +
+ @empty +

Sin respuestas registradas.

+ @endforelse + @else + @foreach($item['por_docente'] as $pd) +
+
+ {{ $pd['docente_nombre'] }} + ({{ $pd['total'] }} respuesta(s)) +
+ @forelse($pd['textos'] as $txt) +
+ {{ $txt }} +
+ @empty +

Sin respuestas.

+ @endforelse +
+ @endforeach + @endif + + {{-- Gráfica única --}} + @elseif(!$item['es_docente']) + @if($item['total'] > 0) +
+
+ + +
+
+

+ + {{ $item['total'] }} respuesta(s) +

+ @if(isset($item['promedio'])) +
+ {{ $item['promedio'] }} +
+ promedio + @endif +
+
+ @else +

Sin respuestas registradas.

+ @endif + + {{-- Gráficas por docente --}} + @else + @if(empty($item['por_docente'])) +

Sin respuestas registradas.

+ @else +
+ @foreach($item['por_docente'] as $di => $pd) +
+
+
+ + {{ $pd['docente_nombre'] }} + + @if(isset($pd['promedio'])) + Prom: {{ $pd['promedio'] }} + @endif +
+
+ @if($pd['total'] > 0) + +

+ {{ $pd['total'] }} respuesta(s) +

+ @else +

Sin respuestas.

+ @endif +
+
+
+ @endforeach +
+ @endif + @endif + +
+
+ @endforeach + +
+@endsection + +@section('scripts') + + +@endsection diff --git a/resources/views/evaluacion/reporte_pdf.blade.php b/resources/views/evaluacion/reporte_pdf.blade.php new file mode 100644 index 00000000..3303296e --- /dev/null +++ b/resources/views/evaluacion/reporte_pdf.blade.php @@ -0,0 +1,355 @@ + + + + +Reporte — {{ $evaluacion->titulo }} + + + + +{{-- ENCABEZADO --}} +
+

{{ $evaluacion->titulo }}

+
+ Tipo: {{ $evaluacion->tipo?->nombre }} +  |  + Estado: {{ ucfirst($evaluacion->status) }} +  |  + Participantes: {{ $totalSubmissions }} +  |  + Generado: {{ now()->format('d/m/Y H:i') }} + @if($evaluacion->fecha_inicio || $evaluacion->fecha_fin) +  |  + Vigencia: + {{ $evaluacion->fecha_inicio?->format('d/m/Y') ?? '—' }} + al + {{ $evaluacion->fecha_fin?->format('d/m/Y') ?? '—' }} + @endif +
+ @if($evaluacion->descripcion) +
{{ $evaluacion->descripcion }}
+ @endif +
+ +{{-- RESUMEN --}} + + + + + + + +
+ Participantes + {{ $totalSubmissions }} + + Preguntas + {{ count($datos) }} + + Estado + {{ ucfirst($evaluacion->status) }} + + Tipo + {{ $evaluacion->tipo?->nombre }} +
+ +@if($totalSubmissions === 0) +

+ Esta evaluación aún no tiene respuestas registradas. +

+@endif + +{{-- PREGUNTAS --}} +@foreach($datos as $i => $item) +@php $pregunta = $item['pregunta']; @endphp + +
+ +
+ {{ $i + 1 }}. {{ $pregunta->texto }} + @if($pregunta->es_docente) +  Por docente + @endif + + {{ $pregunta->tipo?->nombre }} + +
+ +
+ + {{-- Texto libre --}} + @if($item['tipo'] === 'texto_libre') + + @if(!$item['es_docente']) + @forelse($item['textos'] as $txt) +
{{ $txt }}
+ @empty +

Sin respuestas registradas.

+ @endforelse + @else + @foreach($item['por_docente'] as $pd) +
+
+ {{ $pd['docente_nombre'] }} + + — {{ $pd['total'] }} respuesta(s) + +
+ @forelse($pd['textos'] as $txt) +
{{ $txt }}
+ @empty +

Sin respuestas.

+ @endforelse +
+ @endforeach + @endif + + {{-- Gráfica única --}} + @elseif(!$item['es_docente']) + + @if($item['total'] > 0) + + + + + +
+ @if(isset($item['chart_img'])) + Gráfica + @else +
Gráfica no disponible
+ @endif +
+
Respuestas
+
{{ $item['total'] }}
+ @if(isset($item['promedio'])) +
+
Promedio
+
{{ $item['promedio'] }}
+
/ {{ str_contains($item['tipo'], '10') ? '10' : '5' }}
+
+ @endif +
+ @else +

Sin respuestas registradas.

+ @endif + + {{-- Gráficas por docente (tabla 2 columnas) --}} + @else + + @if(empty($item['por_docente'])) +

Sin respuestas registradas.

+ @else + @php + $chunks = array_chunk($item['por_docente'], 2); + @endphp + @foreach($chunks as $pair) + + + @foreach($pair as $pd) + + @endforeach + @if(count($pair) === 1) + + @endif + +
+
+
+ {{ $pd['docente_nombre'] }} + @if(isset($pd['promedio'])) +  ·  + + Prom: {{ $pd['promedio'] }} + + @endif +
+ @if($pd['total'] > 0) + @if(isset($pd['chart_img'])) + Gráfica + @else +
Gráfica no disponible
+ @endif +

+ {{ $pd['total'] }} respuesta(s) +

+ @else +

Sin respuestas.

+ @endif +
+
+ @endforeach + @endif + + @endif + +
+
+@endforeach + + + + + diff --git a/resources/views/evaluacion/responder.blade.php b/resources/views/evaluacion/responder.blade.php index 0847a851..5406aed9 100644 --- a/resources/views/evaluacion/responder.blade.php +++ b/resources/views/evaluacion/responder.blade.php @@ -79,7 +79,7 @@ @php $user = $docente->docentes->first(); $fotoUrl = $user?->profile_photo_url; - $nombreDocente = $user?->name ?? 'Docente'; + $nombreDocente = trim("{$user?->apellidoPaterno} {$user?->apellidoMaterno} {$user?->name}") ?: 'Docente'; @endphp
@@ -138,8 +138,8 @@
-
@@ -149,3 +149,24 @@
@endsection + +@section('scripts') + +@endsection diff --git a/resources/views/evaluacion/show.blade.php b/resources/views/evaluacion/show.blade.php index fe2b13a4..207af824 100644 --- a/resources/views/evaluacion/show.blade.php +++ b/resources/views/evaluacion/show.blade.php @@ -17,8 +17,11 @@
{{ $evaluacion->titulo }}
-
- +
+ + Reporte + + Editar diff --git a/resources/views/layouts/_partials/menu.blade.php b/resources/views/layouts/_partials/menu.blade.php index c3a53568..a8a413c2 100644 --- a/resources/views/layouts/_partials/menu.blade.php +++ b/resources/views/layouts/_partials/menu.blade.php @@ -1,294 +1,218 @@ +
+ - @hasrole('Promotor') - - @endrole + @can('pagar') + + @endcan - @hasrole('Control escolar') - + @endcan - @can('alumno.index') - Alumnos - @endcan + @can('evaluaciones.responder') + + @endcan - @can('grupo.index') - Grupos - @endcan -
-
- - @endrole + @can('comunidad') + + @endcan - @hasrole('Coordinación académica') - + @endcan - @can('docente.index') - Docentes - @endcan - - @can('grupo.index') - Grupos - @endcan -
-
- - @endrole - - @hasrole(['Alumno']) - - @can('pago.index') - - @endcan - - - - - - @endrole - - @hasrole(['Docente']) - - - - - @endrole - - - - - - - - @can('comunidad') - - @endcan - - @can('amigos') - - @endcan - - @can('mensajes') - - @endcan + @can('mensajes') + + @endcan @can('asistencia') @endcan - - - @can('note.index') - + @endcan - +
+ diff --git a/resources/views/layouts/_partials/scripts.blade.php b/resources/views/layouts/_partials/scripts.blade.php index ef71b214..5a316ad5 100644 --- a/resources/views/layouts/_partials/scripts.blade.php +++ b/resources/views/layouts/_partials/scripts.blade.php @@ -17,3 +17,22 @@ + + + diff --git a/resources/views/livewire/documentos.blade.php b/resources/views/livewire/documentos.blade.php index 3a274f53..829d51c0 100644 --- a/resources/views/livewire/documentos.blade.php +++ b/resources/views/livewire/documentos.blade.php @@ -9,27 +9,42 @@ @endphp
- {{ strtoupper($tipo->name) }} : @if($doc) @if($doc->status == 1) - ✔ Validado +
+ {{ strtoupper($tipo->name) }} : Validado + + +
@elseif($doc->status == 2) - ✘ Rechazado +
+ {{ strtoupper($tipo->name) }} : Rechazado +
+ @else - Pendiente +
+ + {{ strtoupper($tipo->name) }} : Pendiente de revisión + + + +
@endif - - @else - ✘ Faltante +
+ {{ strtoupper($tipo->name) }} : Faltante +
@endif
diff --git a/resources/views/livewire/evaluacion-builder.blade.php b/resources/views/livewire/evaluacion-builder.blade.php index c669f181..c7b09274 100644 --- a/resources/views/livewire/evaluacion-builder.blade.php +++ b/resources/views/livewire/evaluacion-builder.blade.php @@ -84,10 +84,9 @@ @@ -410,3 +409,25 @@ {{-- fin columna lateral --}} {{-- fin row --}} + +@push('scripts') + +@endpush diff --git a/resources/views/profile/show.blade.php b/resources/views/profile/show.blade.php index 06eecb13..5b8e1a52 100644 --- a/resources/views/profile/show.blade.php +++ b/resources/views/profile/show.blade.php @@ -169,28 +169,7 @@ }); - function verDocumento(url) { - document.getElementById('visorFrame').src = url + '?t=' + new Date().getTime(); - var modal = new bootstrap.Modal(document.getElementById('visorModal')); - modal.show(); - } - - // 🔥 limpiar iframe al cerrar - document.getElementById('visorModal').addEventListener('hidden.bs.modal', function () { - document.getElementById('visorFrame').src = ''; - }); - - - - - -