Fix citas module: horarios disponibles, form fields, Carbon type errors
This commit is contained in:
191
app/Http/Controllers/Admin/CitaController.php
Normal file → Executable file
191
app/Http/Controllers/Admin/CitaController.php
Normal file → Executable file
@@ -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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
0
app/Http/Controllers/Admin/HorarioBloqueadoController.php
Normal file → Executable file
0
app/Http/Controllers/Admin/HorarioBloqueadoController.php
Normal file → Executable file
0
app/Http/Requests/CitaRequest.php
Normal file → Executable file
0
app/Http/Requests/CitaRequest.php
Normal file → Executable file
0
app/Http/Requests/HorarioBloqueadoRequest.php
Normal file → Executable file
0
app/Http/Requests/HorarioBloqueadoRequest.php
Normal file → Executable file
Reference in New Issue
Block a user