Modificación del de vista de dopcumentos del alumno
This commit is contained in:
@@ -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'));
|
||||
}
|
||||
|
||||
|
||||
@@ -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.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Docente;
|
||||
use App\Models\Evaluacion;
|
||||
use Barryvdh\DomPDF\Facade\Pdf;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class EvaluacionReporteController extends Controller
|
||||
{
|
||||
public function show(Evaluacion $evaluacion)
|
||||
{
|
||||
$datos = $this->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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class GrupoController extends Controller
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
if ($user->hasRole('Admin')) {
|
||||
if ($user->hasRole('Administrador')) {
|
||||
$grupos = Grupo::all();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ProspectoController extends Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -59,6 +59,22 @@ class GrupoDocente extends Component
|
||||
->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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user