From 803e7d888b98984e8e1923e4f0898c0f4ae136c2 Mon Sep 17 00:00:00 2001 From: nickpons666 Date: Wed, 8 Apr 2026 01:18:17 -0600 Subject: [PATCH] Fix citas module: horarios disponibles, form fields, Carbon type errors --- PLAN_AGENDAMIENTO_CITAS.md | 0 app/Http/Controllers/Admin/CitaController.php | 191 +++++++++++++--- .../Admin/HorarioBloqueadoController.php | 0 app/Http/Requests/CitaRequest.php | 0 app/Http/Requests/HorarioBloqueadoRequest.php | 0 app/Models/Cita.php | 0 app/Models/HorarioBloqueado.php | 0 config/currency.php | 0 .../2024_01_01_000008_create_citas_table.php | 0 ...00009_create_horarios_bloqueados_table.php | 0 .../views/admin/citas/calendario.blade.php | 0 resources/views/admin/citas/create.blade.php | 204 ++++++------------ resources/views/admin/citas/edit.blade.php | 0 resources/views/admin/citas/index.blade.php | 0 resources/views/admin/citas/show.blade.php | 0 .../views/admin/horarios/create.blade.php | 0 resources/views/admin/horarios/edit.blade.php | 0 .../views/admin/horarios/index.blade.php | 0 routes/admin.php | 11 +- 19 files changed, 238 insertions(+), 168 deletions(-) mode change 100644 => 100755 PLAN_AGENDAMIENTO_CITAS.md mode change 100644 => 100755 app/Http/Controllers/Admin/CitaController.php mode change 100644 => 100755 app/Http/Controllers/Admin/HorarioBloqueadoController.php mode change 100644 => 100755 app/Http/Requests/CitaRequest.php mode change 100644 => 100755 app/Http/Requests/HorarioBloqueadoRequest.php mode change 100644 => 100755 app/Models/Cita.php mode change 100644 => 100755 app/Models/HorarioBloqueado.php mode change 100644 => 100755 config/currency.php mode change 100644 => 100755 database/migrations/2024_01_01_000008_create_citas_table.php mode change 100644 => 100755 database/migrations/2024_01_01_000009_create_horarios_bloqueados_table.php mode change 100644 => 100755 resources/views/admin/citas/calendario.blade.php mode change 100644 => 100755 resources/views/admin/citas/create.blade.php mode change 100644 => 100755 resources/views/admin/citas/edit.blade.php mode change 100644 => 100755 resources/views/admin/citas/index.blade.php mode change 100644 => 100755 resources/views/admin/citas/show.blade.php mode change 100644 => 100755 resources/views/admin/horarios/create.blade.php mode change 100644 => 100755 resources/views/admin/horarios/edit.blade.php mode change 100644 => 100755 resources/views/admin/horarios/index.blade.php diff --git a/PLAN_AGENDAMIENTO_CITAS.md b/PLAN_AGENDAMIENTO_CITAS.md old mode 100644 new mode 100755 diff --git a/app/Http/Controllers/Admin/CitaController.php b/app/Http/Controllers/Admin/CitaController.php old mode 100644 new mode 100755 index 5ef635c..dce1d4e --- a/app/Http/Controllers/Admin/CitaController.php +++ b/app/Http/Controllers/Admin/CitaController.php @@ -11,6 +11,7 @@ use Carbon\Carbon; use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Log; use Illuminate\View\View; class CitaController extends Controller @@ -51,13 +52,24 @@ class CitaController extends Controller } /** - * Mostrar formulario de creación + * Mostrar formulario de creacion */ public function create(): View { $mensajes = Mensaje::orderBy('created_at', 'desc')->get(); + $mensaje = null; - return view('admin.citas.create', compact('mensajes')); + return view('admin.citas.create', compact('mensajes', 'mensaje')); + } + + /** + * Mostrar formulario de creacion desde mensaje + */ + public function createFromMensaje(Mensaje $mensaje): View + { + $mensajes = Mensaje::orderBy('created_at', 'desc')->get(); + + return view('admin.citas.create', compact('mensajes', 'mensaje')); } /** @@ -65,36 +77,44 @@ class CitaController extends Controller */ public function store(CitaRequest $request): RedirectResponse { - $data = $request->validated(); + try { + $data = $request->validated(); + Log::info('Datos validados', $data); - // Calcular hora_fin basada en hora_inicio y duracion - $horaInicio = Carbon::parse($data['hora_inicio']); - $horaFin = $horaInicio->addMinutes($data['duracion']); - $data['hora_fin'] = $horaFin->format('H:i:s'); + // Calcular hora_fin basada en hora_inicio y duracion + $horaInicio = Carbon::parse($data['hora_inicio']); + $horaFin = $horaInicio->addMinutes((int) $data['duracion']); + $data['hora_fin'] = $horaFin->format('H:i:s'); - // Verificar disponibilidad antes de crear - $conflicto = Cita::whereDate('fecha', $data['fecha']) - ->where(function ($query) use ($data) { - $query->where(function ($q) use ($data) { - $q->whereTime('hora_inicio', '<', $data['hora_fin']) - ->whereTime('hora_fin', '>', $data['hora_inicio']); - }); - }) - ->whereIn('estado', ['pendiente', 'confirmada']) - ->exists(); + // Verificar disponibilidad antes de crear + $conflicto = Cita::whereDate('fecha', $data['fecha']) + ->where(function ($query) use ($data) { + $query->where(function ($q) use ($data) { + $q->whereTime('hora_inicio', '<', $data['hora_fin']) + ->whereTime('hora_fin', '>', $data['hora_inicio']); + }); + }) + ->whereIn('estado', ['pendiente', 'confirmada']) + ->exists(); - if ($conflicto) { - return back()->withErrors(['hora_inicio' => 'Ya existe una cita programada en este horario.'])->withInput(); + if ($conflicto) { + return back()->withErrors(['hora_inicio' => 'Ya existe una cita programada en este horario.'])->withInput(); + } + + // Verificar si el horario está bloqueado + if (HorarioBloqueado::estaBloqueado($data['fecha'], $data['hora_inicio'])) { + return back()->withErrors(['hora_inicio' => 'El horario está bloqueado.'])->withInput(); + } + + $cita = Cita::create($data); + Log::info('Cita creada', ['id' => $cita->id]); + + return redirect()->route('admin.citas.index')->with('success', 'Cita creada correctamente.'); + } catch (\Exception $e) { + Log::error('Error al crear cita: '.$e->getMessage()); + + return back()->withErrors(['error' => 'Error al crear la cita: '.$e->getMessage()])->withInput(); } - - // Verificar si el horario está bloqueado - if (HorarioBloqueado::estaBloqueado($data['fecha'], $data['hora_inicio'])) { - return back()->withErrors(['hora_inicio' => 'El horario está bloqueado.'])->withInput(); - } - - Cita::create($data); - - return redirect()->route('admin.citas.index')->with('success', 'Cita creada correctamente.'); } /** @@ -128,7 +148,7 @@ class CitaController extends Controller if ($request->has('hora_inicio') || $request->has('duracion')) { $horaInicio = Carbon::parse($data['hora_inicio'] ?? $cita->hora_inicio); $duracion = $data['duracion'] ?? $cita->duracion; - $horaFin = $horaInicio->addMinutes($duracion); + $horaFin = $horaInicio->addMinutes((int) $duracion); $data['hora_fin'] = $horaFin->format('H:i:s'); } @@ -209,4 +229,117 @@ class CitaController extends Controller return response()->json($citas); } + + /** + * Obtener horarios disponibles para una fecha (API) + */ + public function getHorariosDisponibles(Request $request): JsonResponse + { + $request->validate([ + 'fecha' => ['required', 'date'], + 'duracion' => ['nullable', 'integer', 'min:15', 'max:240'], + ]); + + $fecha = $request->fecha; + $duracion = $request->duracion ?? 60; + + // Verificar si es domingo (no laborable) + $diaSemana = Carbon::parse($fecha)->dayOfWeek; + if ($diaSemana === 0) { + return response()->json([ + 'error' => 'No se atienden los domingos', + 'horarios' => [], + ]); + } + + // Horario de atención: 9:00 a 19:00 (último turno a las 18:00) + $horaApertura = 9; + $horaCierre = 18; + + // Obtener citas existentes para esa fecha + $citasExistentes = Cita::whereDate('fecha', $fecha) + ->whereIn('estado', ['pendiente', 'confirmada']) + ->orderBy('hora_inicio') + ->get(); + + // Obtener horarios bloqueados + $bloqueos = HorarioBloqueado::whereDate('fecha', $fecha) + ->where('activo', true) + ->orderBy('hora_inicio') + ->get(); + + // Generar todos los horarios disponibles + $horarios = []; + $intervalo = 30; // intervals of 30 minutes + + for ($hora = $horaApertura; $hora < $horaCierre; $hora++) { + for ($minuto = 0; $minuto < 60; $minuto += $intervalo) { + // Check if appointment fits in the schedule + $horaInicioMinutos = $hora * 60 + $minuto; + $horaFinMinutos = $horaInicioMinutos + $duracion; + + if ($horaFinMinutos > $horaCierre * 60) { + continue; + } + + $horaInicio = sprintf('%02d:%02d', $hora, $minuto); + $horaFinMinutos = $horaInicioMinutos + $duracion; + $horaFinHora = intdiv($horaFinMinutos, 60); + $horaFinMinuto = $horaFinMinutos % 60; + $horaFin = sprintf('%02d:%02d', $horaFinHora, $horaFinMinuto); + + // Verificar si hay conflicto con citas existentes + $conflicto = $citasExistentes->contains(function ($cita) use ($horaInicio, $horaFin) { + return $cita->hora_inicio < $horaFin && $cita->hora_fin > $horaInicio; + }); + + // Verificar si hay conflicto con bloqueos + $bloqueado = $bloqueos->contains(function ($bloqueo) use ($horaInicio, $horaFin) { + return $bloqueo->hora_inicio < $horaFin && $bloqueo->hora_fin > $horaInicio; + }); + + if (! $conflicto && ! $bloqueado) { + $horarios[] = [ + 'hora' => $horaInicio, + 'hora_fin' => $horaFin, + ]; + } + } + } + + return response()->json([ + 'fecha' => $fecha, + 'duracion' => $duracion, + 'horarios' => $horarios, + ]); + } + + /** + * Obtener citas en un rango de fechas (API para calendario) + */ + public function getCitasPorFecha(Request $request): JsonResponse + { + $request->validate([ + 'inicio' => ['required', 'date'], + 'fin' => ['required', 'date'], + ]); + + $citas = Cita::with('mensaje') + ->whereBetween('fecha', [$request->inicio, $request->fin]) + ->whereIn('estado', ['pendiente', 'confirmada']) + ->orderBy('fecha') + ->orderBy('hora_inicio') + ->get(); + + $bloqueos = HorarioBloqueado::whereBetween('fecha', [$request->inicio, $request->fin]) + ->where('activo', true) + ->orderBy('fecha') + ->orderBy('hora_inicio') + ->get(); + + return response()->json([ + 'citas' => $citas, + 'bloqueos' => $bloqueos, + ]); + } } diff --git a/app/Http/Controllers/Admin/HorarioBloqueadoController.php b/app/Http/Controllers/Admin/HorarioBloqueadoController.php old mode 100644 new mode 100755 diff --git a/app/Http/Requests/CitaRequest.php b/app/Http/Requests/CitaRequest.php old mode 100644 new mode 100755 diff --git a/app/Http/Requests/HorarioBloqueadoRequest.php b/app/Http/Requests/HorarioBloqueadoRequest.php old mode 100644 new mode 100755 diff --git a/app/Models/Cita.php b/app/Models/Cita.php old mode 100644 new mode 100755 diff --git a/app/Models/HorarioBloqueado.php b/app/Models/HorarioBloqueado.php old mode 100644 new mode 100755 diff --git a/config/currency.php b/config/currency.php old mode 100644 new mode 100755 diff --git a/database/migrations/2024_01_01_000008_create_citas_table.php b/database/migrations/2024_01_01_000008_create_citas_table.php old mode 100644 new mode 100755 diff --git a/database/migrations/2024_01_01_000009_create_horarios_bloqueados_table.php b/database/migrations/2024_01_01_000009_create_horarios_bloqueados_table.php old mode 100644 new mode 100755 diff --git a/resources/views/admin/citas/calendario.blade.php b/resources/views/admin/citas/calendario.blade.php old mode 100644 new mode 100755 diff --git a/resources/views/admin/citas/create.blade.php b/resources/views/admin/citas/create.blade.php old mode 100644 new mode 100755 index 70d7b02..1cabc6d --- a/resources/views/admin/citas/create.blade.php +++ b/resources/views/admin/citas/create.blade.php @@ -5,7 +5,6 @@ @section('page-title', 'Nueva Cita') @section('content') -