Comence con las vistas. El modelo de agenda está practicamente terminado
This commit is contained in:
+2
-1
@@ -41,7 +41,8 @@ Formulario -Si Cli_DNI es null, entonces ese formulario lo envió
|
|||||||
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
|
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
|
||||||
LogSeguridad -Responsable_ID: Vendría a ser el DNI de la persona que ejecutó esa accion.
|
LogSeguridad -Responsable_ID: Vendría a ser el DNI de la persona que ejecutó esa accion.
|
||||||
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
|
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
|
||||||
|
HorariosAtenciones -TIPO = Corresponde a AM o PM
|
||||||
|
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use App\Models\Administrador;
|
||||||
|
use App\Models\CredencialProfesional;
|
||||||
|
use App\Models\Foto;
|
||||||
|
use App\Models\Persona;
|
||||||
|
use Illuminate\Contracts\Console\Kernel;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
||||||
|
require __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
$app = require __DIR__ . '/bootstrap/app.php';
|
||||||
|
$app->make(Kernel::class)->bootstrap();
|
||||||
|
|
||||||
|
$usuario = env('ADMIN_USUARIO', 'admin');
|
||||||
|
$passwordPlano = env('ADMIN_PASSWORD', 'admin1234');
|
||||||
|
$correo = env('ADMIN_CORREO', 'admin@abogadaslitoral.com');
|
||||||
|
$dni = env('ADMIN_DNI', '30000000');
|
||||||
|
$nombre = env('ADMIN_NOMBRE', 'Usuario');
|
||||||
|
$apellido = env('ADMIN_APELLIDO', 'Administrador');
|
||||||
|
$cuil = env('ADMIN_CUIL', '20-30000000-0');
|
||||||
|
$fechaNac = env('ADMIN_FECHANAC', '2026-01-01');
|
||||||
|
|
||||||
|
try {
|
||||||
|
DB::transaction(function () use ($usuario, $passwordPlano, $correo, $dni, $nombre, $apellido, $cuil, $fechaNac): void {
|
||||||
|
$foto = Foto::firstOrCreate(
|
||||||
|
['ruta' => 'avatars/admin-default.png'],
|
||||||
|
[
|
||||||
|
'extension' => 'png',
|
||||||
|
'nombre' => 'admin-default',
|
||||||
|
'mime_type' => 'image/png',
|
||||||
|
'tamanio_bytes' => 0,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$persona = Persona::updateOrCreate(
|
||||||
|
['dni' => $dni],
|
||||||
|
[
|
||||||
|
'nombre' => $nombre,
|
||||||
|
'apellido' => $apellido,
|
||||||
|
'cuil' => $cuil,
|
||||||
|
'fechanac' => $fechaNac,
|
||||||
|
'foto_id' => $foto->id,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$credencial = CredencialProfesional::updateOrCreate(
|
||||||
|
['usuario' => $usuario],
|
||||||
|
[
|
||||||
|
'contra' => Hash::make($passwordPlano),
|
||||||
|
'rol' => 'ADMIN',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
Administrador::updateOrCreate(
|
||||||
|
['dni' => $dni, 'correo' => $correo],
|
||||||
|
[
|
||||||
|
'persona_id' => $persona->id,
|
||||||
|
'credencialprofesional_id' => $credencial->id,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
echo "Administrador creado/actualizado correctamente." . PHP_EOL;
|
||||||
|
echo "Usuario: {$usuario}" . PHP_EOL;
|
||||||
|
echo "Correo: {$correo}" . PHP_EOL;
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
fwrite(STDERR, 'Error al crear administrador: ' . $e->getMessage() . PHP_EOL);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
@@ -76,4 +76,5 @@ class AgendaController extends Controller
|
|||||||
'message' => 'Registro eliminado correctamente',
|
'message' => 'Registro eliminado correctamente',
|
||||||
], 200);
|
], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,237 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\CredencialCliente;
|
||||||
|
use App\Models\CredencialProfesional;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class AuthController extends Controller
|
||||||
|
{
|
||||||
|
public function loginClienteWeb(Request $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'correo' => ['required', 'string'],
|
||||||
|
'contra' => ['required', 'string'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$correo = trim((string) $request->input('correo'));
|
||||||
|
$contra = (string) $request->input('contra');
|
||||||
|
|
||||||
|
$credencial = CredencialCliente::where('correo', $correo)->first();
|
||||||
|
if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
|
||||||
|
return back()
|
||||||
|
->withInput($request->except('contra'))
|
||||||
|
->with('login_error', 'Usuario o contraseña incorrectos.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = Str::random(64);
|
||||||
|
$credencial->token = $token;
|
||||||
|
$credencial->fecha_hora = now();
|
||||||
|
$credencial->save();
|
||||||
|
|
||||||
|
return back()->with('login_success', 'Login exitoso.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loginPersonalWeb(Request $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'usuario' => ['required', 'string'],
|
||||||
|
'contra' => ['required', 'string'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$usuario = trim((string) $request->input('usuario'));
|
||||||
|
$contra = (string) $request->input('contra');
|
||||||
|
|
||||||
|
$credencial = CredencialProfesional::where('usuario', $usuario)->first();
|
||||||
|
if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
|
||||||
|
return back()
|
||||||
|
->withInput($request->except('contra'))
|
||||||
|
->with('login_error', 'Usuario o contraseña incorrectos.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = Str::random(64);
|
||||||
|
$credencial->token = $token;
|
||||||
|
$credencial->fecha_hora = now();
|
||||||
|
$credencial->save();
|
||||||
|
|
||||||
|
return back()->with('login_success', 'Login exitoso.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loginCliente(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'correo' => ['required', 'string'],
|
||||||
|
'contra' => ['required', 'string'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$correo = trim((string) $request->input('correo'));
|
||||||
|
$contra = (string) $request->input('contra');
|
||||||
|
|
||||||
|
$credencial = CredencialCliente::where('correo', $correo)->first();
|
||||||
|
if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Credenciales invalidas',
|
||||||
|
], 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = Str::random(64);
|
||||||
|
$credencial->token = $token;
|
||||||
|
$credencial->fecha_hora = now();
|
||||||
|
$credencial->save();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'data' => [
|
||||||
|
'tipo' => 'cliente',
|
||||||
|
'id_credencial' => $credencial->id,
|
||||||
|
'token' => $token,
|
||||||
|
],
|
||||||
|
'message' => 'Login de cliente exitoso',
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loginPersonal(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'usuario' => ['required', 'string'],
|
||||||
|
'contra' => ['required', 'string'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$usuario = trim((string) $request->input('usuario'));
|
||||||
|
$contra = (string) $request->input('contra');
|
||||||
|
|
||||||
|
$credencial = CredencialProfesional::where('usuario', $usuario)->first();
|
||||||
|
if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Credenciales invalidas',
|
||||||
|
], 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = Str::random(64);
|
||||||
|
$credencial->token = $token;
|
||||||
|
$credencial->fecha_hora = now();
|
||||||
|
$credencial->save();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'data' => [
|
||||||
|
'tipo' => 'personal',
|
||||||
|
'rol' => $credencial->rol,
|
||||||
|
'id_credencial' => $credencial->id,
|
||||||
|
'token' => $token,
|
||||||
|
],
|
||||||
|
'message' => 'Login de personal exitoso',
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function login(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'identificador' => ['required', 'string'],
|
||||||
|
'contra' => ['required', 'string'],
|
||||||
|
'tipo' => ['nullable', 'in:cliente,profesional'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$identificador = trim((string) $request->input('identificador'));
|
||||||
|
$contra = (string) $request->input('contra');
|
||||||
|
$tipo = $request->input('tipo');
|
||||||
|
|
||||||
|
$credencial = null;
|
||||||
|
$tipoDetectado = null;
|
||||||
|
|
||||||
|
if ($tipo === 'cliente') {
|
||||||
|
$credencial = CredencialCliente::where('correo', $identificador)->first();
|
||||||
|
$tipoDetectado = 'cliente';
|
||||||
|
} elseif ($tipo === 'profesional') {
|
||||||
|
$credencial = CredencialProfesional::where('usuario', $identificador)->first();
|
||||||
|
$tipoDetectado = 'personal';
|
||||||
|
} else {
|
||||||
|
$credencial = CredencialCliente::where('correo', $identificador)->first();
|
||||||
|
$tipoDetectado = $credencial ? 'cliente' : null;
|
||||||
|
|
||||||
|
if (!$credencial) {
|
||||||
|
$credencial = CredencialProfesional::where('usuario', $identificador)->first();
|
||||||
|
$tipoDetectado = $credencial ? 'personal' : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Credenciales invalidas',
|
||||||
|
], 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = Str::random(64);
|
||||||
|
$credencial->token = $token;
|
||||||
|
$credencial->fecha_hora = now();
|
||||||
|
$credencial->save();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'data' => [
|
||||||
|
'tipo' => $tipoDetectado,
|
||||||
|
'rol' => $credencial instanceof CredencialProfesional ? $credencial->rol : null,
|
||||||
|
'id_credencial' => $credencial->id,
|
||||||
|
'token' => $token,
|
||||||
|
],
|
||||||
|
'message' => 'Login exitoso',
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function logout(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$token = (string) $request->input('token', '');
|
||||||
|
if ($token === '') {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Token requerido',
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$credencialCliente = CredencialCliente::where('token', $token)->first();
|
||||||
|
if ($credencialCliente) {
|
||||||
|
$credencialCliente->token = null;
|
||||||
|
$credencialCliente->fecha_hora = now();
|
||||||
|
$credencialCliente->save();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Logout exitoso',
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
$credencialProfesional = CredencialProfesional::where('token', $token)->first();
|
||||||
|
if ($credencialProfesional) {
|
||||||
|
$credencialProfesional->token = null;
|
||||||
|
$credencialProfesional->fecha_hora = now();
|
||||||
|
$credencialProfesional->save();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Logout exitoso',
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Token invalido',
|
||||||
|
], 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function credencialValida(string $contraIngresada, string $contraGuardada): bool
|
||||||
|
{
|
||||||
|
if ($contraIngresada === $contraGuardada) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Hash::check($contraIngresada, $contraGuardada);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\EstadoTurno;
|
use App\Models\EstadoTurno;
|
||||||
use Illuminate\Http\JsonResponse;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class EstadoTurnoController extends Controller
|
class EstadoTurnoController extends Controller
|
||||||
@@ -11,41 +10,43 @@ class EstadoTurnoController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Display a listing of the resource.
|
* Display a listing of the resource.
|
||||||
*/
|
*/
|
||||||
public function index(): JsonResponse
|
public function index() : JsonResponse
|
||||||
{
|
{
|
||||||
$items = EstadoTurno::all();
|
$estadoTurnos = EstadoTurno::all();
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'data' => $items,
|
'data' => $estadoTurnos,
|
||||||
'message' => 'Registros obtenidos correctamente',
|
'message' => 'Estados de turno obtenidos correctamente'
|
||||||
], 200);
|
], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a newly created resource in storage.
|
* Store a newly created resource in storage.
|
||||||
*/
|
*/
|
||||||
public function store(Request $request): JsonResponse
|
public function store(Request $request) : JsonResponse
|
||||||
{
|
{
|
||||||
$payload = $request->only((new EstadoTurno())->getFillable());
|
$validated = $request->validate([
|
||||||
$estadoTurno = EstadoTurno::create($payload);
|
'descripcion' => 'required|string|max:255|unique:estado_turnos,descripcion',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$estadoTurno = EstadoTurno::create($validated);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'data' => $estadoTurno,
|
'data' => $estadoTurno,
|
||||||
'message' => 'Registro creado correctamente',
|
'message' => 'Estado de turno creado correctamente'
|
||||||
], 201);
|
], 201);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the specified resource.
|
* Display the specified resource.
|
||||||
*/
|
*/
|
||||||
public function show(EstadoTurno $estadoTurno): JsonResponse
|
public function show(EstadoTurno $estadoTurno) : JsonResponse
|
||||||
{
|
{
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'data' => $estadoTurno,
|
'data' => $estadoTurno,
|
||||||
'message' => 'Registro obtenido correctamente',
|
'message' => 'Estado de turno obtenido correctamente'
|
||||||
], 200);
|
], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,26 +55,28 @@ class EstadoTurnoController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function update(Request $request, EstadoTurno $estadoTurno): JsonResponse
|
public function update(Request $request, EstadoTurno $estadoTurno): JsonResponse
|
||||||
{
|
{
|
||||||
$payload = $request->only((new EstadoTurno())->getFillable());
|
$validated = $request->validate([
|
||||||
$estadoTurno->update($payload);
|
'descripcion' => 'required|string|max:255|unique:estado_turnos,descripcion,' . $estadoTurno->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$estadoTurno->update($validated);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'data' => $estadoTurno,
|
'data' => $estadoTurno,
|
||||||
'message' => 'Registro actualizado correctamente',
|
'message' => 'Estado de turno actualizado correctamente'
|
||||||
], 200);
|
], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the specified resource from storage.
|
* Remove the specified resource from storage.
|
||||||
*/
|
*/
|
||||||
public function destroy(EstadoTurno $estadoTurno): JsonResponse
|
public function destroy(EstadoTurno $estadoTurno) : JsonResponse
|
||||||
{
|
{
|
||||||
$estadoTurno->delete();
|
$estadoTurno->delete();
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'message' => 'Registro eliminado correctamente',
|
'message' => 'Estado de turno eliminado correctamente'
|
||||||
], 200);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ use App\Models\Profesional;
|
|||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
use App\Http\Controllers\LogSeguridadController;
|
||||||
|
|
||||||
class ProfesionalController extends Controller
|
class ProfesionalController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@@ -22,21 +24,54 @@ class ProfesionalController extends Controller
|
|||||||
], 200);
|
], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a newly created resource in storage.
|
* Store a newly created resource in storage.
|
||||||
*/
|
*/
|
||||||
public function store(Request $request): JsonResponse
|
public function store(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$payload = $request->only((new Profesional())->getFillable());
|
// 1. Validamos los datos de entrada
|
||||||
$profesional = Profesional::create($payload);
|
$validated = $request->validate([
|
||||||
|
'inicio' => 'required|date_format:Y-m-d H:i:s',
|
||||||
|
'correo' => 'nullable|email|max:255',
|
||||||
|
'nombrecompleto' => 'nullable|string|max:255',
|
||||||
|
'descripcion' => 'required|string',
|
||||||
|
'cliente_id' => 'nullable|exists:clientes,id',
|
||||||
|
'estadoturno_id' => 'required|exists:estadosturnos,id',
|
||||||
|
'agenda_id' => 'required|exists:agendas,id',
|
||||||
|
'profesional_id' => 'required|exists:profesionales,id',
|
||||||
|
'servicio_id' => 'required|exists:servicios,id',
|
||||||
|
'modalidad_id' => 'required|exists:modalidades,id',
|
||||||
|
]);
|
||||||
|
// 2. Verificamos si el profesional ya tiene un turno en ese "inicio"
|
||||||
|
$existeTurno = Turno::where('profesional_id', $validated['profesional_id'])
|
||||||
|
->where('inicio', $validated['inicio'])
|
||||||
|
->whereIn('estadoturno_id', [1, 2]) // 1: Pendiente, 2: Confirmado
|
||||||
|
->exists();
|
||||||
|
|
||||||
|
if ($existeTurno) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => false,
|
||||||
'data' => $profesional,
|
'message' => 'Horario no disponible.',
|
||||||
'message' => 'Registro creado correctamente',
|
], 422); // Error de validación lógica
|
||||||
], 201);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$turno = Turno::create($validated);
|
||||||
|
|
||||||
|
// 3. Registramos el evento en el log de seguridad
|
||||||
|
$personaId = auth()->check() ? auth()->user()->persona->id : null;
|
||||||
|
LogSeguridadController::registrarAccion('Creacion de turno ID: ' . $turno->id, 'Profesional', 17, $personaId);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'data' => $turno,
|
||||||
|
'message' => 'Turno agendado correctamente para el ' . $turno->inicio->format('d/m/Y H:i'),
|
||||||
|
], 201);
|
||||||
|
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Display the specified resource.
|
* Display the specified resource.
|
||||||
*/
|
*/
|
||||||
@@ -76,4 +111,14 @@ class ProfesionalController extends Controller
|
|||||||
'message' => 'Registro eliminado correctamente',
|
'message' => 'Registro eliminado correctamente',
|
||||||
], 200);
|
], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function aceptarFormulario(Formulario $formulario): JsonResponse
|
||||||
|
{
|
||||||
|
$turno->update(['estadoturno_id' => 2]);
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'message' => 'Turno aceptado correctamente',
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -76,4 +76,59 @@ class TurnoController extends Controller
|
|||||||
'message' => 'Registro eliminado correctamente',
|
'message' => 'Registro eliminado correctamente',
|
||||||
], 200);
|
], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function confirmar(Turno $turno): JsonResponse
|
||||||
|
{
|
||||||
|
$turno->confirmar();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'data' => $turno,
|
||||||
|
'message' => 'Turno confirmado correctamente',
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cancelar(Turno $turno): JsonResponse
|
||||||
|
{
|
||||||
|
$turno->cancelar();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'data' => $turno,
|
||||||
|
'message' => 'Turno cancelado correctamente',
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reprogramar(Turno $turno): JsonResponse
|
||||||
|
{
|
||||||
|
$turno->reprogramar();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'data' => $turno,
|
||||||
|
'message' => 'Turno reprogramado correctamente',
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clienteAusente(Turno $turno): JsonResponse
|
||||||
|
{
|
||||||
|
$turno->clienteAusente();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'data' => $turno,
|
||||||
|
'message' => 'Turno marcado como cliente ausente',
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clientePresente(Turno $turno): JsonResponse
|
||||||
|
{
|
||||||
|
$turno->clientePresente();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
'data' => $turno,
|
||||||
|
'message' => 'Turno marcado como cliente presente',
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ namespace App\Models;
|
|||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
class Agenda extends Model
|
class Agenda extends Model
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -40,4 +41,509 @@ class Agenda extends Model
|
|||||||
{
|
{
|
||||||
return $this->hasMany(ModoVacaciones::class, 'agenda_id', 'id');
|
return $this->hasMany(ModoVacaciones::class, 'agenda_id', 'id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function formularios()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(
|
||||||
|
\App\Models\Formulario::class,
|
||||||
|
'profesionales_formularios',
|
||||||
|
'profesional_id',
|
||||||
|
'formulario_id',
|
||||||
|
'profesional_id',
|
||||||
|
'id'
|
||||||
|
)->withPivot('estadoformulario')->withTimestamps();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function estaDisponible($fecha, $hora, $agendaId = null) // Verificar si la agenda está disponible para una fecha y hora específicas
|
||||||
|
{
|
||||||
|
$agenda = $agendaId ? self::find($agendaId) : $this;
|
||||||
|
if (!$agenda) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar si la fecha es un feriado
|
||||||
|
if ($agenda->feriado()->where('fecha', $fecha)->exists()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar si la fecha está dentro de un período de vacaciones
|
||||||
|
if ($agenda->modoVacaciones()->where('inicio', '<=', $fecha)->where('fin', '>=', $fecha)->exists()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar si el día de atención corresponde al día de la semana de la fecha
|
||||||
|
$diaSemana = date('N', strtotime($fecha)); // 1 (lunes) a 7 (domingo)
|
||||||
|
$diaAtencion = $agenda->diaDeAtencion()->where('dia_id', $diaSemana)->first();
|
||||||
|
if (!$diaAtencion) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar si la hora está dentro del horario de atención
|
||||||
|
if (!$diaAtencion->horariosAtenciones()->where('horariocomienzo', '<=', $hora)->where('horariofin', '>=', $hora)->exists()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar si la hora no está dentro de un horario de receso
|
||||||
|
if ($diaAtencion->horariosRecesos()->where('comienzo', '<=', $hora)->where('fin', '>=', $hora)->exists()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verificar si ya existe un turno para esa fecha y hora
|
||||||
|
if ($agenda->turno()->where('inicio', $fecha . ' ' . $hora)->exists()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function obtenerTurnoDisponible($idProfesional, $tipopreferencia = 'INDISTINTO', $diasPreferencia = []) //Devuelve el turno disponible más cercano según preferencias
|
||||||
|
{
|
||||||
|
// Inicializa estructuras de salida con valores por defecto.
|
||||||
|
$DiasDeAtenciones = array_fill(0, 7, array_fill(0, 5, null));
|
||||||
|
$tipo = null;
|
||||||
|
$recesos = [];
|
||||||
|
$vacaciones = [];
|
||||||
|
$feriados = [];
|
||||||
|
$turnoMasCercano = null;
|
||||||
|
|
||||||
|
// Busca la agenda asociada al profesional recibido por parámetro.
|
||||||
|
$agendaId = self::where('profesional_id', $idProfesional)->value('id');
|
||||||
|
|
||||||
|
// Si no existe agenda para ese profesional, devuelve la estructura vacía.
|
||||||
|
if (!$agendaId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtiene días de atención y sus horarios AM/PM para la agenda encontrada.
|
||||||
|
// [fila][0]=dia_id, [fila][1]=inicio AM, [fila][2]=fin AM, [fila][3]=inicio PM, [fila][4]=fin PM
|
||||||
|
|
||||||
|
$filasAtencion = DB::table('diasdeatenciones as d')
|
||||||
|
->leftJoin('horariosatenciones as h', 'h.diadeatencion_id', '=', 'd.id')
|
||||||
|
->where('d.agenda_id', $agendaId)
|
||||||
|
->select('d.dia_id', 'h.horariocomienzo', 'h.horariofin', 'h.tipo')
|
||||||
|
->orderBy('d.dia_id')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
// Recorre cada fila y la ubica en la matriz de 7x5 según día y tipo de horario.
|
||||||
|
foreach ($filasAtencion as $filaAtencion) {
|
||||||
|
$fila = (int) $filaAtencion->dia_id - 1;
|
||||||
|
|
||||||
|
// Ignora valores fuera del rango esperado de días (1 a 7).
|
||||||
|
if ($fila < 0 || $fila > 6) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guarda el identificador del día en la primera columna.
|
||||||
|
$DiasDeAtenciones[$fila][0] = $filaAtencion->dia_id;
|
||||||
|
|
||||||
|
// Si no hay tipo de horario, salta al siguiente registro.
|
||||||
|
if ($filaAtencion->tipo === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normaliza y guarda el tipo actual (AM o PM).
|
||||||
|
$tipoActual = strtoupper(trim((string) $filaAtencion->tipo));
|
||||||
|
$tipo = $tipoActual;
|
||||||
|
|
||||||
|
// Completa columnas de mañana (inicio y fin).
|
||||||
|
if ($tipoActual === 'AM') {
|
||||||
|
$DiasDeAtenciones[$fila][1] = $filaAtencion->horariocomienzo;
|
||||||
|
$DiasDeAtenciones[$fila][2] = $filaAtencion->horariofin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Completa columnas de tarde (inicio y fin).
|
||||||
|
if ($tipoActual === 'PM') {
|
||||||
|
$DiasDeAtenciones[$fila][3] = $filaAtencion->horariocomienzo;
|
||||||
|
$DiasDeAtenciones[$fila][4] = $filaAtencion->horariofin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trae los recesos de la agenda y los transforma a una matriz [dia_id, comienzo, fin].
|
||||||
|
$recesos = DB::table('horariosrecesos as r')
|
||||||
|
->join('diasdeatenciones as d', 'd.id', '=', 'r.diadeatencion_id')
|
||||||
|
->where('d.agenda_id', $agendaId)
|
||||||
|
->select('d.dia_id', 'r.comienzo', 'r.fin')
|
||||||
|
->orderBy('d.dia_id')
|
||||||
|
->get()
|
||||||
|
->map(function ($receso) {
|
||||||
|
return [
|
||||||
|
$receso->dia_id,
|
||||||
|
$receso->comienzo,
|
||||||
|
$receso->fin,
|
||||||
|
];
|
||||||
|
})
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
// Trae períodos de vacaciones y los transforma a una matriz [inicio, fin].
|
||||||
|
$vacaciones = DB::table('modosvacaciones')
|
||||||
|
->where('agenda_id', $agendaId)
|
||||||
|
->select('inicio', 'fin')
|
||||||
|
->orderBy('inicio')
|
||||||
|
->get()
|
||||||
|
->map(function ($vacacion) {
|
||||||
|
return [
|
||||||
|
$vacacion->inicio,
|
||||||
|
$vacacion->fin,
|
||||||
|
];
|
||||||
|
})
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
// Trae todos los feriados de la agenda y los transforma en una matriz de fechas.
|
||||||
|
$feriados = DB::table('feriados')
|
||||||
|
->where('agenda_id', $agendaId)
|
||||||
|
->select('fecha')
|
||||||
|
->orderBy('fecha')
|
||||||
|
->pluck('fecha')
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
|
||||||
|
// Normaliza preferencia horaria (AM, PM o INDISTINTO).
|
||||||
|
$tipopreferencia = strtoupper(trim((string) $tipopreferencia));
|
||||||
|
if (!in_array($tipopreferencia, ['AM', 'PM', 'INDISTINTO'], true)) {
|
||||||
|
$tipopreferencia = 'INDISTINTO';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convierte los días preferidos en texto a ids de 1 (lunes) a 7 (domingo).
|
||||||
|
$mapaDias = [
|
||||||
|
'lunes' => 1,
|
||||||
|
'martes' => 2,
|
||||||
|
'miercoles' => 3,
|
||||||
|
'jueves' => 4,
|
||||||
|
'viernes' => 5,
|
||||||
|
'sabado' => 6,
|
||||||
|
'domingo' => 7,
|
||||||
|
];
|
||||||
|
$diasPreferidosIds = [];
|
||||||
|
foreach ((array) $diasPreferencia as $diaPreferido) {
|
||||||
|
$diaNormalizado = strtolower(trim((string) $diaPreferido));
|
||||||
|
$diaNormalizado = strtr($diaNormalizado, [
|
||||||
|
'á' => 'a',
|
||||||
|
'é' => 'e',
|
||||||
|
'í' => 'i',
|
||||||
|
'ó' => 'o',
|
||||||
|
'ú' => 'u',
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (isset($mapaDias[$diaNormalizado])) {
|
||||||
|
$diasPreferidosIds[] = $mapaDias[$diaNormalizado];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$diasPreferidosIds = array_values(array_unique($diasPreferidosIds));
|
||||||
|
|
||||||
|
// Toma la duración de turno de la agenda; si no existe, usa 40 minutos.
|
||||||
|
$duracionTurno = (int) (self::where('id', $agendaId)->value('duracionturno') ?? 40);
|
||||||
|
if ($duracionTurno <= 0) {
|
||||||
|
$duracionTurno = 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define el punto de inicio de la búsqueda desde el día siguiente (para evitar asignar turnos inmediatos del día actual).
|
||||||
|
$baseTimestamp = strtotime('tomorrow');
|
||||||
|
|
||||||
|
// Crea índices rápidos para validar fechas bloqueadas y horarios ocupados.
|
||||||
|
$feriadosLookup = array_flip($feriados);
|
||||||
|
$recesosPorDia = [];
|
||||||
|
foreach ($recesos as $receso) {
|
||||||
|
$diaId = (int) $receso[0];
|
||||||
|
if (!isset($recesosPorDia[$diaId])) {
|
||||||
|
$recesosPorDia[$diaId] = [];
|
||||||
|
}
|
||||||
|
$recesosPorDia[$diaId][] = [$receso[1], $receso[2]];
|
||||||
|
}
|
||||||
|
|
||||||
|
$turnosOcupadosLookup = [];
|
||||||
|
$turnosOcupados = DB::table('turnos')
|
||||||
|
->where('agenda_id', $agendaId)
|
||||||
|
->where('inicio', '>=', date('Y-m-d', $baseTimestamp))
|
||||||
|
->select('inicio')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
foreach ($turnosOcupados as $turnoOcupado) {
|
||||||
|
$tsTurno = strtotime((string) $turnoOcupado->inicio);
|
||||||
|
if ($tsTurno === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$turnosOcupadosLookup[date('Y-m-d H:i:s', $tsTurno)] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Busca el primer turno disponible respetando tipo, días preferidos y reglas de agenda.
|
||||||
|
for ($offsetDias = 0; $offsetDias <= 120; $offsetDias++) {
|
||||||
|
$tsDia = strtotime(date('Y-m-d', $baseTimestamp) . ' +' . $offsetDias . ' day');
|
||||||
|
if ($tsDia === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$fechaIterada = date('Y-m-d', $tsDia);
|
||||||
|
$diaId = (int) date('N', $tsDia);
|
||||||
|
|
||||||
|
if (!empty($diasPreferidosIds) && !in_array($diaId, $diasPreferidosIds, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($feriadosLookup[$fechaIterada])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$enVacaciones = false;
|
||||||
|
foreach ($vacaciones as $vacacion) {
|
||||||
|
if ($fechaIterada >= $vacacion[0] && $fechaIterada <= $vacacion[1]) {
|
||||||
|
$enVacaciones = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($enVacaciones) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$filaDia = $DiasDeAtenciones[$diaId - 1] ?? null;
|
||||||
|
if (!$filaDia || $filaDia[0] === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ventanas = [];
|
||||||
|
if (($tipopreferencia === 'AM' || $tipopreferencia === 'INDISTINTO') && $filaDia[1] !== null && $filaDia[2] !== null) {
|
||||||
|
$ventanas[] = ['tipo' => 'AM', 'inicio' => $filaDia[1], 'fin' => $filaDia[2]];
|
||||||
|
}
|
||||||
|
if (($tipopreferencia === 'PM' || $tipopreferencia === 'INDISTINTO') && $filaDia[3] !== null && $filaDia[4] !== null) {
|
||||||
|
$ventanas[] = ['tipo' => 'PM', 'inicio' => $filaDia[3], 'fin' => $filaDia[4]];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($ventanas as $ventana) {
|
||||||
|
$inicioVentanaTs = strtotime($fechaIterada . ' ' . $ventana['inicio']);
|
||||||
|
$finVentanaTs = strtotime($fechaIterada . ' ' . $ventana['fin']);
|
||||||
|
if ($inicioVentanaTs === false || $finVentanaTs === false || $inicioVentanaTs >= $finVentanaTs) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($slotTs = $inicioVentanaTs; ($slotTs + ($duracionTurno * 60)) <= $finVentanaTs; $slotTs += ($duracionTurno * 60)) {
|
||||||
|
$slotFecha = date('Y-m-d', $slotTs);
|
||||||
|
$slotHora = date('H:i:s', $slotTs);
|
||||||
|
$slotDateTime = $slotFecha . ' ' . $slotHora;
|
||||||
|
$slotFinTs = $slotTs + ($duracionTurno * 60);
|
||||||
|
|
||||||
|
if (isset($turnosOcupadosLookup[$slotDateTime])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$solapaReceso = false;
|
||||||
|
foreach ($recesosPorDia[$diaId] ?? [] as $recesoDia) {
|
||||||
|
$recesoInicioTs = strtotime($slotFecha . ' ' . $recesoDia[0]);
|
||||||
|
$recesoFinTs = strtotime($slotFecha . ' ' . $recesoDia[1]);
|
||||||
|
if ($recesoInicioTs === false || $recesoFinTs === false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($slotTs < $recesoFinTs && $slotFinTs > $recesoInicioTs) {
|
||||||
|
$solapaReceso = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($solapaReceso) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->estaDisponible($slotFecha, $slotHora, $agendaId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$turnoMasCercano = [
|
||||||
|
'fecha' => $slotFecha,
|
||||||
|
'hora' => $slotHora,
|
||||||
|
'fechaHora' => $slotDateTime,
|
||||||
|
'tipo' => $ventana['tipo'],
|
||||||
|
'duracionMinutos' => $duracionTurno,
|
||||||
|
];
|
||||||
|
break 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Devuelve solo la fecha/hora del turno más cercano o null si no hay disponibilidad.
|
||||||
|
return $turnoMasCercano['fechaHora'] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function crearDiaDeAtencion($diaId, $horarioComienzo, $horarioFin, $tipo)
|
||||||
|
{
|
||||||
|
$tipoNormalizado = strtoupper(trim((string) $tipo));
|
||||||
|
if (!in_array($tipoNormalizado, ['AM', 'PM'], true)) {
|
||||||
|
throw new \InvalidArgumentException('El tipo debe ser AM o PM.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$horarioComienzoNormalizado = $this->normalizarHora($horarioComienzo, 'horarioComienzo');
|
||||||
|
$horarioFinNormalizado = $this->normalizarHora($horarioFin, 'horarioFin');
|
||||||
|
|
||||||
|
if (strtotime('1970-01-01 ' . $horarioComienzoNormalizado) >= strtotime('1970-01-01 ' . $horarioFinNormalizado)) {
|
||||||
|
throw new \InvalidArgumentException('horarioComienzo debe ser menor que horarioFin.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return DB::transaction(function () use ($diaId, $tipoNormalizado, $horarioComienzoNormalizado, $horarioFinNormalizado) {
|
||||||
|
$diaAtencion = DiaDeAtencion::firstOrCreate(
|
||||||
|
['agenda_id' => $this->id, 'dia_id' => $diaId],
|
||||||
|
['descripcion' => 'Dia de atencion']
|
||||||
|
);
|
||||||
|
|
||||||
|
HorarioDeAtencion::updateOrCreate(
|
||||||
|
[
|
||||||
|
'diadeatencion_id' => $diaAtencion->id,
|
||||||
|
'tipo' => $tipoNormalizado,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'horariocomienzo' => $horarioComienzoNormalizado,
|
||||||
|
'horariofin' => $horarioFinNormalizado,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
return $diaAtencion;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function eliminarDiaDeAtencion($diaId)
|
||||||
|
{
|
||||||
|
return DB::transaction(function () use ($diaId) {
|
||||||
|
$diaAtencion = DiaDeAtencion::where('agenda_id', $this->id)->where('dia_id', $diaId)->first();
|
||||||
|
if ($diaAtencion) {
|
||||||
|
HorarioDeAtencion::where('diadeatencion_id', $diaAtencion->id)->delete();
|
||||||
|
HorarioReceso::where('diadeatencion_id', $diaAtencion->id)->delete();
|
||||||
|
$diaAtencion->delete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function crearModoVacaciones($inicio, $fin, $descripcion = null)
|
||||||
|
{
|
||||||
|
return ModoVacaciones::create([
|
||||||
|
'agenda_id' => $this->id,
|
||||||
|
'inicio' => $inicio,
|
||||||
|
'fin' => $fin,
|
||||||
|
'descripcion' => $descripcion,
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function eliminarModoVacaciones($id)
|
||||||
|
{
|
||||||
|
$modoVacaciones = ModoVacaciones::where('agenda_id', $this->id)->where('id', $id)->first();
|
||||||
|
if ($modoVacaciones) {
|
||||||
|
$modoVacaciones->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function crearFeriado($fecha, $descripcion = null)
|
||||||
|
{
|
||||||
|
return Feriado::create([
|
||||||
|
'agenda_id' => $this->id,
|
||||||
|
'fecha' => $fecha,
|
||||||
|
'descripcion' => $descripcion,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function eliminarFeriado($id)
|
||||||
|
{
|
||||||
|
$feriado = Feriado::where('agenda_id', $this->id)->where('id', $id)->first();
|
||||||
|
if ($feriado) {
|
||||||
|
$feriado->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function guardarTurno($inicio, $correo = null, $nombrecompleto = null, $descripcion = null, $clienteId = null, $estadoturnoId = null, $profesionalId = null, $servicioId = null, $modalidadId = null, $agendaId = null)
|
||||||
|
{
|
||||||
|
$inicioNormalizado = $this->normalizarDatetime($inicio, 'inicio');
|
||||||
|
|
||||||
|
$turno = Turno::create([
|
||||||
|
'agenda_id' => $agendaId ?? $this->id,
|
||||||
|
'correo' => $correo,
|
||||||
|
'nombrecompleto' => $nombrecompleto,
|
||||||
|
'descripcion' => $descripcion,
|
||||||
|
'cliente_id' => $clienteId,
|
||||||
|
'estadoturno_id' => $estadoturnoId,
|
||||||
|
'profesional_id' => $profesionalId,
|
||||||
|
'servicio_id' => $servicioId,
|
||||||
|
'modalidad_id' => $modalidadId,
|
||||||
|
'inicio' => $inicioNormalizado,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $turno;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function normalizarHora($valor, $campo)
|
||||||
|
{
|
||||||
|
if ($valor instanceof \DateTimeInterface) {
|
||||||
|
return $valor->format('H:i:s');
|
||||||
|
}
|
||||||
|
|
||||||
|
$timestamp = strtotime(trim((string) $valor));
|
||||||
|
if ($timestamp === false) {
|
||||||
|
throw new \InvalidArgumentException('El campo ' . $campo . ' debe ser una hora valida.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return date('H:i:s', $timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function normalizarDatetime($valor, $campo)
|
||||||
|
{
|
||||||
|
if ($valor instanceof \DateTimeInterface) {
|
||||||
|
return $valor->format('Y-m-d H:i:s');
|
||||||
|
}
|
||||||
|
|
||||||
|
$timestamp = strtotime(trim((string) $valor));
|
||||||
|
if ($timestamp === false) {
|
||||||
|
throw new \InvalidArgumentException('El campo ' . $campo . ' debe ser una fecha y hora valida.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return date('Y-m-d H:i:s', $timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function confirmarTurno($id)
|
||||||
|
{
|
||||||
|
$turno = $this->turno()->find($id);
|
||||||
|
if (!$turno) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $turno->confirmar();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cancelarTurno($id)
|
||||||
|
{
|
||||||
|
$turno = $this->turno()->find($id);
|
||||||
|
if (!$turno) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $turno->cancelar();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reprogramarTurno($id)
|
||||||
|
{
|
||||||
|
$turno = $this->turno()->find($id);
|
||||||
|
if (!$turno) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $turno->reprogramar();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function marcarClienteAusente($id)
|
||||||
|
{
|
||||||
|
$turno = $this->turno()->find($id);
|
||||||
|
if (!$turno) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $turno->clienteAusente();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function marcarClientePresente($id)
|
||||||
|
{
|
||||||
|
$turno = $this->turno()->find($id);
|
||||||
|
if (!$turno) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $turno->clientePresente();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ use HasFactory;
|
|||||||
}
|
}
|
||||||
public function horariosAtenciones()
|
public function horariosAtenciones()
|
||||||
{
|
{
|
||||||
return $this->hasMany(HorarioAtencion::class, 'diadeatencion_id', 'id');
|
return $this->hasMany(HorarioDeAtencion::class, 'diadeatencion_id', 'id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class Formulario extends Model
|
|||||||
return $this->belongsToMany(DiaPreferencia::class, 'formularios_diaspreferidos', 'formulario_id', 'diapreferencia_id');
|
return $this->belongsToMany(DiaPreferencia::class, 'formularios_diaspreferidos', 'formulario_id', 'diapreferencia_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function horariopreferido()
|
public function horariosPreferidos()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(HorarioPreferencia::class, 'formularios_horariospreferidos', 'formulario_id', 'horariopreferencia_id');
|
return $this->belongsToMany(HorarioPreferencia::class, 'formularios_horariospreferidos', 'formulario_id', 'horariopreferencia_id');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ class HorarioDeAtencion extends Model
|
|||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'horariocomienzo',
|
'horariocomienzo',
|
||||||
'horariofin',
|
'horariofin',
|
||||||
|
'tipo',
|
||||||
'diadeatencion_id',
|
'diadeatencion_id',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -19,4 +19,9 @@ class Modalidad extends Model
|
|||||||
return $this->hasMany(Formulario::class, 'modalidad_id', 'id');
|
return $this->hasMany(Formulario::class, 'modalidad_id', 'id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function turnos()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Turno::class, 'modalidad_id', 'id');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ class Turno extends Model
|
|||||||
'agenda_id',
|
'agenda_id',
|
||||||
'profesional_id',
|
'profesional_id',
|
||||||
'servicio_id',
|
'servicio_id',
|
||||||
|
'modalidad_id',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'inicio' => 'datetime',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function estadoTurno()
|
public function estadoTurno()
|
||||||
@@ -45,4 +50,45 @@ class Turno extends Model
|
|||||||
{
|
{
|
||||||
return $this->belongsTo(Servicio::class, 'servicio_id', 'id');
|
return $this->belongsTo(Servicio::class, 'servicio_id', 'id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function modalidad()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Modalidad::class, 'modalidad_id', 'id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function confirmar()
|
||||||
|
{
|
||||||
|
$this->estadoturno_id = 1; // ID del estado Confirmado (dato maestro)
|
||||||
|
$this->save();
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cancelar()
|
||||||
|
{
|
||||||
|
$this->estadoturno_id = 2; // ID del estado Cancelado (dato maestro)
|
||||||
|
$this->save();
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reprogramar()
|
||||||
|
{
|
||||||
|
$this->estadoturno_id = 3; // ID del estado Reprogramado (dato maestro)
|
||||||
|
$this->save();
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clienteAusente()
|
||||||
|
{
|
||||||
|
$this->estadoturno_id = 4; // ID del estado Cliente Ausente (dato maestro)
|
||||||
|
$this->save();
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clientePresente()
|
||||||
|
{
|
||||||
|
$this->estadoturno_id = 5; // ID del estado Cliente Presente (dato maestro)
|
||||||
|
$this->save();
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ return new class extends Migration
|
|||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
$table->time('horariocomienzo');
|
$table->time('horariocomienzo');
|
||||||
$table->time('horariofin');
|
$table->time('horariofin');
|
||||||
|
$table->string('tipo'); // AM o PM
|
||||||
$table->foreignId('diadeatencion_id')
|
$table->foreignId('diadeatencion_id')
|
||||||
->constrained('diasdeatenciones');
|
->constrained('diasdeatenciones');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ return new class extends Migration
|
|||||||
$table->foreignId('servicio_id')
|
$table->foreignId('servicio_id')
|
||||||
->constrained('servicios')
|
->constrained('servicios')
|
||||||
->onDelete('cascade');
|
->onDelete('cascade');
|
||||||
|
|
||||||
|
$table->foreignId('modalidad_id')
|
||||||
|
->constrained('modalidades')
|
||||||
|
->onDelete('cascade');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ class EstadoTurnoSeeder extends Seeder
|
|||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
$estados = [
|
$estados = [
|
||||||
['descripcion' => 'Pendiente'],
|
|
||||||
['descripcion' => 'Confirmado'],
|
['descripcion' => 'Confirmado'],
|
||||||
['descripcion' => 'Rechazado'],
|
|
||||||
['descripcion' => 'Cancelado'],
|
['descripcion' => 'Cancelado'],
|
||||||
['descripcion' => 'Reprogramado']
|
['descripcion' => 'Reprogramado'],
|
||||||
|
['descripcion' => 'Cliente Ausente'],
|
||||||
|
['descripcion' => 'Cliente Presente']
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach ($estados as $estado){
|
foreach ($estados as $estado){
|
||||||
|
|||||||
Generated
+1836
File diff suppressed because it is too large
Load Diff
+4
-2
@@ -7,11 +7,13 @@
|
|||||||
"dev": "vite"
|
"dev": "vite"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/vite": "^4.0.0",
|
|
||||||
"axios": "^1.11.0",
|
"axios": "^1.11.0",
|
||||||
"concurrently": "^9.0.1",
|
"concurrently": "^9.0.1",
|
||||||
"laravel-vite-plugin": "^2.0.0",
|
"laravel-vite-plugin": "^2.0.0",
|
||||||
"tailwindcss": "^4.0.0",
|
|
||||||
"vite": "^7.0.7"
|
"vite": "^7.0.7"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@popperjs/core": "^2.11.8",
|
||||||
|
"bootstrap": "^5.3.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use App\Models\Profesional;
|
||||||
|
use App\Models\CredencialProfesional;
|
||||||
|
use App\Models\Foto;
|
||||||
|
use App\Models\Persona;
|
||||||
|
use Illuminate\Contracts\Console\Kernel;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
||||||
|
require __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
$app = require __DIR__ . '/bootstrap/app.php';
|
||||||
|
$app->make(Kernel::class)->bootstrap();
|
||||||
|
|
||||||
|
$usuario = env('PROFESIONAL_USUARIO', 'profesional');
|
||||||
|
$passwordPlano = env('PROFESIONAL_PASSWORD', 'profesional1234');
|
||||||
|
$correo = env('PROFESIONAL_CORREO', 'profesional@abogadaslitoral.com');
|
||||||
|
$dni = env('PROFESIONAL_DNI', '40000000');
|
||||||
|
$nombre = env('PROFESIONAL_NOMBRE', 'Usuario');
|
||||||
|
$apellido = env('PROFESIONAL_APELLIDO', 'Profesional');
|
||||||
|
$cuil = env('PROFESIONAL_CUIL', '20-40000000-0');
|
||||||
|
$fechaNac = env('PROFESIONAL_FECHANAC', '2026-01-01');
|
||||||
|
|
||||||
|
try {
|
||||||
|
DB::transaction(function () use ($usuario, $passwordPlano, $correo, $dni, $nombre, $apellido, $cuil, $fechaNac): void {
|
||||||
|
$foto = Foto::firstOrCreate(
|
||||||
|
['ruta' => 'avatars/profesional-default.png'],
|
||||||
|
[
|
||||||
|
'extension' => 'png',
|
||||||
|
'nombre' => 'profesional-default',
|
||||||
|
'mime_type' => 'image/png',
|
||||||
|
'tamanio_bytes' => 0,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$persona = Persona::updateOrCreate(
|
||||||
|
['dni' => $dni],
|
||||||
|
[
|
||||||
|
'nombre' => $nombre,
|
||||||
|
'apellido' => $apellido,
|
||||||
|
'cuil' => $cuil,
|
||||||
|
'fechanac' => $fechaNac,
|
||||||
|
'foto_id' => $foto->id,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$credencial = CredencialProfesional::updateOrCreate(
|
||||||
|
['usuario' => $usuario],
|
||||||
|
[
|
||||||
|
'contra' => Hash::make($passwordPlano),
|
||||||
|
'rol' => 'PROFESIONAL',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
Profesional::updateOrCreate(
|
||||||
|
['dni' => $dni, 'correo' => $correo],
|
||||||
|
[
|
||||||
|
'persona_id' => $persona->id,
|
||||||
|
'credencialprofesional_id' => $credencial->id,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
echo "Profesional creado correctamente." . PHP_EOL;
|
||||||
|
echo "Usuario: {$usuario}" . PHP_EOL;
|
||||||
|
echo "Correo: {$correo}" . PHP_EOL;
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
fwrite(STDERR, 'Error al crear profesional: ' . $e->getMessage() . PHP_EOL);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
+1
-11
@@ -1,11 +1 @@
|
|||||||
@import 'tailwindcss';
|
@import 'bootstrap/dist/css/bootstrap.min.css';
|
||||||
|
|
||||||
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
|
|
||||||
@source '../../storage/framework/views/*.php';
|
|
||||||
@source '../**/*.blade.php';
|
|
||||||
@source '../**/*.js';
|
|
||||||
|
|
||||||
@theme {
|
|
||||||
--font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
|
||||||
'Segoe UI Symbol', 'Noto Color Emoji';
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
import './bootstrap';
|
import './bootstrap';
|
||||||
|
import 'bootstrap';
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Login Cliente</title>
|
||||||
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
|
</head>
|
||||||
|
<body class="bg-light">
|
||||||
|
<main class="container py-5">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-12 col-sm-10 col-md-8 col-lg-5">
|
||||||
|
<div class="card shadow-sm border-0">
|
||||||
|
<div class="card-body p-4 p-md-5">
|
||||||
|
<h1 class="h4 mb-1">Login de Cliente</h1>
|
||||||
|
<p class="text-muted mb-4">Ingresa con tu correo y contraseña</p>
|
||||||
|
|
||||||
|
@if (session('login_error'))
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
{{ session('login_error') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if (session('login_success'))
|
||||||
|
<div class="alert alert-success" role="alert">
|
||||||
|
{{ session('login_success') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<form method="POST" action="/login/cliente" class="d-grid gap-3">
|
||||||
|
@csrf
|
||||||
|
<div>
|
||||||
|
<label class="form-label" for="correo">Correo</label>
|
||||||
|
<input class="form-control" id="correo" name="correo" type="email" value="{{ old('correo') }}" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="form-label" for="contra">Contraseña</label>
|
||||||
|
<input class="form-control" id="contra" name="contra" type="password" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn btn-primary" type="submit">Ingresar</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<hr class="my-4">
|
||||||
|
<p class="mb-0 text-center">
|
||||||
|
¿Sos profesional o administrador?
|
||||||
|
<a href="/login/personal">Ir a login de personal</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Login Personal</title>
|
||||||
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
|
</head>
|
||||||
|
<body class="bg-light">
|
||||||
|
<main class="container py-5">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-12 col-sm-10 col-md-8 col-lg-5">
|
||||||
|
<div class="card shadow-sm border-0">
|
||||||
|
<div class="card-body p-4 p-md-5">
|
||||||
|
<h1 class="h4 mb-1">Login de Personal</h1>
|
||||||
|
<p class="text-muted mb-4">Acceso para profesionales y administradores.</p>
|
||||||
|
|
||||||
|
@if (session('login_error'))
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
{{ session('login_error') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if (session('login_success'))
|
||||||
|
<div class="alert alert-success" role="alert">
|
||||||
|
{{ session('login_success') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<form method="POST" action="/login/personal" class="d-grid gap-3">
|
||||||
|
@csrf
|
||||||
|
<div>
|
||||||
|
<label class="form-label" for="usuario">Usuario</label>
|
||||||
|
<input class="form-control" id="usuario" name="usuario" type="text" value="{{ old('usuario') }}" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="form-label" for="contra">Contraseña</label>
|
||||||
|
<input class="form-control" id="contra" name="contra" type="password" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn btn-primary" type="submit">Ingresar</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<hr class="my-4">
|
||||||
|
<p class="mb-0 text-center">
|
||||||
|
¿Sos cliente?
|
||||||
|
<a href="/login/cliente">Ir a login de cliente</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-center mt-4">
|
||||||
|
<a href="/" class="btn btn-secondary">Agregar Administrador</a>
|
||||||
|
</div>
|
||||||
|
<div class="text-center mt-2">
|
||||||
|
<a href="/" class="btn btn-secondary">Agregar Profesional</a>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>@yield('title', 'Abogadas Litoral')</title>
|
||||||
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--site-navbar-bg: #f4ece5;
|
||||||
|
--site-navbar-text: #2f2721;
|
||||||
|
--site-navbar-hover: #1f1915;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-navbar {
|
||||||
|
background-color: var(--site-navbar-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-navbar .navbar-brand,
|
||||||
|
.site-navbar .nav-link,
|
||||||
|
.site-navbar .btn-link {
|
||||||
|
color: var(--site-navbar-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-navbar .nav-link:hover,
|
||||||
|
.site-navbar .btn-link:hover {
|
||||||
|
color: var(--site-navbar-hover);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="d-flex flex-column min-vh-100 bg-light">
|
||||||
|
<header class="border-bottom">
|
||||||
|
<nav class="navbar navbar-expand-lg site-navbar">
|
||||||
|
<div class="container">
|
||||||
|
<a class="navbar-brand d-flex align-items-center" href="/">
|
||||||
|
<div class="border rounded d-flex align-items-center justify-content-center bg-white" style="width: 120px; height: 48px;">
|
||||||
|
<span class="small text-muted">LOGO</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#menuPrincipal" aria-controls="menuPrincipal" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="collapse navbar-collapse" id="menuPrincipal">
|
||||||
|
<ul class="navbar-nav mx-auto mb-2 mb-lg-0">
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#servicios">Servicios</a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#quienes-somos">Quienes Somos</a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#equipo">Equipo</a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#ubicacion">Ubicacion</a></li>
|
||||||
|
<li class="nav-item"><a class="nav-link" href="#formulario">Formulario</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a class="btn btn-primary" href="/login/personal">Iniciar Sesion</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
@yield('content')
|
||||||
|
|
||||||
|
<footer class="bg-white border-top mt-auto py-4">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row g-4 align-items-start text-center">
|
||||||
|
<div class="col-12 col-md-4">
|
||||||
|
<div class="border rounded d-inline-flex align-items-center justify-content-center" style="width: 120px; height: 48px;">
|
||||||
|
<span class="small text-muted">LOGO</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-4 text-center">
|
||||||
|
<h3 class="h6 mb-2">Redes Sociales</h3>
|
||||||
|
<p class="mb-2">Instagram | Facebook | LinkedIn</p>
|
||||||
|
<p class="small text-muted mb-0">Desarrollado por Luciano Belini</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-4">
|
||||||
|
<h3 class="h6 mb-2">Ubicacion</h3>
|
||||||
|
<p class="mb-0">Direccion pendiente de definir</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,87 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
use App\Http\Controllers\AccionLogController;
|
||||||
|
use App\Http\Controllers\AdministradorController;
|
||||||
|
use App\Http\Controllers\AgendaController;
|
||||||
|
use App\Http\Controllers\AuthController;
|
||||||
|
use App\Http\Controllers\BajaController;
|
||||||
|
use App\Http\Controllers\BugController;
|
||||||
|
use App\Http\Controllers\ClienteController;
|
||||||
|
use App\Http\Controllers\ContenidoWebController;
|
||||||
|
use App\Http\Controllers\CredencialClienteController;
|
||||||
|
use App\Http\Controllers\CredencialProfesionalController;
|
||||||
|
use App\Http\Controllers\DiaController;
|
||||||
|
use App\Http\Controllers\DiaDeAtencionController;
|
||||||
|
use App\Http\Controllers\DiaPreferenciaController;
|
||||||
|
use App\Http\Controllers\DocumentacionClienteController;
|
||||||
|
use App\Http\Controllers\ErrorController;
|
||||||
|
use App\Http\Controllers\EstadoProfesionalController;
|
||||||
|
use App\Http\Controllers\EstadoTurnoController;
|
||||||
|
use App\Http\Controllers\FeriadoController;
|
||||||
|
use App\Http\Controllers\FormularioController;
|
||||||
|
use App\Http\Controllers\FotoBugController;
|
||||||
|
use App\Http\Controllers\FotoController;
|
||||||
|
use App\Http\Controllers\HorarioDeAtencionController;
|
||||||
|
use App\Http\Controllers\HorarioPreferenciaController;
|
||||||
|
use App\Http\Controllers\HorarioRecesoController;
|
||||||
|
use App\Http\Controllers\LogSeguridadController;
|
||||||
|
use App\Http\Controllers\ModalidadController;
|
||||||
|
use App\Http\Controllers\ModoVacacionesController;
|
||||||
|
use App\Http\Controllers\PersonaController;
|
||||||
|
use App\Http\Controllers\ProfesionController;
|
||||||
|
use App\Http\Controllers\ProfesionalController;
|
||||||
|
use App\Http\Controllers\ServicioController;
|
||||||
|
use App\Http\Controllers\TelefonoController;
|
||||||
|
use App\Http\Controllers\TurnoController;
|
||||||
|
use App\Http\Controllers\UbicacionController;
|
||||||
|
use App\Http\Controllers\UserController;
|
||||||
|
|
||||||
|
Route::middleware('api')->group(function () {
|
||||||
|
Route::post('auth/login/cliente', [AuthController::class, 'loginCliente']);
|
||||||
|
Route::post('auth/login/personal', [AuthController::class, 'loginPersonal']);
|
||||||
|
Route::post('auth/login', [AuthController::class, 'login']);
|
||||||
|
Route::post('auth/logout', [AuthController::class, 'logout']);
|
||||||
|
|
||||||
|
// Rutas API Resource estándar
|
||||||
|
Route::apiResource('accioneslogs', AccionLogController::class);
|
||||||
|
Route::apiResource('administradores', AdministradorController::class);
|
||||||
|
Route::apiResource('agendas', AgendaController::class);
|
||||||
|
Route::apiResource('bajas', BajaController::class);
|
||||||
|
Route::apiResource('bugs', BugController::class);
|
||||||
|
Route::apiResource('clientes', ClienteController::class);
|
||||||
|
Route::apiResource('contenidosweb', ContenidoWebController::class);
|
||||||
|
Route::apiResource('credencialesclientes', CredencialClienteController::class);
|
||||||
|
Route::apiResource('credencialesprofesionales', CredencialProfesionalController::class);
|
||||||
|
Route::apiResource('dias', DiaController::class);
|
||||||
|
Route::apiResource('diasdeatenciones', DiaDeAtencionController::class);
|
||||||
|
Route::apiResource('diaspreferencias', DiaPreferenciaController::class);
|
||||||
|
Route::apiResource('documentacionesclientes', DocumentacionClienteController::class);
|
||||||
|
Route::apiResource('errores', ErrorController::class);
|
||||||
|
Route::apiResource('estadosprofesionales', EstadoProfesionalController::class);
|
||||||
|
Route::apiResource('estadosturnos', EstadoTurnoController::class);
|
||||||
|
Route::apiResource('feriados', FeriadoController::class);
|
||||||
|
Route::apiResource('formularios', FormularioController::class);
|
||||||
|
Route::apiResource('fotosbugs', FotoBugController::class);
|
||||||
|
Route::apiResource('fotos', FotoController::class);
|
||||||
|
Route::apiResource('horariosatenciones', HorarioDeAtencionController::class);
|
||||||
|
Route::apiResource('horariospreferencias', HorarioPreferenciaController::class);
|
||||||
|
Route::apiResource('horariosrecesos', HorarioRecesoController::class);
|
||||||
|
Route::apiResource('logseguridades', LogSeguridadController::class);
|
||||||
|
Route::apiResource('modalidades', ModalidadController::class);
|
||||||
|
Route::apiResource('modosvacaciones', ModoVacacionesController::class);
|
||||||
|
Route::apiResource('personas', PersonaController::class);
|
||||||
|
Route::apiResource('profesiones', ProfesionController::class);
|
||||||
|
Route::apiResource('profesionales', ProfesionalController::class);
|
||||||
|
Route::apiResource('servicios', ServicioController::class);
|
||||||
|
Route::apiResource('telefonos', TelefonoController::class);
|
||||||
|
Route::apiResource('turnos', TurnoController::class);
|
||||||
|
Route::post('turnos/{turno}/confirmar', [TurnoController::class, 'confirmar']);
|
||||||
|
Route::post('turnos/{turno}/cancelar', [TurnoController::class, 'cancelar']);
|
||||||
|
Route::post('turnos/{turno}/reprogramar', [TurnoController::class, 'reprogramar']);
|
||||||
|
Route::post('turnos/{turno}/cliente-ausente', [TurnoController::class, 'clienteAusente']);
|
||||||
|
Route::post('turnos/{turno}/cliente-presente', [TurnoController::class, 'clientePresente']);
|
||||||
|
Route::apiResource('ubicaciones', UbicacionController::class);
|
||||||
|
Route::apiResource('users', UserController::class);
|
||||||
|
});
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Http\Controllers\AuthController;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
Route::get('/', function () {
|
Route::get('/', function () {
|
||||||
return view('welcome');
|
return view('welcome');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Route::view('/login/cliente', 'auth.login-cliente');
|
||||||
|
Route::view('/login/personal', 'auth.login-personal');
|
||||||
|
Route::post('/login/cliente', [AuthController::class, 'loginClienteWeb']);
|
||||||
|
Route::post('/login/personal', [AuthController::class, 'loginPersonalWeb']);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import laravel from 'laravel-vite-plugin';
|
import laravel from 'laravel-vite-plugin';
|
||||||
import tailwindcss from '@tailwindcss/vite';
|
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
@@ -8,7 +7,6 @@ export default defineConfig({
|
|||||||
input: ['resources/css/app.css', 'resources/js/app.js'],
|
input: ['resources/css/app.css', 'resources/js/app.js'],
|
||||||
refresh: true,
|
refresh: true,
|
||||||
}),
|
}),
|
||||||
tailwindcss(),
|
|
||||||
],
|
],
|
||||||
server: {
|
server: {
|
||||||
watch: {
|
watch: {
|
||||||
|
|||||||
Reference in New Issue
Block a user