Fix citas module: horarios disponibles, form fields, Carbon type errors
This commit is contained in:
0
PLAN_AGENDAMIENTO_CITAS.md
Normal file → Executable file
0
PLAN_AGENDAMIENTO_CITAS.md
Normal file → Executable file
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
0
app/Models/Cita.php
Normal file → Executable file
0
app/Models/Cita.php
Normal file → Executable file
0
app/Models/HorarioBloqueado.php
Normal file → Executable file
0
app/Models/HorarioBloqueado.php
Normal file → Executable file
0
config/currency.php
Normal file → Executable file
0
config/currency.php
Normal file → Executable file
0
database/migrations/2024_01_01_000008_create_citas_table.php
Normal file → Executable file
0
database/migrations/2024_01_01_000008_create_citas_table.php
Normal file → Executable file
0
database/migrations/2024_01_01_000009_create_horarios_bloqueados_table.php
Normal file → Executable file
0
database/migrations/2024_01_01_000009_create_horarios_bloqueados_table.php
Normal file → Executable file
0
resources/views/admin/citas/calendario.blade.php
Normal file → Executable file
0
resources/views/admin/citas/calendario.blade.php
Normal file → Executable file
204
resources/views/admin/citas/create.blade.php
Normal file → Executable file
204
resources/views/admin/citas/create.blade.php
Normal file → Executable file
@@ -5,7 +5,6 @@
|
||||
@section('page-title', 'Nueva Cita')
|
||||
|
||||
@section('content')
|
||||
<!-- Breadcrumb -->
|
||||
<nav aria-label="breadcrumb" class="mb-4">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{{ route('admin.dashboard') }}">Dashboard</a></li>
|
||||
@@ -24,11 +23,11 @@
|
||||
<form method="POST" action="{{ route('admin.citas.store') }}" id="citaForm">
|
||||
@csrf
|
||||
|
||||
@if($mensaje)
|
||||
@if(isset($mensaje) && $mensaje)
|
||||
<input type="hidden" name="mensaje_id" value="{{ $mensaje->id }}">
|
||||
<div class="alert alert-info mb-4">
|
||||
<i class="fas fa-info-circle me-2"></i>
|
||||
<strong>Nota:</strong> Esta cita se creará a partir del mensaje de
|
||||
<strong>Nota:</strong> Esta cita se creara a partir del mensaje de
|
||||
<strong>{{ $mensaje->nombre }}</strong>
|
||||
<a href="{{ route('admin.mensajes.show', $mensaje) }}" class="text-decoration-underline ms-1">
|
||||
(Ver mensaje)
|
||||
@@ -37,150 +36,88 @@
|
||||
@endif
|
||||
|
||||
<div class="row">
|
||||
<!-- Cliente Nombre -->
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="cliente_nombre" class="form-label">
|
||||
Nombre del Cliente <span class="text-danger">*</span>
|
||||
</label>
|
||||
<input type="text"
|
||||
class="form-control @error('cliente_nombre') is-invalid @enderror"
|
||||
id="cliente_nombre"
|
||||
name="cliente_nombre"
|
||||
value="{{ $mensaje->nombre ?? old('cliente_nombre') }}"
|
||||
required>
|
||||
@error('cliente_nombre')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
<label for="nombre_cliente" class="form-label">Nombre del Cliente <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control @error('nombre_cliente') is-invalid @enderror" id="nombre_cliente" name="nombre_cliente" value="{{ isset($mensaje) && $mensaje ? $mensaje->nombre : old('nombre_cliente') }}" required>
|
||||
@error('nombre_cliente')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||
</div>
|
||||
|
||||
<!-- Cliente Email -->
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="cliente_email" class="form-label">
|
||||
Email <span class="text-danger">*</span>
|
||||
</label>
|
||||
<input type="email"
|
||||
class="form-control @error('cliente_email') is-invalid @enderror"
|
||||
id="cliente_email"
|
||||
name="cliente_email"
|
||||
value="{{ $mensaje->email ?? old('cliente_email') }}"
|
||||
required>
|
||||
@error('cliente_email')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
<label for="email_cliente" class="form-label">Email <span class="text-danger">*</span></label>
|
||||
<input type="email" class="form-control @error('email_cliente') is-invalid @enderror" id="email_cliente" name="email_cliente" value="{{ isset($mensaje) && $mensaje ? $mensaje->email : old('email_cliente') }}" required>
|
||||
@error('email_cliente')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||
</div>
|
||||
|
||||
<!-- Cliente Teléfono -->
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="cliente_telefono" class="form-label">
|
||||
Teléfono
|
||||
</label>
|
||||
<input type="tel"
|
||||
class="form-control @error('cliente_telefono') is-invalid @enderror"
|
||||
id="cliente_telefono"
|
||||
name="cliente_telefono"
|
||||
value="{{ $mensaje->telefono ?? old('cliente_telefono') }}">
|
||||
@error('cliente_telefono')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
<label for="telefono_cliente" class="form-label">Telefono</label>
|
||||
<input type="tel" class="form-control @error('telefono_cliente') is-invalid @enderror" id="telefono_cliente" name="telefono_cliente" value="{{ isset($mensaje) && $mensaje ? $mensaje->telefono : old('telefono_cliente') }}">
|
||||
@error('telefono_cliente')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||
</div>
|
||||
|
||||
<!-- Servicio -->
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="servicio" class="form-label">
|
||||
Servicio
|
||||
</label>
|
||||
<select class="form-select @error('servicio') is-invalid @enderror"
|
||||
id="servicio"
|
||||
name="servicio">
|
||||
<option value="Lash Extensions" selected>Lash Extensions</option>
|
||||
<label for="servicio" class="form-label">Servicio <span class="text-danger">*</span></label>
|
||||
<select class="form-select @error('servicio') is-invalid @enderror" id="servicio" name="servicio" required>
|
||||
<option value="">Seleccione servicio</option>
|
||||
<option value="Lash Extensions">Lash Extensions</option>
|
||||
<option value="Lash Lift">Lash Lift</option>
|
||||
<option value="Lash Removal">Lash Removal</option>
|
||||
<option value="Relleno">Relleno</option>
|
||||
<option value="Otro">Otro</option>
|
||||
</select>
|
||||
@error('servicio')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<div class="row">
|
||||
<!-- Fecha -->
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="fecha" class="form-label">
|
||||
Fecha <span class="text-danger">*</span>
|
||||
</label>
|
||||
<input type="date"
|
||||
class="form-control @error('fecha') is-invalid @enderror"
|
||||
id="fecha"
|
||||
name="fecha"
|
||||
value="{{ old('fecha') }}"
|
||||
min="{{ date('Y-m-d') }}"
|
||||
required>
|
||||
@error('fecha')
|
||||
<label for="fecha" class="form-label">Fecha <span class="text-danger">*</span></label>
|
||||
<input type="date" class="form-control @error('fecha') is-invalid @enderror" id="fecha" name="fecha" value="{{ old('fecha') }}" min="{{ date('Y-m-d') }}" required>
|
||||
@error('fecha')<div class="invalid-feedback">{{ $message }}</div>@else<small class="text-muted">Horario: 9:00 AM - 7:00 PM (Lun-Sab)</small>@enderror
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="hora_inicio" class="form-label">Hora <span class="text-danger">*</span></label>
|
||||
<select class="form-select @error('hora_inicio') is-invalid @enderror" id="hora_inicio" name="hora_inicio" required>
|
||||
<option value="">Seleccione fecha primero</option>
|
||||
</select>
|
||||
@error('hora_inicio')<div class="invalid-feedback">{{ $message }}</div>@else<small class="text-muted">Duracion: 60 minutos</small>@enderror
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="duracion" class="form-label">Duracion (minutos) <span class="text-danger">*</span></label>
|
||||
<select class="form-select" id="duracion" name="duracion" required>
|
||||
<option value="60" selected>60 minutos</option>
|
||||
<option value="45">45 minutos</option>
|
||||
<option value="30">30 minutos</option>
|
||||
<option value="90">90 minutos</option>
|
||||
<option value="120">120 minutos</option>
|
||||
</select>
|
||||
@error('duracion')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@else
|
||||
<small class="text-muted">
|
||||
<i class="fas fa-info-circle me-1"></i>
|
||||
Horario: 9:00 AM - 7:00 PM (Lun-Sáb)
|
||||
</small>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Hora Inicio -->
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="hora_inicio" class="form-label">
|
||||
Hora <span class="text-danger">*</span>
|
||||
</label>
|
||||
<select class="form-select @error('hora_inicio') is-invalid @enderror"
|
||||
id="hora_inicio"
|
||||
name="hora_inicio"
|
||||
required
|
||||
{{ old('fecha') ? '' : 'disabled' }}>
|
||||
<option value="">Seleccione fecha primero</option>
|
||||
@if(old('fecha'))
|
||||
@php
|
||||
$oldFecha = old('fecha');
|
||||
$oldHora = old('hora_inicio');
|
||||
@endphp
|
||||
@foreach($horariosDisponibles ?? [] as $hora)
|
||||
<option value="{{ $hora['hora'] }}"
|
||||
{{ $oldHora == $hora['hora'] ? 'selected' : '' }}>
|
||||
{{ $hora['label'] }}
|
||||
</option>
|
||||
@endforeach
|
||||
@endif
|
||||
<label for="estado" class="form-label">Estado</label>
|
||||
<select class="form-select" id="estado" name="estado">
|
||||
<option value="pendiente" selected>Pendiente</option>
|
||||
<option value="confirmada">Confirmada</option>
|
||||
</select>
|
||||
@error('hora_inicio')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@else
|
||||
<small class="text-muted" id="horaHelp">
|
||||
Duración: 60 minutos
|
||||
</small>
|
||||
@enderror
|
||||
<div class="loading-spinner d-none" id="horaLoading">
|
||||
<div class="spinner"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Notas -->
|
||||
<div class="mb-3">
|
||||
<label for="notas" class="form-label">Notas</label>
|
||||
<textarea class="form-control @error('notas') is-invalid @enderror"
|
||||
id="notas"
|
||||
name="notas"
|
||||
rows="3"
|
||||
placeholder="Notas adicionales sobre la cita...">{{ old('notas') }}</textarea>
|
||||
@error('notas')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
<textarea class="form-control" id="notas" name="notas" rows="3" placeholder="Notas adicionales sobre la cita...">{{ old('notas') }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-4">
|
||||
<a href="{{ route('admin.citas.index') }}" class="btn btn-secondary-admin">
|
||||
<i class="fas fa-arrow-left me-2"></i>Volver
|
||||
</a>
|
||||
<button type="submit" class="btn btn-primary-admin" id="submitBtn">
|
||||
<button type="submit" class="btn btn-primary-admin">
|
||||
<i class="fas fa-calendar-check me-2"></i>Agendar Cita
|
||||
</button>
|
||||
</div>
|
||||
@@ -189,18 +126,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card-admin mb-3">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-info-circle me-2"></i>Información
|
||||
<i class="fas fa-info-circle me-2"></i>Informacion
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="text-muted">
|
||||
<li class="mb-2">Duración de cada cita: <strong>60 minutos</strong></li>
|
||||
<li class="mb-2">Horario de atención: <strong>9:00 AM - 7:00 PM</strong></li>
|
||||
<li class="mb-2">Días laborables: <strong>Lunes a Sábado</strong></li>
|
||||
<li class="mb-2">Último turno: <strong>6:00 PM</strong></li>
|
||||
<li class="mb-2">Duracion de cada cita: <strong>60 minutos</strong></li>
|
||||
<li class="mb-2">Horario de atencion: <strong>9:00 AM - 7:00 PM</strong></li>
|
||||
<li class="mb-2">Dias laborables: <strong>Lunes a Sabado</strong></li>
|
||||
<li class="mb-2">Ultimo turno: <strong>6:00 PM</strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -227,18 +163,16 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const fechaInput = document.getElementById('fecha');
|
||||
const horaSelect = document.getElementById('hora_inicio');
|
||||
const horaLoading = document.getElementById('horaLoading');
|
||||
const horaHelp = document.getElementById('horaHelp');
|
||||
const duracionSelect = document.getElementById('duracion');
|
||||
const hoy = new Date().toISOString().split('T')[0];
|
||||
fechaInput.setAttribute('min', hoy);
|
||||
|
||||
// Sundays disabled
|
||||
fechaInput.addEventListener('change', function() {
|
||||
const fecha = new Date(this.value + 'T00:00:00');
|
||||
const diaSemana = fecha.getDay();
|
||||
|
||||
if (diaSemana === 0) {
|
||||
alert('Los domingos no hay atención.');
|
||||
alert('Los domingos no hay atencion.');
|
||||
this.value = '';
|
||||
horaSelect.innerHTML = '<option value="">Seleccione fecha primero</option>';
|
||||
horaSelect.disabled = true;
|
||||
@@ -248,25 +182,34 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
cargarHorariosDisponibles(this.value);
|
||||
});
|
||||
|
||||
duracionSelect.addEventListener('change', function() {
|
||||
if (fechaInput.value) {
|
||||
cargarHorariosDisponibles(fechaInput.value);
|
||||
}
|
||||
});
|
||||
|
||||
function cargarHorariosDisponibles(fecha) {
|
||||
if (!fecha) return;
|
||||
|
||||
horaSelect.disabled = true;
|
||||
horaLoading.classList.remove('d-none');
|
||||
horaHelp.classList.add('d-none');
|
||||
horaSelect.innerHTML = '<option value="">Cargando...</option>';
|
||||
|
||||
fetch(`/admin/citas/disponibles?fecha=${fecha}`)
|
||||
const duracion = duracionSelect.value;
|
||||
|
||||
fetch(`/admin/citas/disponibles?fecha=${fecha}&duracion=${duracion}`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
horaSelect.innerHTML = '<option value="">Seleccione un horario</option>';
|
||||
|
||||
if (data.length === 0) {
|
||||
if (data.error) {
|
||||
horaSelect.innerHTML = '<option value="">' + data.error + '</option>';
|
||||
} else if (data.horarios && data.horarios.length === 0) {
|
||||
horaSelect.innerHTML = '<option value="">No hay horarios disponibles</option>';
|
||||
} else {
|
||||
data.forEach(hora => {
|
||||
} else if (data.horarios) {
|
||||
data.horarios.forEach(hora => {
|
||||
const option = document.createElement('option');
|
||||
option.value = hora.hora;
|
||||
option.textContent = hora.label;
|
||||
option.textContent = hora.hora + ' - ' + hora.hora_fin;
|
||||
horaSelect.appendChild(option);
|
||||
});
|
||||
}
|
||||
@@ -277,17 +220,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
console.error('Error:', error);
|
||||
horaSelect.innerHTML = '<option value="">Error al cargar horarios</option>';
|
||||
horaSelect.disabled = false;
|
||||
})
|
||||
.finally(() => {
|
||||
horaLoading.classList.add('d-none');
|
||||
horaHelp.classList.remove('d-none');
|
||||
});
|
||||
}
|
||||
|
||||
// Trigger on page load if fecha has value
|
||||
if (fechaInput.value) {
|
||||
cargarHorariosDisponibles(fechaInput.value);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@endpush
|
||||
|
||||
0
resources/views/admin/citas/edit.blade.php
Normal file → Executable file
0
resources/views/admin/citas/edit.blade.php
Normal file → Executable file
0
resources/views/admin/citas/index.blade.php
Normal file → Executable file
0
resources/views/admin/citas/index.blade.php
Normal file → Executable file
0
resources/views/admin/citas/show.blade.php
Normal file → Executable file
0
resources/views/admin/citas/show.blade.php
Normal file → Executable file
0
resources/views/admin/horarios/create.blade.php
Normal file → Executable file
0
resources/views/admin/horarios/create.blade.php
Normal file → Executable file
0
resources/views/admin/horarios/edit.blade.php
Normal file → Executable file
0
resources/views/admin/horarios/edit.blade.php
Normal file → Executable file
0
resources/views/admin/horarios/index.blade.php
Normal file → Executable file
0
resources/views/admin/horarios/index.blade.php
Normal file → Executable file
@@ -86,17 +86,20 @@ Route::middleware(['admin.auth', 'security.headers'])->group(function () {
|
||||
Route::get('/create/{mensaje_id}', [CitaController::class, 'createFromMensaje'])->name('create-from-mensaje');
|
||||
Route::post('/', [CitaController::class, 'store'])->name('store');
|
||||
Route::get('/calendario', [CitaController::class, 'calendario'])->name('calendario');
|
||||
|
||||
// API endpoints (antes de la ruta dinamica)
|
||||
Route::get('/disponibles', [CitaController::class, 'getHorariosDisponibles'])->name('disponibles');
|
||||
Route::get('/por-fecha', [CitaController::class, 'getCitasPorFecha'])->name('por-fecha');
|
||||
|
||||
// Rutas dinamicas al final
|
||||
Route::get('/{cita}', [CitaController::class, 'show'])->name('show');
|
||||
Route::get('/{cita}/edit', [CitaController::class, 'edit'])->name('edit');
|
||||
Route::put('/{cita}', [CitaController::class, 'update'])->name('update');
|
||||
Route::delete('/{cita}', [CitaController::class, 'destroy'])->name('destroy');
|
||||
Route::patch('/{cita}/estado', [CitaController::class, 'cambiarEstado'])->name('estado');
|
||||
Route::get('/{cita}/ver', [CitaController::class, 'porFecha'])->name('ver');
|
||||
});
|
||||
|
||||
// API de Citas
|
||||
Route::get('/citas/disponibles', [CitaController::class, 'getHorariosDisponibles'])->name('admin.citas.disponibles');
|
||||
Route::get('/citas/por-fecha', [CitaController::class, 'getCitasPorFecha'])->name('admin.citas.por-fecha');
|
||||
|
||||
// Horarios Bloqueados
|
||||
Route::prefix('horarios')->name('admin.horarios.')->group(function () {
|
||||
Route::get('/', [HorarioBloqueadoController::class, 'index'])->name('index');
|
||||
|
||||
Reference in New Issue
Block a user