Comence con las vistas. El modelo de agenda está practicamente terminado

This commit is contained in:
Lucho
2026-03-25 13:51:02 -03:00
parent 6c2c300d6e
commit 25f4b73b68
29 changed files with 3267 additions and 319 deletions
View File
+2 -1
View File
@@ -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
_________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________
+73
View File
@@ -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);
} }
} }
+237
View File
@@ -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);
}
}
+21 -18
View File
@@ -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);
} }
} }
+53 -8
View File
@@ -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);
}
} }
+55
View File
@@ -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);
}
} }
+506
View File
@@ -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();
}
} }
+1 -1
View File
@@ -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');
} }
+1 -1
View File
@@ -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');
} }
+1
View File
@@ -13,6 +13,7 @@ class HorarioDeAtencion extends Model
protected $fillable = [ protected $fillable = [
'horariocomienzo', 'horariocomienzo',
'horariofin', 'horariofin',
'tipo',
'diadeatencion_id', 'diadeatencion_id',
]; ];
+5
View File
@@ -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');
}
} }
+46
View File
@@ -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');
}); });
} }
+3 -3
View File
@@ -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){
+1836
View File
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -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"
} }
} }
+73
View File
@@ -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
View File
@@ -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
View File
@@ -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>
+85
View File
@@ -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
+87
View File
@@ -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);
});
+6
View File
@@ -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']);
-2
View File
@@ -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: {