Modificación del userController para el cambnio de rol si es alumno o docente
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\Alumno;
|
use App\Models\Alumno;
|
||||||
|
use App\Models\Campan;
|
||||||
use App\Models\Entrega;
|
use App\Models\Entrega;
|
||||||
use App\Models\Tarea;
|
use App\Models\Tarea;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
@@ -16,22 +17,43 @@ class EntregaController extends Controller
|
|||||||
$alumno = Alumno::whereHas('alumnos', fn($q) => $q->where('id', Auth::id()))->first();
|
$alumno = Alumno::whereHas('alumnos', fn($q) => $q->where('id', Auth::id()))->first();
|
||||||
|
|
||||||
if (!$alumno) {
|
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'])
|
->with(['material', 'grupo'])
|
||||||
->orderByDesc('created_at')
|
->orderByDesc('created_at')
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$entregasMap = Entrega::where('alumno_id', $alumno->id)
|
$entregasMap = Entrega::where('alumno_id', $alumno->id)
|
||||||
->whereIn('tarea_id', $tareas->pluck('id'))
|
->whereIn('tarea_id', $todasTareas->pluck('id'))
|
||||||
->get()
|
->get()
|
||||||
->keyBy('tarea_id');
|
->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)
|
public function store(Request $request, Tarea $tarea)
|
||||||
|
|||||||
@@ -51,7 +51,13 @@ class EvaluacionController extends Controller
|
|||||||
{
|
{
|
||||||
$evaluacion->delete();
|
$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)
|
public function toggleStatus(Request $request, Evaluacion $evaluacion)
|
||||||
|
|||||||
@@ -2,9 +2,162 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
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\Http\Request;
|
||||||
|
use Illuminate\Routing\Controller;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class EvaluacionResponderController extends Controller
|
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.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,12 +188,8 @@ class GrupoController extends Controller
|
|||||||
|
|
||||||
public function update(Request $request,Grupo $grupo)
|
public function update(Request $request,Grupo $grupo)
|
||||||
{
|
{
|
||||||
$nivel = Plan::where('id',$request->plan_id)->first();
|
$grupo->update([
|
||||||
$grupo = Grupo::update([
|
'ciclo' => $request->ciclo,
|
||||||
'plan_id' => $request->plan_id,
|
|
||||||
'ciclo' => 1,
|
|
||||||
'nivel' => $nivel->nivel,
|
|
||||||
'plantel' => $request->plantel,
|
|
||||||
'turno' => $request->turno,
|
'turno' => $request->turno,
|
||||||
'status'=>1
|
'status'=>1
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -127,6 +127,24 @@ class TareaController extends Controller
|
|||||||
->with('info', 'Tarea eliminada.');
|
->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)
|
public function calificar(Request $request, Tarea $tarea, Alumno $alumno)
|
||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
|
|||||||
@@ -2,11 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\Alumno;
|
||||||
use App\Models\AreaCode;
|
use App\Models\AreaCode;
|
||||||
|
use App\Models\Docente;
|
||||||
use App\Models\Plantel;
|
use App\Models\Plantel;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Routing\Controller;
|
use Illuminate\Routing\Controller;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Password;
|
use Illuminate\Support\Facades\Password;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@@ -58,6 +61,8 @@ class UserController extends Controller
|
|||||||
|
|
||||||
$user->plantelUsuarios()->sync($request->plantels);
|
$user->plantelUsuarios()->sync($request->plantels);
|
||||||
|
|
||||||
|
$this->syncAlumnoDocente($user, $request->roles ?? []);
|
||||||
|
|
||||||
$token = Password::createToken($user);
|
$token = Password::createToken($user);
|
||||||
$user->notify(new ResetPassword($token));
|
$user->notify(new ResetPassword($token));
|
||||||
|
|
||||||
@@ -82,12 +87,14 @@ class UserController extends Controller
|
|||||||
$user->update($request->except(['plantels']));
|
$user->update($request->except(['plantels']));
|
||||||
$user->roles()->sync($request->roles);
|
$user->roles()->sync($request->roles);
|
||||||
$user->plantelUsuarios()->sync($request->plantels);
|
$user->plantelUsuarios()->sync($request->plantels);
|
||||||
|
$this->syncAlumnoDocente($user, $request->roles ?? []);
|
||||||
return redirect()->route('user.edit',compact('user'))->with('info','Se modifico el usuario correctamente');
|
return redirect()->route('user.edit',compact('user'))->with('info','Se modifico el usuario correctamente');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function destroy(User $user)
|
public function destroy(User $user)
|
||||||
{
|
{
|
||||||
|
$this->detachAlumnoDocente($user);
|
||||||
$user->plantelUsuarios()->detach();
|
$user->plantelUsuarios()->detach();
|
||||||
$user->roles()->detach();
|
$user->roles()->detach();
|
||||||
$user->delete();
|
$user->delete();
|
||||||
@@ -102,4 +109,65 @@ class UserController extends Controller
|
|||||||
public function profile(){
|
public function profile(){
|
||||||
return view('profile.show');
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ class EvaluacionBuilder extends Component
|
|||||||
'texto' => $p->texto,
|
'texto' => $p->texto,
|
||||||
'pregunta_tipo_id' => (string) $p->pregunta_tipo_id,
|
'pregunta_tipo_id' => (string) $p->pregunta_tipo_id,
|
||||||
'requerida' => (bool) $p->requerida,
|
'requerida' => (bool) $p->requerida,
|
||||||
|
'es_docente' => (bool) $p->es_docente,
|
||||||
'opciones' => $p->opciones->map(fn($o) => [
|
'opciones' => $p->opciones->map(fn($o) => [
|
||||||
'id' => $o->id,
|
'id' => $o->id,
|
||||||
'texto' => $o->texto,
|
'texto' => $o->texto,
|
||||||
@@ -74,6 +75,7 @@ class EvaluacionBuilder extends Component
|
|||||||
'texto' => '',
|
'texto' => '',
|
||||||
'pregunta_tipo_id' => (string) $defaultTipo,
|
'pregunta_tipo_id' => (string) $defaultTipo,
|
||||||
'requerida' => true,
|
'requerida' => true,
|
||||||
|
'es_docente' => false,
|
||||||
'opciones' => [],
|
'opciones' => [],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -164,6 +166,7 @@ class EvaluacionBuilder extends Component
|
|||||||
'texto' => $pData['texto'],
|
'texto' => $pData['texto'],
|
||||||
'pregunta_tipo_id' => $pData['pregunta_tipo_id'],
|
'pregunta_tipo_id' => $pData['pregunta_tipo_id'],
|
||||||
'requerida' => $pData['requerida'] ?? true,
|
'requerida' => $pData['requerida'] ?? true,
|
||||||
|
'es_docente' => $pData['es_docente'] ?? false,
|
||||||
'orden' => $orden,
|
'orden' => $orden,
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
@@ -171,6 +174,7 @@ class EvaluacionBuilder extends Component
|
|||||||
'texto' => $pData['texto'],
|
'texto' => $pData['texto'],
|
||||||
'pregunta_tipo_id' => $pData['pregunta_tipo_id'],
|
'pregunta_tipo_id' => $pData['pregunta_tipo_id'],
|
||||||
'requerida' => $pData['requerida'] ?? true,
|
'requerida' => $pData['requerida'] ?? true,
|
||||||
|
'es_docente' => $pData['es_docente'] ?? false,
|
||||||
'orden' => $orden,
|
'orden' => $orden,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('preguntas', function (Blueprint $table) {
|
||||||
|
$table->boolean('es_docente')->default(false)->after('requerida');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('preguntas', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('es_docente');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('respuestas', function (Blueprint $table) {
|
||||||
|
$table->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');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -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')
|
||||||
|
<textarea name="{{ $fieldName }}"
|
||||||
|
id="{{ $fieldId }}"
|
||||||
|
class="form-control"
|
||||||
|
rows="4"
|
||||||
|
placeholder="Escribe tu respuesta aquí…"
|
||||||
|
{{ $pregunta->requerida ? 'required' : '' }}></textarea>
|
||||||
|
|
||||||
|
@elseif($tipoSlug === 'opcion_multiple')
|
||||||
|
<div>
|
||||||
|
@foreach($pregunta->opciones as $opcion)
|
||||||
|
<div class="form-check mb-2">
|
||||||
|
<input type="radio"
|
||||||
|
name="{{ $fieldName }}"
|
||||||
|
id="{{ $fieldId }}_{{ $opcion->id }}"
|
||||||
|
value="{{ $opcion->id }}"
|
||||||
|
class="form-check-input"
|
||||||
|
{{ $pregunta->requerida ? 'required' : '' }}>
|
||||||
|
<label class="form-check-label" for="{{ $fieldId }}_{{ $opcion->id }}">
|
||||||
|
{{ $opcion->texto }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@elseif($tipoSlug === 'si_no')
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="form-check mr-4">
|
||||||
|
<input type="radio"
|
||||||
|
name="{{ $fieldName }}"
|
||||||
|
id="{{ $fieldId }}_si"
|
||||||
|
value="si"
|
||||||
|
class="form-check-input"
|
||||||
|
{{ $pregunta->requerida ? 'required' : '' }}>
|
||||||
|
<label class="form-check-label font-weight-bold text-success" for="{{ $fieldId }}_si">
|
||||||
|
Sí
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input type="radio"
|
||||||
|
name="{{ $fieldName }}"
|
||||||
|
id="{{ $fieldId }}_no"
|
||||||
|
value="no"
|
||||||
|
class="form-check-input"
|
||||||
|
{{ $pregunta->requerida ? 'required' : '' }}>
|
||||||
|
<label class="form-check-label font-weight-bold text-danger" for="{{ $fieldId }}_no">
|
||||||
|
No
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@elseif($tipoSlug === 'escala_5')
|
||||||
|
<div class="d-flex flex-wrap" style="gap:.5rem;">
|
||||||
|
@for($k = 1; $k <= 5; $k++)
|
||||||
|
<div class="form-check form-check-inline mr-0">
|
||||||
|
<input type="radio"
|
||||||
|
name="{{ $fieldName }}"
|
||||||
|
id="{{ $fieldId }}_{{ $k }}"
|
||||||
|
value="{{ $k }}"
|
||||||
|
class="form-check-input d-none escala-input"
|
||||||
|
{{ $pregunta->requerida ? 'required' : '' }}>
|
||||||
|
<label class="btn btn-outline-primary px-3 py-2"
|
||||||
|
for="{{ $fieldId }}_{{ $k }}">{{ $k }}</label>
|
||||||
|
</div>
|
||||||
|
@endfor
|
||||||
|
<small class="text-muted align-self-center ml-2">1 = Muy malo 5 = Excelente</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@elseif($tipoSlug === 'escala_10')
|
||||||
|
<div class="d-flex flex-wrap" style="gap:.5rem;">
|
||||||
|
@for($k = 1; $k <= 10; $k++)
|
||||||
|
<div class="form-check form-check-inline mr-0">
|
||||||
|
<input type="radio"
|
||||||
|
name="{{ $fieldName }}"
|
||||||
|
id="{{ $fieldId }}_{{ $k }}"
|
||||||
|
value="{{ $k }}"
|
||||||
|
class="form-check-input d-none escala-input"
|
||||||
|
{{ $pregunta->requerida ? 'required' : '' }}>
|
||||||
|
<label class="btn btn-outline-primary px-3 py-2"
|
||||||
|
for="{{ $fieldId }}_{{ $k }}">{{ $k }}</label>
|
||||||
|
</div>
|
||||||
|
@endfor
|
||||||
|
<small class="text-muted align-self-center ml-2">1 = Muy malo 10 = Excelente</small>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<div class="card-header py-3">
|
<div class="card-header py-3">
|
||||||
<div class="d-sm-flex align-items-center justify-content-between">
|
<div class="d-sm-flex align-items-center justify-content-between">
|
||||||
<h6 class="m-0 font-weight-bold text-primary">
|
<h6 class="m-0 font-weight-bold text-primary">
|
||||||
<i class="fas fa-clipboard-list mr-1"></i> Evaluaciones
|
<i class="fa-duotone fa-regular fa-clipboard-list"></i> Evaluaciones
|
||||||
</h6>
|
</h6>
|
||||||
<a href="{{ route('evaluacion.create') }}" class="btn btn-sm btn-primary shadow-sm">
|
<a href="{{ route('evaluacion.create') }}" class="btn btn-sm btn-primary shadow-sm">
|
||||||
<i class="fas fa-plus fa-sm text-white-50"></i> Nueva evaluación
|
<i class="fas fa-plus fa-sm text-white-50"></i> Nueva evaluación
|
||||||
@@ -25,8 +25,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="table-responsive">
|
<table id="tcont" class="table table-striped table-bordered nowrap table-hover" style="width:100%;">
|
||||||
<table class="table table-bordered table-hover">
|
|
||||||
<thead class="thead-light">
|
<thead class="thead-light">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Título</th>
|
<th>Título</th>
|
||||||
@@ -52,11 +51,11 @@
|
|||||||
<td>
|
<td>
|
||||||
@php $slug = $evaluacion->tipo?->slug; @endphp
|
@php $slug = $evaluacion->tipo?->slug; @endphp
|
||||||
@if($slug === 'docente')
|
@if($slug === 'docente')
|
||||||
<span class="badge badge-info"><i class="fas fa-chalkboard-teacher mr-1"></i>Docentes</span>
|
<span class="badge badge-info"><i class="fa-duotone fa-solid fa-chalkboard-user"></i> Docentes</span>
|
||||||
@elseif($slug === 'alumno')
|
@elseif($slug === 'alumno')
|
||||||
<span class="badge badge-success"><i class="fas fa-user-graduate mr-1"></i>Alumnos</span>
|
<span class="badge badge-success"><i class="fa-duotone fa-solid fa-user-graduate"></i> Alumnos</span>
|
||||||
@elseif($slug === 'administrativo')
|
@elseif($slug === 'administrativo')
|
||||||
<span class="badge badge-warning text-dark"><i class="fas fa-briefcase mr-1"></i>Administrativos</span>
|
<span class="badge badge-warning text-dark"><i class="fa-duotone fa-regular fa-briefcase"></i> Administrativos</span>
|
||||||
@else
|
@else
|
||||||
<span class="badge badge-light">—</span>
|
<span class="badge badge-light">—</span>
|
||||||
@endif
|
@endif
|
||||||
@@ -86,34 +85,32 @@
|
|||||||
<td class="text-center text-nowrap">
|
<td class="text-center text-nowrap">
|
||||||
<a href="{{ route('evaluacion.show', $evaluacion) }}"
|
<a href="{{ route('evaluacion.show', $evaluacion) }}"
|
||||||
class="btn btn-sm btn-info" title="Ver detalle">
|
class="btn btn-sm btn-info" title="Ver detalle">
|
||||||
<i class="fas fa-eye"></i>
|
<i class="fa-duotone fa-solid fa-eye"></i>
|
||||||
</a>
|
</a>
|
||||||
<a href="{{ route('evaluacion.edit', $evaluacion) }}"
|
<a href="{{ route('evaluacion.edit', $evaluacion) }}"
|
||||||
class="btn btn-sm btn-primary" title="Editar">
|
class="btn btn-sm btn-primary" title="Editar">
|
||||||
<i class="fas fa-edit"></i>
|
<i class="fa-duotone fa-solid fa-pen-to-square"></i>
|
||||||
</a>
|
</a>
|
||||||
<form action="{{ route('evaluacion.destroy', $evaluacion) }}"
|
<form action="{{ route('evaluacion.destroy', $evaluacion) }}"
|
||||||
method="POST" class="d-inline"
|
method="POST" class="d-inline delete-form">
|
||||||
onsubmit="return confirm('¿Eliminar esta evaluación y todas sus preguntas?')">
|
|
||||||
@csrf @method('DELETE')
|
@csrf @method('DELETE')
|
||||||
<button class="btn btn-sm btn-danger" title="Eliminar">
|
<button class="btn btn-sm btn-danger" title="Eliminar">
|
||||||
<i class="fas fa-trash"></i>
|
<i class="fa-duotone fa-solid fa-trash-can"></i>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@empty
|
@empty
|
||||||
<tr>
|
|
||||||
<td colspan="7" class="text-center text-muted py-4">
|
|
||||||
<i class="fas fa-clipboard fa-2x mb-2 d-block"></i>
|
|
||||||
No hay evaluaciones registradas.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
@endforelse
|
@endforelse
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
|
|
||||||
|
@section('scripts')
|
||||||
|
@include('layouts._partials.tablaScript')
|
||||||
|
@include('layouts._partials.delete')
|
||||||
|
@endsection
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
@extends('layouts.landing')
|
||||||
|
|
||||||
|
@section('title', 'Mis Evaluaciones')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container-fluid">
|
||||||
|
|
||||||
|
@if(session('info'))
|
||||||
|
<div class="alert alert-success alert-dismissible fade show">
|
||||||
|
<i class="fas fa-check-circle mr-1"></i> {{ session('info') }}
|
||||||
|
<button type="button" class="close" data-dismiss="alert"><span>×</span></button>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">
|
||||||
|
<i class="fas fa-clipboard-check mr-1"></i> Mis Evaluaciones
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
@if(!$docente && !$alumno)
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
Tu cuenta no está vinculada a un docente o alumno. Contacta al administrador.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@elseif($evaluaciones->isEmpty())
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<i class="fas fa-info-circle mr-1"></i>
|
||||||
|
No tienes evaluaciones activas en este momento.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@else
|
||||||
|
<div class="row">
|
||||||
|
@foreach($evaluaciones as $evaluacion)
|
||||||
|
@php $yaRespondida = in_array($evaluacion->id, $respondidas); @endphp
|
||||||
|
<div class="col-md-6 col-lg-4 mb-4">
|
||||||
|
<div class="card h-100 shadow-sm
|
||||||
|
{{ $yaRespondida ? 'border-success' : 'border-primary' }}">
|
||||||
|
<div class="card-header py-2 d-flex align-items-center justify-content-between
|
||||||
|
{{ $yaRespondida ? 'bg-success text-white' : 'bg-primary text-white' }}">
|
||||||
|
<small class="font-weight-bold">
|
||||||
|
@if($yaRespondida)
|
||||||
|
<i class="fas fa-check-circle mr-1"></i> Respondida
|
||||||
|
@else
|
||||||
|
<i class="fas fa-clipboard-list mr-1"></i> Pendiente
|
||||||
|
@endif
|
||||||
|
</small>
|
||||||
|
<span class="badge badge-light text-dark">
|
||||||
|
{{ $evaluacion->preguntas_count }} preguntas
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body d-flex flex-column">
|
||||||
|
<h6 class="font-weight-bold mb-1">{{ $evaluacion->titulo }}</h6>
|
||||||
|
|
||||||
|
@if($evaluacion->descripcion)
|
||||||
|
<p class="text-muted small mb-2">
|
||||||
|
{{ Str::limit($evaluacion->descripcion, 120) }}
|
||||||
|
</p>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="mt-auto">
|
||||||
|
@if($evaluacion->fecha_inicio || $evaluacion->fecha_fin)
|
||||||
|
<small class="text-muted d-block mb-2">
|
||||||
|
<i class="fas fa-calendar-alt mr-1"></i>
|
||||||
|
{{ $evaluacion->fecha_inicio
|
||||||
|
? \Carbon\Carbon::parse($evaluacion->fecha_inicio)->format('d/m/Y')
|
||||||
|
: 'Sin inicio' }}
|
||||||
|
—
|
||||||
|
{{ $evaluacion->fecha_fin
|
||||||
|
? \Carbon\Carbon::parse($evaluacion->fecha_fin)->format('d/m/Y')
|
||||||
|
: 'Sin límite' }}
|
||||||
|
</small>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if($yaRespondida)
|
||||||
|
<button class="btn btn-success btn-sm btn-block" disabled>
|
||||||
|
<i class="fas fa-check mr-1"></i> Ya respondida
|
||||||
|
</button>
|
||||||
|
@else
|
||||||
|
<a href="{{ route('evaluacion.responder.show', $evaluacion) }}"
|
||||||
|
class="btn btn-primary btn-sm btn-block">
|
||||||
|
<i class="fas fa-pencil-alt mr-1"></i> Responder evaluación
|
||||||
|
</a>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
@extends('layouts.landing')
|
||||||
|
|
||||||
|
@section('title', $evaluacion->titulo)
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container-fluid">
|
||||||
|
|
||||||
|
<div class="d-sm-flex align-items-center justify-content-between mb-3">
|
||||||
|
<div>
|
||||||
|
<h5 class="m-0 font-weight-bold text-primary">
|
||||||
|
<i class="fas fa-clipboard-list mr-1"></i> {{ $evaluacion->titulo }}
|
||||||
|
</h5>
|
||||||
|
@if($evaluacion->descripcion)
|
||||||
|
<small class="text-muted">{{ $evaluacion->descripcion }}</small>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<a href="{{ route('evaluacion.mis_evaluaciones') }}" class="btn btn-sm btn-secondary mt-2 mt-sm-0">
|
||||||
|
<i class="fas fa-arrow-left mr-1"></i> Regresar
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if(session('error'))
|
||||||
|
<div class="alert alert-danger">{{ session('error') }}</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if($yaRespondida)
|
||||||
|
{{-- Ya respondida --}}
|
||||||
|
<div class="card shadow">
|
||||||
|
<div class="card-body text-center py-5">
|
||||||
|
<i class="fas fa-check-circle fa-4x text-success mb-3 d-block"></i>
|
||||||
|
<h5 class="font-weight-bold">Ya respondiste esta evaluación</h5>
|
||||||
|
<p class="text-muted">Tu participación ha sido registrada. ¡Gracias!</p>
|
||||||
|
<a href="{{ route('evaluacion.mis_evaluaciones') }}" class="btn btn-primary">
|
||||||
|
<i class="fas fa-arrow-left mr-1"></i> Volver a mis evaluaciones
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@else
|
||||||
|
{{-- Formulario --}}
|
||||||
|
<form action="{{ route('evaluacion.responder.store', $evaluacion) }}" method="POST"
|
||||||
|
id="form-evaluacion">
|
||||||
|
@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 --}}
|
||||||
|
<div class="alert alert-info d-flex align-items-center mb-4">
|
||||||
|
<i class="fas fa-info-circle fa-lg mr-3"></i>
|
||||||
|
<div>
|
||||||
|
<strong>{{ $totalPreguntas }} pregunta(s)</strong> en esta evaluación.
|
||||||
|
@if($evaluacion->anonima)
|
||||||
|
<span class="badge badge-secondary">
|
||||||
|
<i class="fas fa-user-secret mr-1"></i>Anónima
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
@if($evaluacion->fecha_fin)
|
||||||
|
Cierra el
|
||||||
|
<strong>{{ \Carbon\Carbon::parse($evaluacion->fecha_fin)->format('d/m/Y') }}</strong>.
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
<div class="card shadow-sm mb-4 border-left-info">
|
||||||
|
<div class="card-header py-2 bg-light d-flex align-items-center">
|
||||||
|
{{-- Foto del docente --}}
|
||||||
|
<img src="{{ $fotoUrl }}"
|
||||||
|
alt="{{ $nombreDocente }}"
|
||||||
|
class="rounded-circle mr-3"
|
||||||
|
style="width:48px; height:48px; object-fit:cover; border:2px solid #36b9cc;">
|
||||||
|
<div>
|
||||||
|
<div class="font-weight-bold">{{ $nombreDocente }}</div>
|
||||||
|
<small class="text-muted">
|
||||||
|
<i class="fas fa-chalkboard-teacher mr-1 text-info"></i>
|
||||||
|
Pregunta {{ $numPregunta++ }}
|
||||||
|
@if($pregunta->requerida)
|
||||||
|
<span class="text-danger">*</span>
|
||||||
|
@endif
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="font-weight-bold mb-3">{{ $pregunta->texto }}</p>
|
||||||
|
|
||||||
|
@include('evaluacion._input_pregunta', [
|
||||||
|
'pregunta' => $pregunta,
|
||||||
|
'tipoSlug' => $tipoSlug,
|
||||||
|
'fieldName' => "resp[{$pregunta->id}][{$docente->id}]",
|
||||||
|
'fieldId' => "resp_{$pregunta->id}_{$docente->id}",
|
||||||
|
])
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
@else
|
||||||
|
{{-- ═══ PREGUNTA NORMAL ═══ --}}
|
||||||
|
<div class="card shadow-sm mb-4 border-left-primary">
|
||||||
|
<div class="card-body">
|
||||||
|
<small class="text-muted font-weight-bold d-block mb-1">
|
||||||
|
Pregunta {{ $numPregunta++ }}
|
||||||
|
@if($pregunta->requerida)
|
||||||
|
<span class="text-danger">*</span>
|
||||||
|
@endif
|
||||||
|
</small>
|
||||||
|
<p class="font-weight-bold mb-3">{{ $pregunta->texto }}</p>
|
||||||
|
|
||||||
|
@include('evaluacion._input_pregunta', [
|
||||||
|
'pregunta' => $pregunta,
|
||||||
|
'tipoSlug' => $tipoSlug,
|
||||||
|
'fieldName' => "resp[{$pregunta->id}]",
|
||||||
|
'fieldId' => "resp_{$pregunta->id}",
|
||||||
|
])
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
<div class="card shadow-sm mb-4">
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<button type="submit" class="btn btn-success btn-lg px-5"
|
||||||
|
onclick="return confirm('¿Estás seguro de enviar la evaluación? No podrás cambiar tus respuestas.')">
|
||||||
|
<i class="fas fa-paper-plane mr-2"></i> Enviar evaluación
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -150,12 +150,19 @@
|
|||||||
<div class="card-body py-2 px-3">
|
<div class="card-body py-2 px-3">
|
||||||
<div class="d-flex justify-content-between align-items-start">
|
<div class="d-flex justify-content-between align-items-start">
|
||||||
<div class="flex-grow-1">
|
<div class="flex-grow-1">
|
||||||
<small class="text-muted font-weight-bold d-block mb-1">
|
<div class="d-flex align-items-center mb-1 flex-wrap" style="gap:.3rem;">
|
||||||
|
<small class="text-muted font-weight-bold">
|
||||||
Pregunta {{ $i + 1 }}
|
Pregunta {{ $i + 1 }}
|
||||||
@if($pregunta->requerida)
|
@if($pregunta->requerida)
|
||||||
<span class="text-danger">*</span>
|
<span class="text-danger">*</span>
|
||||||
@endif
|
@endif
|
||||||
</small>
|
</small>
|
||||||
|
@if($pregunta->es_docente)
|
||||||
|
<span class="badge badge-info">
|
||||||
|
<i class="fas fa-chalkboard-teacher mr-1"></i>Por docente
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
<p class="mb-1 font-weight-bold">{{ $pregunta->texto }}</p>
|
<p class="mb-1 font-weight-bold">{{ $pregunta->texto }}</p>
|
||||||
<small class="text-muted">{{ $pregunta->tipo?->nombre }}</small>
|
<small class="text-muted">{{ $pregunta->tipo?->nombre }}</small>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -202,6 +202,12 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ route('evaluacion.mis_evaluaciones') }}">
|
||||||
|
<i class="fa-duotone fa-regular fa-clipboard-list" style="--fa-primary-color: rgb(255, 255, 255); --fa-secondary-color: rgb(255, 255, 255);"></i>
|
||||||
|
<span>Encuestas</span></a>
|
||||||
|
</li>
|
||||||
|
|
||||||
@endrole
|
@endrole
|
||||||
|
|
||||||
@hasrole(['Docente'])
|
@hasrole(['Docente'])
|
||||||
@@ -211,6 +217,12 @@
|
|||||||
<i class="fa-duotone fa-regular fa-user-group" style="--fa-primary-color: rgb(255, 255, 255); --fa-secondary-color: rgb(255, 255, 255);"></i>
|
<i class="fa-duotone fa-regular fa-user-group" style="--fa-primary-color: rgb(255, 255, 255); --fa-secondary-color: rgb(255, 255, 255);"></i>
|
||||||
<span>Mis grupos</span></a>
|
<span>Mis grupos</span></a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ route('evaluacion.mis_evaluaciones') }}">
|
||||||
|
<i class="fa-duotone fa-regular fa-clipboard-list " style="--fa-primary-color: rgb(255, 255, 255); --fa-secondary-color: rgb(255, 255, 255);"></i>
|
||||||
|
<span>Encuestas</span></a>
|
||||||
|
</li>
|
||||||
@endrole
|
@endrole
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -94,6 +94,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-body py-3">
|
<div class="card-body py-3">
|
||||||
|
@php
|
||||||
|
$tipoAlumnoId = (string)(collect($tipos)->firstWhere('slug', 'alumno')['id'] ?? 0);
|
||||||
|
$esEvalAlumno = $evaluacionTipoId === $tipoAlumnoId;
|
||||||
|
@endphp
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{{-- Texto de la pregunta --}}
|
{{-- Texto de la pregunta --}}
|
||||||
<div class="col-md-7">
|
<div class="col-md-7">
|
||||||
@@ -110,7 +114,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- Tipo de respuesta --}}
|
{{-- Tipo de respuesta --}}
|
||||||
<div class="col-md-4">
|
<div class="col-md-{{ $esEvalAlumno ? '3' : '4' }}">
|
||||||
<div class="form-group mb-2">
|
<div class="form-group mb-2">
|
||||||
<label class="small font-weight-bold">Tipo de respuesta</label>
|
<label class="small font-weight-bold">Tipo de respuesta</label>
|
||||||
<select wire:model.live="preguntas.{{ $i }}.pregunta_tipo_id"
|
<select wire:model.live="preguntas.{{ $i }}.pregunta_tipo_id"
|
||||||
@@ -135,7 +139,31 @@
|
|||||||
<label class="form-check-label small" for="req-{{ $i }}">*</label>
|
<label class="form-check-label small" for="req-{{ $i }}">*</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{-- Pregunta de docente (solo en evaluaciones de tipo Alumno) --}}
|
||||||
|
@if($esEvalAlumno)
|
||||||
|
<div class="col-md-1 d-flex align-items-end pb-3">
|
||||||
|
<div class="form-check"
|
||||||
|
title="Marcar si esta pregunta evalúa a un docente específico">
|
||||||
|
<input type="checkbox"
|
||||||
|
wire:model="preguntas.{{ $i }}.es_docente"
|
||||||
|
class="form-check-input"
|
||||||
|
id="esd-{{ $i }}">
|
||||||
|
<label class="form-check-label small text-primary" for="esd-{{ $i }}">
|
||||||
|
<i class="fas fa-chalkboard-teacher"></i>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- Indicador visual si es pregunta de docente --}}
|
||||||
|
@if($esEvalAlumno && !empty($pregunta['es_docente']))
|
||||||
|
<div class="alert alert-info py-1 px-2 mb-2 d-flex align-items-center" style="font-size:.82rem;">
|
||||||
|
<i class="fas fa-chalkboard-teacher mr-2"></i>
|
||||||
|
Esta pregunta se repetirá por cada docente que imparte clase al alumno.
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
{{-- Vista previa y opciones según tipo --}}
|
{{-- Vista previa y opciones según tipo --}}
|
||||||
@php
|
@php
|
||||||
|
|||||||
@@ -143,7 +143,7 @@
|
|||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
@empty
|
@empty
|
||||||
<tr><td colspan="5">Sin materias en este plan.</td></tr>
|
<tr>Sin materias en este plan.</tr>
|
||||||
@endforelse
|
@endforelse
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -0,0 +1,213 @@
|
|||||||
|
@extends('layouts.landing')
|
||||||
|
|
||||||
|
@section('title', 'Acta de Calificaciones')
|
||||||
|
|
||||||
|
@section('head')
|
||||||
|
<style>
|
||||||
|
@media print {
|
||||||
|
.no-print { display: none !important; }
|
||||||
|
body { font-size: 12px; }
|
||||||
|
.card { border: none !important; box-shadow: none !important; }
|
||||||
|
.card-header { background: none !important; border-bottom: 2px solid #333 !important; }
|
||||||
|
table { font-size: 11px; }
|
||||||
|
}
|
||||||
|
.calificacion-cell { min-width: 70px; text-align: center; }
|
||||||
|
.promedio-cell { min-width: 80px; text-align: center; font-weight: bold; }
|
||||||
|
th.rotate { height: 130px; white-space: nowrap; vertical-align: bottom; }
|
||||||
|
th.rotate > div { transform: rotate(-60deg); transform-origin: bottom left; width: 30px; padding-bottom: 5px; }
|
||||||
|
</style>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3">
|
||||||
|
<div class="d-sm-flex align-items-center justify-content-between">
|
||||||
|
<div>
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">
|
||||||
|
Acta de Calificaciones
|
||||||
|
</h6>
|
||||||
|
<small class="text-muted">
|
||||||
|
{{ $material->name }} · Grupo: {{ $grupo->clave }}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div class="no-print">
|
||||||
|
<a href="{{ route('grupo.material.tareas', [$grupo, $material]) }}"
|
||||||
|
class="btn btn-sm btn-secondary shadow-sm mr-2">
|
||||||
|
<i class="fas fa-arrow-left fa-sm"></i> Regresar
|
||||||
|
</a>
|
||||||
|
<button onclick="window.print()" class="btn btn-sm btn-primary shadow-sm">
|
||||||
|
<i class="fas fa-print fa-sm"></i> Imprimir
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
{{-- Resumen --}}
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-md-3 col-sm-6 mb-3">
|
||||||
|
<div class="card border-left-primary shadow-sm h-100 py-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="text-xs font-weight-bold text-primary text-uppercase mb-1">
|
||||||
|
Total de Tareas
|
||||||
|
</div>
|
||||||
|
<div class="h5 mb-0 font-weight-bold text-gray-800">{{ $tareas->count() }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 col-sm-6 mb-3">
|
||||||
|
<div class="card border-left-success shadow-sm h-100 py-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="text-xs font-weight-bold text-success text-uppercase mb-1">
|
||||||
|
Total de Alumnos
|
||||||
|
</div>
|
||||||
|
<div class="h5 mb-0 font-weight-bold text-gray-800">{{ $alumnos->count() }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 col-sm-6 mb-3">
|
||||||
|
<div class="card border-left-info shadow-sm h-100 py-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="text-xs font-weight-bold text-info text-uppercase mb-1">
|
||||||
|
Materia
|
||||||
|
</div>
|
||||||
|
<div class="h5 mb-0 font-weight-bold text-gray-800">{{ $material->name }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 col-sm-6 mb-3">
|
||||||
|
<div class="card border-left-warning shadow-sm h-100 py-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="text-xs font-weight-bold text-warning text-uppercase mb-1">
|
||||||
|
Generado
|
||||||
|
</div>
|
||||||
|
<div class="h5 mb-0 font-weight-bold text-gray-800">
|
||||||
|
{{ \Carbon\Carbon::now()->format('d/m/Y') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if ($tareas->isEmpty())
|
||||||
|
<div class="alert alert-info">No hay tareas registradas para esta materia.</div>
|
||||||
|
@elseif ($alumnos->isEmpty())
|
||||||
|
<div class="alert alert-info">No hay alumnos inscritos en este grupo.</div>
|
||||||
|
@else
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered table-sm" style="width:100%">
|
||||||
|
<thead class="thead-dark">
|
||||||
|
<tr>
|
||||||
|
<th class="align-middle" style="min-width:40px">#</th>
|
||||||
|
<th class="align-middle" style="min-width:180px">Alumno</th>
|
||||||
|
@foreach ($tareas as $tarea)
|
||||||
|
<th class="rotate calificacion-cell">
|
||||||
|
<div title="{{ $tarea->titulo }}">
|
||||||
|
{{ Str::limit($tarea->titulo, 25) }}
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
@endforeach
|
||||||
|
<th class="align-middle promedio-cell text-center"
|
||||||
|
style="background:#1a3a5c; color:#fff;">
|
||||||
|
Promedio Final
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@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
|
||||||
|
<tr>
|
||||||
|
<td class="text-center align-middle">{{ $fila++ }}</td>
|
||||||
|
<td class="align-middle">{{ $nombre }}</td>
|
||||||
|
@foreach ($tareas as $tarea)
|
||||||
|
@php $data = $calificaciones[$tarea->id]; @endphp
|
||||||
|
<td class="calificacion-cell align-middle">
|
||||||
|
@if ($data['valor'] !== null)
|
||||||
|
<span class="{{ $data['valor'] >= 6 ? 'text-success' : 'text-danger' }} font-weight-bold">
|
||||||
|
{{ number_format($data['valor'], 1) }}
|
||||||
|
</span>
|
||||||
|
@elseif ($data['status'] === 'pendiente')
|
||||||
|
<span class="text-warning" title="Pendiente de calificar">
|
||||||
|
<i class="fas fa-clock"></i>
|
||||||
|
</span>
|
||||||
|
@elseif ($data['status'] === 'sin_subir')
|
||||||
|
<span class="text-muted" title="Sin entregar">—</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
@endforeach
|
||||||
|
<td class="promedio-cell align-middle" style="background:#f8f9fc;">
|
||||||
|
@if ($promedio !== null)
|
||||||
|
<span class="badge badge-pill {{ $promedio >= 6 ? 'badge-success' : 'badge-danger' }}"
|
||||||
|
style="font-size:.95rem; padding:.4em .7em;">
|
||||||
|
{{ number_format($promedio, 1) }}
|
||||||
|
</span>
|
||||||
|
@else
|
||||||
|
<span class="text-muted">S/C</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-3 no-print">
|
||||||
|
<small class="text-muted">
|
||||||
|
<i class="fas fa-info-circle"></i>
|
||||||
|
El promedio final se calcula únicamente sobre las tareas ya calificadas.
|
||||||
|
<strong>—</strong> = sin entregar |
|
||||||
|
<i class="fas fa-clock text-warning"></i> = entregada, pendiente de calificación |
|
||||||
|
<strong>S/C</strong> = sin calificaciones registradas.
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- Firma para impresión --}}
|
||||||
|
<div class="row mt-5 d-none d-print-flex">
|
||||||
|
<div class="col-4 text-center">
|
||||||
|
<div style="border-top:1px solid #333; padding-top:8px; margin-top:50px;">
|
||||||
|
Firma del Docente
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-4 text-center">
|
||||||
|
<div style="border-top:1px solid #333; padding-top:8px; margin-top:50px;">
|
||||||
|
Firma del Director
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-4 text-center">
|
||||||
|
<div style="border-top:1px solid #333; padding-top:8px; margin-top:50px;">
|
||||||
|
Sello de la Institución
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -24,6 +24,10 @@
|
|||||||
<a href="{{ route('grupo.administrar', $grupo) }}" class="btn btn-sm btn-secondary shadow-sm mr-2">
|
<a href="{{ route('grupo.administrar', $grupo) }}" class="btn btn-sm btn-secondary shadow-sm mr-2">
|
||||||
<i class="fas fa-arrow-left fa-sm"></i> Regresar
|
<i class="fas fa-arrow-left fa-sm"></i> Regresar
|
||||||
</a>
|
</a>
|
||||||
|
<a href="{{ route('grupo.material.tareas.acta', [$grupo, $material]) }}"
|
||||||
|
class="btn btn-sm btn-success shadow-sm mr-2">
|
||||||
|
<i class="fas fa-file-alt fa-sm text-white-50"></i> Generar Acta
|
||||||
|
</a>
|
||||||
<a href="{{ route('grupo.material.tareas.create', [$grupo, $material]) }}"
|
<a href="{{ route('grupo.material.tareas.create', [$grupo, $material]) }}"
|
||||||
class="btn btn-sm btn-primary shadow-sm">
|
class="btn btn-sm btn-primary shadow-sm">
|
||||||
<i class="fas fa-plus fa-sm text-white-50"></i> Nueva Tarea
|
<i class="fas fa-plus fa-sm text-white-50"></i> Nueva Tarea
|
||||||
|
|||||||
@@ -1,147 +1,315 @@
|
|||||||
@extends('layouts.landing')
|
@extends('layouts.landing')
|
||||||
|
|
||||||
@section('head')
|
|
||||||
@include('layouts._partials.tablaStyle')
|
|
||||||
@endsection
|
|
||||||
|
|
||||||
@section('title', 'Mis Tareas')
|
@section('title', 'Mis Tareas')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="card shadow mb-4">
|
|
||||||
<div class="card-header py-3">
|
|
||||||
<h6 class="m-0 font-weight-bold text-primary">Mis Tareas</h6>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card-body">
|
|
||||||
|
|
||||||
|
{{-- Alertas de sesión --}}
|
||||||
@if (session('info'))
|
@if (session('info'))
|
||||||
<div class="alert alert-success"><strong>{{ session('info') }}</strong></div>
|
<div class="alert alert-success alert-dismissible fade show">
|
||||||
|
<strong>{{ session('info') }}</strong>
|
||||||
|
<button type="button" class="close" data-dismiss="alert"><span>×</span></button>
|
||||||
|
</div>
|
||||||
@endif
|
@endif
|
||||||
@if (session('error'))
|
@if (session('error'))
|
||||||
<div class="alert alert-danger"><strong>{{ session('error') }}</strong></div>
|
<div class="alert alert-danger alert-dismissible fade show">
|
||||||
|
<strong>{{ session('error') }}</strong>
|
||||||
|
<button type="button" class="close" data-dismiss="alert"><span>×</span></button>
|
||||||
|
</div>
|
||||||
@endif
|
@endif
|
||||||
@if ($errors->has('entrega'))
|
@if ($errors->has('entrega'))
|
||||||
<div class="alert alert-warning"><strong>{{ $errors->first('entrega') }}</strong></div>
|
<div class="alert alert-warning alert-dismissible fade show">
|
||||||
|
<strong>{{ $errors->first('entrega') }}</strong>
|
||||||
|
<button type="button" class="close" data-dismiss="alert"><span>×</span></button>
|
||||||
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@if (!isset($tareas) || $tareas->isEmpty())
|
{{-- Sin ciclo activo --}}
|
||||||
<div class="alert alert-info">No tienes tareas asignadas en tus grupos.</div>
|
@if (!$cicloActual)
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<i class="fas fa-info-circle"></i>
|
||||||
|
No hay un ciclo escolar activo en este momento. Contacta a tu coordinador.
|
||||||
|
</div>
|
||||||
@else
|
@else
|
||||||
|
|
||||||
|
{{-- ══════════════════════════════════════════════════
|
||||||
|
SECCIÓN 1: TAREAS POR ENTREGAR (ciclo actual)
|
||||||
|
══════════════════════════════════════════════════ --}}
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3 d-flex align-items-center justify-content-between">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">
|
||||||
|
<i class="fas fa-tasks mr-1"></i> Tareas por Entregar
|
||||||
|
</h6>
|
||||||
|
<span class="badge badge-primary badge-pill">{{ $cicloActual->name }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
@if ($tareasPendientes->isEmpty())
|
||||||
|
<div class="alert alert-success mb-0">
|
||||||
|
<i class="fas fa-check-circle"></i> ¡Estás al corriente! No tienes tareas pendientes de entrega.
|
||||||
|
</div>
|
||||||
|
@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
|
||||||
|
|
||||||
|
<div class="card border mb-3 shadow-sm">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center"
|
||||||
|
data-toggle="collapse"
|
||||||
|
data-target="#pend-mat-{{ $materialId }}"
|
||||||
|
aria-expanded="true"
|
||||||
|
style="cursor:pointer; user-select:none;">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<i class="fas fa-book-open mr-2 text-primary"></i>
|
||||||
|
<div>
|
||||||
|
<h6 class="mb-0 font-weight-bold">{{ $material->name }}</h6>
|
||||||
|
<small class="text-muted">Grupo: {{ $grupo->clave }}</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
@if ($vencidas > 0)
|
||||||
|
<span class="badge badge-danger badge-pill mr-1"
|
||||||
|
title="{{ $vencidas }} tarea(s) vencida(s)">
|
||||||
|
{{ $vencidas }} vencida(s)
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
<span class="badge badge-warning badge-pill mr-2">
|
||||||
|
{{ $grupoTareas->count() }} por entregar
|
||||||
|
</span>
|
||||||
|
<i class="fas fa-chevron-down text-muted accordion-icon"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="pend-mat-{{ $materialId }}" class="collapse show">
|
||||||
|
<div class="card-body p-0">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table id="tcont" class="table table-striped table-bordered table-hover" style="width:100%">
|
<table class="table table-sm table-hover mb-0">
|
||||||
<thead>
|
<thead class="thead-light">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Tarea</th>
|
<th>Tarea</th>
|
||||||
<th>Materia</th>
|
<th style="width:130px">Fecha límite</th>
|
||||||
<th>Grupo</th>
|
<th>Entregar</th>
|
||||||
<th>Fecha límite</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>Calificación</th>
|
|
||||||
<th>Tu entrega / Acción</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach ($tareas as $tarea)
|
@foreach ($grupoTareas as $tarea)
|
||||||
@php $entrega = $entregasMap[$tarea->id] ?? null; $status = $entrega?->status ?? 'sin_subir'; @endphp
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td class="align-middle">
|
||||||
<strong>{{ $tarea->titulo }}</strong>
|
<strong>{{ $tarea->titulo }}</strong>
|
||||||
@if ($tarea->descripcion)
|
@if ($tarea->descripcion)
|
||||||
<p class="text-muted small mb-0">{{ Str::limit($tarea->descripcion, 80) }}</p>
|
<p class="text-muted small mb-0">
|
||||||
|
{{ Str::limit($tarea->descripcion, 100) }}
|
||||||
|
</p>
|
||||||
@endif
|
@endif
|
||||||
</td>
|
</td>
|
||||||
<td>{{ $tarea->material->name }}</td>
|
<td class="align-middle">
|
||||||
<td>{{ $tarea->grupo->clave }}</td>
|
|
||||||
<td>
|
|
||||||
@if ($tarea->fecha_entrega)
|
@if ($tarea->fecha_entrega)
|
||||||
{{ \Carbon\Carbon::parse($tarea->fecha_entrega)->format('d/m/Y') }}
|
{{ \Carbon\Carbon::parse($tarea->fecha_entrega)->format('d/m/Y') }}
|
||||||
@if (\Carbon\Carbon::parse($tarea->fecha_entrega)->isPast())
|
@if (\Carbon\Carbon::parse($tarea->fecha_entrega)->isPast())
|
||||||
<span class="badge badge-danger">Vencida</span>
|
<br><span class="badge badge-danger">Vencida</span>
|
||||||
@else
|
@else
|
||||||
<span class="badge badge-success">Activa</span>
|
<br><span class="badge badge-success">Activa</span>
|
||||||
@endif
|
@endif
|
||||||
@else
|
@else
|
||||||
<span class="text-muted">Sin fecha</span>
|
<span class="text-muted">Sin fecha</span>
|
||||||
@endif
|
@endif
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@if ($status === 'sin_subir')
|
<form action="{{ route('tarea.entregar', $tarea) }}"
|
||||||
<span class="badge badge-secondary">Sin subir</span>
|
method="POST"
|
||||||
@elseif ($status === 'pendiente')
|
|
||||||
<span class="badge badge-warning">Pendiente</span>
|
|
||||||
@else
|
|
||||||
<span class="badge badge-success">Calificada</span>
|
|
||||||
@endif
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@if ($status === 'calificada' && $entrega)
|
|
||||||
<strong class="text-success">{{ number_format($entrega->calificacion, 1) }}</strong>
|
|
||||||
@if ($entrega->comentario)
|
|
||||||
<br><small class="text-muted">{{ $entrega->comentario }}</small>
|
|
||||||
@endif
|
|
||||||
@else
|
|
||||||
<span class="text-muted">—</span>
|
|
||||||
@endif
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@if ($status === 'sin_subir')
|
|
||||||
{{-- Formulario de entrega: texto y/o archivo --}}
|
|
||||||
<form action="{{ route('tarea.entregar', $tarea) }}" method="POST"
|
|
||||||
enctype="multipart/form-data">
|
enctype="multipart/form-data">
|
||||||
@csrf
|
@csrf
|
||||||
<div class="form-group mb-1">
|
<div class="form-group mb-1">
|
||||||
<textarea name="texto" class="form-control form-control-sm"
|
<textarea name="texto"
|
||||||
rows="3" placeholder="Escribe tu respuesta aquí (opcional)..."></textarea>
|
class="form-control form-control-sm"
|
||||||
|
rows="3"
|
||||||
|
placeholder="Escribe tu respuesta aquí (opcional)..."></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mb-1">
|
<div class="form-group mb-1">
|
||||||
<div class="custom-file">
|
<div class="custom-file">
|
||||||
<input type="file" class="custom-file-input" id="archivo_{{ $tarea->id }}" name="archivo">
|
<input type="file"
|
||||||
<label class="custom-file-label small" for="archivo_{{ $tarea->id }}">
|
class="custom-file-input"
|
||||||
|
id="archivo_{{ $tarea->id }}"
|
||||||
|
name="archivo">
|
||||||
|
<label class="custom-file-label small"
|
||||||
|
for="archivo_{{ $tarea->id }}">
|
||||||
Adjuntar archivo (opcional)
|
Adjuntar archivo (opcional)
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary btn-sm btn-block mt-1">
|
<button type="submit"
|
||||||
|
class="btn btn-primary btn-sm btn-block mt-1">
|
||||||
<i class="fas fa-upload fa-sm"></i> Entregar
|
<i class="fas fa-upload fa-sm"></i> Entregar
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@elseif ($status === 'pendiente' || $status === 'calificada')
|
{{-- ══════════════════════════════════════════════════
|
||||||
{{-- Mostrar lo que entregó --}}
|
SECCIÓN 2: TAREAS YA ENTREGADAS
|
||||||
|
══════════════════════════════════════════════════ --}}
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3 d-flex align-items-center justify-content-between">
|
||||||
|
<h6 class="m-0 font-weight-bold text-success">
|
||||||
|
<i class="fas fa-check-double mr-1"></i> Tareas Entregadas
|
||||||
|
</h6>
|
||||||
|
<span class="badge badge-success badge-pill">{{ $tareasEntregadas->count() }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
@if ($tareasEntregadas->isEmpty())
|
||||||
|
<div class="alert alert-info mb-0">
|
||||||
|
Aún no has entregado ninguna tarea en este ciclo.
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
@php $entregadasAgrupadas = $tareasEntregadas->groupBy('material_id'); @endphp
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
<div class="card border mb-3 shadow-sm">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center"
|
||||||
|
data-toggle="collapse"
|
||||||
|
data-target="#entr-mat-{{ $materialId }}"
|
||||||
|
aria-expanded="false"
|
||||||
|
style="cursor:pointer; user-select:none;">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<i class="fas fa-book-open mr-2 text-success"></i>
|
||||||
|
<div>
|
||||||
|
<h6 class="mb-0 font-weight-bold">{{ $material->name }}</h6>
|
||||||
|
<small class="text-muted">Grupo: {{ $grupo->clave }}</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
@if ($pendientes > 0)
|
||||||
|
<span class="badge badge-warning badge-pill mr-1"
|
||||||
|
title="{{ $pendientes }} esperando calificación">
|
||||||
|
{{ $pendientes }} por calificar
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
@if ($calificadas > 0)
|
||||||
|
<span class="badge badge-success badge-pill mr-1">
|
||||||
|
{{ $calificadas }} calificada(s)
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
<span class="badge badge-secondary badge-pill mr-2">
|
||||||
|
{{ $grupoTareas->count() }} total
|
||||||
|
</span>
|
||||||
|
<i class="fas fa-chevron-down text-muted accordion-icon"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="entr-mat-{{ $materialId }}" class="collapse">
|
||||||
|
<div class="card-body p-0">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-sm table-hover mb-0">
|
||||||
|
<thead class="thead-light">
|
||||||
|
<tr>
|
||||||
|
<th>Tarea</th>
|
||||||
|
<th style="width:110px">Status</th>
|
||||||
|
<th style="width:90px">Calificación</th>
|
||||||
|
<th>Lo que entregaste</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach ($grupoTareas as $tarea)
|
||||||
|
@php
|
||||||
|
$entrega = $entregasMap[$tarea->id] ?? null;
|
||||||
|
$status = $entrega?->status ?? 'sin_subir';
|
||||||
|
@endphp
|
||||||
|
<tr>
|
||||||
|
<td class="align-middle">
|
||||||
|
<strong>{{ $tarea->titulo }}</strong>
|
||||||
|
@if ($tarea->descripcion)
|
||||||
|
<p class="text-muted small mb-0">
|
||||||
|
{{ Str::limit($tarea->descripcion, 80) }}
|
||||||
|
</p>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td class="align-middle">
|
||||||
|
@if ($status === 'pendiente')
|
||||||
|
<span class="badge badge-warning">
|
||||||
|
<i class="fas fa-clock"></i> Pendiente
|
||||||
|
</span>
|
||||||
|
@else
|
||||||
|
<span class="badge badge-success">
|
||||||
|
<i class="fas fa-check"></i> Calificada
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td class="align-middle text-center">
|
||||||
|
@if ($status === 'calificada' && $entrega)
|
||||||
|
<strong class="text-success h5 mb-0">
|
||||||
|
{{ number_format($entrega->calificacion, 1) }}
|
||||||
|
</strong>
|
||||||
|
@if ($entrega->comentario)
|
||||||
|
<br><small class="text-muted">{{ $entrega->comentario }}</small>
|
||||||
|
@endif
|
||||||
|
@else
|
||||||
|
<span class="text-muted small">Esperando...</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td class="align-middle">
|
||||||
@if ($entrega?->texto)
|
@if ($entrega?->texto)
|
||||||
<div class="border rounded p-2 bg-light mb-2" style="max-height:120px;overflow-y:auto;font-size:.85rem;">
|
<div class="border rounded p-2 bg-light"
|
||||||
|
style="max-height:100px;overflow-y:auto;font-size:.85rem;">
|
||||||
{{ $entrega->texto }}
|
{{ $entrega->texto }}
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
@if ($entrega?->archivo)
|
@if ($entrega?->archivo)
|
||||||
<a href="{{ route('files.serve', $entrega->archivo) }}"
|
<a href="{{ route('files.serve', $entrega->archivo) }}"
|
||||||
target="_blank" class="btn btn-sm btn-outline-info">
|
target="_blank"
|
||||||
<i class="fas fa-eye fa-sm"></i> Ver archivo entregado
|
class="btn btn-sm btn-outline-info mt-1">
|
||||||
|
<i class="fas fa-eye fa-sm"></i> Ver archivo
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
@if (!$entrega?->texto && !$entrega?->archivo)
|
@if (!$entrega?->texto && !$entrega?->archivo)
|
||||||
<span class="text-muted small">Sin contenido registrado</span>
|
<span class="text-muted small">Sin contenido registrado</span>
|
||||||
@endif
|
@endif
|
||||||
@if ($status === 'pendiente')
|
|
||||||
<br><small class="text-warning"><i class="fas fa-clock"></i> Esperando calificación</small>
|
|
||||||
@endif
|
|
||||||
@endif
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@endforeach
|
@endforeach
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
@endif
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@endif {{-- fin @if $cicloActual --}}
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@section('scripts')
|
@section('scripts')
|
||||||
@include('layouts._partials.tablaScript')
|
|
||||||
<script>
|
<script>
|
||||||
document.querySelectorAll('.custom-file-input').forEach(input => {
|
document.querySelectorAll('.custom-file-input').forEach(input => {
|
||||||
input.addEventListener('change', function () {
|
input.addEventListener('change', function () {
|
||||||
@@ -149,5 +317,16 @@ document.querySelectorAll('.custom-file-input').forEach(input => {
|
|||||||
this.nextElementSibling.textContent = fileName;
|
this.nextElementSibling.textContent = fileName;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll('[data-toggle="collapse"]').forEach(header => {
|
||||||
|
const target = document.querySelector(header.dataset.target);
|
||||||
|
if (!target) return;
|
||||||
|
const icon = header.querySelector('.accordion-icon');
|
||||||
|
if (target.classList.contains('show') && icon) {
|
||||||
|
icon.classList.add('fa-rotate-180');
|
||||||
|
}
|
||||||
|
target.addEventListener('show.bs.collapse', () => icon?.classList.add('fa-rotate-180'));
|
||||||
|
target.addEventListener('hide.bs.collapse', () => icon?.classList.remove('fa-rotate-180'));
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
+8
-1
@@ -18,6 +18,7 @@ use App\Http\Controllers\ConceptoController;
|
|||||||
use App\Http\Controllers\DocenteController;
|
use App\Http\Controllers\DocenteController;
|
||||||
use App\Http\Controllers\EntregaController;
|
use App\Http\Controllers\EntregaController;
|
||||||
use App\Http\Controllers\EvaluacionController;
|
use App\Http\Controllers\EvaluacionController;
|
||||||
|
use App\Http\Controllers\EvaluacionResponderController;
|
||||||
use App\Http\Controllers\TareaController;
|
use App\Http\Controllers\TareaController;
|
||||||
use App\Http\Controllers\DocumentoController;
|
use App\Http\Controllers\DocumentoController;
|
||||||
use App\Http\Controllers\DocumentoTipoController;
|
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');
|
Route::get('/search', UserSearchController::class)->name('users.search');
|
||||||
|
|
||||||
// Evaluaciones
|
// Evaluaciones (administración)
|
||||||
Route::resource('/evaluacion', EvaluacionController::class);
|
Route::resource('/evaluacion', EvaluacionController::class);
|
||||||
Route::patch('/evaluacion/{evaluacion}/status', [EvaluacionController::class, 'toggleStatus'])->name('evaluacion.toggleStatus');
|
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)
|
// Anuncios (gestión: solo Admin)
|
||||||
Route::resource('/anuncio', AnuncioController::class);
|
Route::resource('/anuncio', AnuncioController::class);
|
||||||
Route::patch('/anuncio/{anuncio}/toggle', [AnuncioController::class, 'toggleActive'])->name('anuncio.toggleActive');
|
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', [TareaController::class, 'index'])->name('grupo.material.tareas');
|
||||||
Route::get('/grupo/{grupo}/material/{material}/tareas/create', [TareaController::class, 'create'])->name('grupo.material.tareas.create');
|
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::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}', [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::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');
|
Route::put('/grupo/{grupo}/material/{material}/tareas/{tarea}', [TareaController::class, 'update'])->name('grupo.material.tareas.update');
|
||||||
|
|||||||
Reference in New Issue
Block a user