Actualización de modelos, controladores y middleware
This commit is contained in:
@@ -2,12 +2,15 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\AccionLog;
|
||||||
use App\Models\CredencialCliente;
|
use App\Models\CredencialCliente;
|
||||||
use App\Models\CredencialProfesional;
|
use App\Models\CredencialProfesional;
|
||||||
|
use App\Models\LogSeguridad;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Support\Facades\RateLimiter;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class AuthController extends Controller
|
class AuthController extends Controller
|
||||||
@@ -17,24 +20,65 @@ class AuthController extends Controller
|
|||||||
$request->validate([
|
$request->validate([
|
||||||
'correo' => ['required', 'string'],
|
'correo' => ['required', 'string'],
|
||||||
'contra' => ['required', 'string'],
|
'contra' => ['required', 'string'],
|
||||||
|
'website' => ['nullable', 'string', 'max:255'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if (!$this->honeypotValido($request)) {
|
||||||
|
return back()
|
||||||
|
->withInput($request->except(['contra', 'website']))
|
||||||
|
->withErrors(['login_error' => 'No se pudo procesar el inicio de sesión.']);
|
||||||
|
}
|
||||||
|
|
||||||
$correo = trim((string) $request->input('correo'));
|
$correo = trim((string) $request->input('correo'));
|
||||||
$contra = (string) $request->input('contra');
|
$contra = (string) $request->input('contra');
|
||||||
|
|
||||||
$credencial = CredencialCliente::where('correo', $correo)->first();
|
$credencial = CredencialCliente::with('cliente.persona.telefonos')->where('correo', $correo)->first();
|
||||||
if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
|
if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
|
||||||
return back()
|
return back()
|
||||||
->withInput($request->except('contra'))
|
->withInput($request->except('contra'))
|
||||||
->with('login_error', 'Usuario o contraseña incorrectos.');
|
->with('login_error', 'Usuario o contraseña incorrectos.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((int) ($credencial->cliente?->baja_id ?? 0) !== 1) {
|
||||||
|
return back()
|
||||||
|
->withInput($request->except('contra'))
|
||||||
|
->with('login_error', 'No es posible iniciar sesion. Comuníquese con un profesional de Abogadas del Litoral');
|
||||||
|
}
|
||||||
|
|
||||||
$token = Str::random(64);
|
$token = Str::random(64);
|
||||||
$credencial->token = $token;
|
$credencial->token = $token;
|
||||||
$credencial->fecha_hora = now();
|
$credencial->fecha_hora = now();
|
||||||
$credencial->save();
|
$credencial->save();
|
||||||
|
|
||||||
return back()->with('login_success', 'Login exitoso.');
|
$personaCliente = $credencial->cliente?->persona;
|
||||||
|
$nombreCliente = trim((string) ($personaCliente?->nombre ?? ''));
|
||||||
|
$apellidoCliente = trim((string) ($personaCliente?->apellido ?? ''));
|
||||||
|
$celularCliente = trim((string) ($personaCliente?->telefonos?->first()?->telefono ?? ''));
|
||||||
|
|
||||||
|
$request->session()->regenerate();
|
||||||
|
$request->session()->regenerateToken();
|
||||||
|
|
||||||
|
$request->session()->put([
|
||||||
|
'cliente_auth' => true,
|
||||||
|
'cliente_id' => (int) ($credencial->cliente?->id ?? 0),
|
||||||
|
'cliente_token' => $token,
|
||||||
|
'cliente_correo' => (string) $credencial->correo,
|
||||||
|
'cliente_nombre' => $nombreCliente !== '' ? $nombreCliente : (string) $credencial->correo,
|
||||||
|
'cliente_apellido' => $apellidoCliente,
|
||||||
|
'cliente_celular' => $celularCliente,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->registrarAccionSeguridad(
|
||||||
|
(int) ($credencial->cliente?->persona_id ?? 0),
|
||||||
|
'CLIENTE',
|
||||||
|
(string) $request->ip(),
|
||||||
|
'Inició sesión',
|
||||||
|
'El cliente ID ' . (int) ($credencial->cliente?->id ?? 0) . ' inició sesión.'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->limpiarThrottle($request, 'login-cliente-web');
|
||||||
|
|
||||||
|
return redirect('/cliente/dashboard')->with('login_success', 'Login exitoso.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loginPersonalWeb(Request $request): RedirectResponse
|
public function loginPersonalWeb(Request $request): RedirectResponse
|
||||||
@@ -42,24 +86,75 @@ class AuthController extends Controller
|
|||||||
$request->validate([
|
$request->validate([
|
||||||
'usuario' => ['required', 'string'],
|
'usuario' => ['required', 'string'],
|
||||||
'contra' => ['required', 'string'],
|
'contra' => ['required', 'string'],
|
||||||
|
'website' => ['nullable', 'string', 'max:255'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if (!$this->honeypotValido($request)) {
|
||||||
|
return back()
|
||||||
|
->withInput($request->except(['contra', 'website']))
|
||||||
|
->withErrors(['login_error' => 'No se pudo procesar el inicio de sesión.']);
|
||||||
|
}
|
||||||
|
|
||||||
$usuario = trim((string) $request->input('usuario'));
|
$usuario = trim((string) $request->input('usuario'));
|
||||||
$contra = (string) $request->input('contra');
|
$contra = (string) $request->input('contra');
|
||||||
|
|
||||||
$credencial = CredencialProfesional::where('usuario', $usuario)->first();
|
$credencial = CredencialProfesional::with(['administrador.persona', 'profesional.persona'])
|
||||||
|
->where('usuario', $usuario)
|
||||||
|
->first();
|
||||||
if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
|
if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
|
||||||
return back()
|
return back()
|
||||||
->withInput($request->except('contra'))
|
->withInput($request->except('contra'))
|
||||||
->with('login_error', 'Usuario o contraseña incorrectos.');
|
->with('login_error', 'Usuario o contraseña incorrectos.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strtoupper((string) $credencial->rol) !== 'ADMIN' && (int) ($credencial->profesional?->baja_id ?? 0) !== 1) {
|
||||||
|
return back()
|
||||||
|
->withInput($request->except('contra'))
|
||||||
|
->with('login_error', 'Usted está dado de baja. Comuníquese con el administrador');
|
||||||
|
}
|
||||||
|
|
||||||
$token = Str::random(64);
|
$token = Str::random(64);
|
||||||
$credencial->token = $token;
|
$credencial->token = $token;
|
||||||
$credencial->fecha_hora = now();
|
$credencial->fecha_hora = now();
|
||||||
$credencial->save();
|
$credencial->save();
|
||||||
|
|
||||||
return back()->with('login_success', 'Login exitoso.');
|
$rol = strtoupper((string) $credencial->rol);
|
||||||
|
$personaProfesional = $credencial->profesional?->persona;
|
||||||
|
$nombreProfesional = trim((string) ($personaProfesional?->nombre ?? '') . ' ' . (string) ($personaProfesional?->apellido ?? ''));
|
||||||
|
$personaAdmin = $credencial->administrador?->persona;
|
||||||
|
$nombreAdmin = trim((string) ($personaAdmin?->nombre ?? '') . ' ' . (string) ($personaAdmin?->apellido ?? ''));
|
||||||
|
$nombreSesion = $rol === 'ADMIN' ? $nombreAdmin : $nombreProfesional;
|
||||||
|
|
||||||
|
$request->session()->regenerate();
|
||||||
|
$request->session()->regenerateToken();
|
||||||
|
|
||||||
|
$request->session()->put([
|
||||||
|
'personal_auth' => true,
|
||||||
|
'personal_token' => $token,
|
||||||
|
'personal_usuario' => (string) $credencial->usuario,
|
||||||
|
'personal_nombre' => $nombreSesion !== '' ? $nombreSesion : (string) $credencial->usuario,
|
||||||
|
'personal_apellido' => $rol === 'ADMIN'
|
||||||
|
? ($personaAdmin?->apellido ?? '')
|
||||||
|
: ($personaProfesional?->apellido ?? ''),
|
||||||
|
'personal_rol' => $rol,
|
||||||
|
'personal_credencial_id' => (int) $credencial->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->registrarAccionSeguridad(
|
||||||
|
$rol === 'ADMIN'
|
||||||
|
? (int) ($credencial->administrador?->persona_id ?? 0)
|
||||||
|
: (int) ($credencial->profesional?->persona_id ?? 0),
|
||||||
|
$rol === 'ADMIN' ? 'ADMIN' : 'PROFESIONAL',
|
||||||
|
(string) $request->ip(),
|
||||||
|
'Inició sesión',
|
||||||
|
($rol === 'ADMIN' ? 'La administradora ID ' . (int) ($credencial->administrador?->id ?? 0) : 'El profesional ID ' . (int) ($credencial->profesional?->id ?? 0)) . ' inició sesión.'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->limpiarThrottle($request, 'login-personal-web');
|
||||||
|
|
||||||
|
$redirectPath = $rol === 'ADMIN' ? '/administrador/dashboard' : '/profesional/dashboard';
|
||||||
|
|
||||||
|
return redirect($redirectPath)->with('login_success', 'Login exitoso.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loginCliente(Request $request): JsonResponse
|
public function loginCliente(Request $request): JsonResponse
|
||||||
@@ -72,7 +167,7 @@ class AuthController extends Controller
|
|||||||
$correo = trim((string) $request->input('correo'));
|
$correo = trim((string) $request->input('correo'));
|
||||||
$contra = (string) $request->input('contra');
|
$contra = (string) $request->input('contra');
|
||||||
|
|
||||||
$credencial = CredencialCliente::where('correo', $correo)->first();
|
$credencial = CredencialCliente::with('cliente')->where('correo', $correo)->first();
|
||||||
if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
|
if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
@@ -80,11 +175,28 @@ class AuthController extends Controller
|
|||||||
], 401);
|
], 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((int) ($credencial->cliente?->baja_id ?? 0) !== 1) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'No es posible iniciar sesion. Comuníquese con un profesional de Abogadas del Litoral',
|
||||||
|
], 403);
|
||||||
|
}
|
||||||
|
|
||||||
$token = Str::random(64);
|
$token = Str::random(64);
|
||||||
$credencial->token = $token;
|
$credencial->token = $token;
|
||||||
$credencial->fecha_hora = now();
|
$credencial->fecha_hora = now();
|
||||||
$credencial->save();
|
$credencial->save();
|
||||||
|
|
||||||
|
$this->registrarAccionSeguridad(
|
||||||
|
(int) ($credencial->cliente?->persona_id ?? 0),
|
||||||
|
'CLIENTE',
|
||||||
|
(string) $request->ip(),
|
||||||
|
'Inició sesión',
|
||||||
|
'El cliente ID ' . (int) ($credencial->cliente?->id ?? 0) . ' inició sesión.'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->limpiarThrottle($request, 'login-cliente-api');
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'data' => [
|
'data' => [
|
||||||
@@ -106,7 +218,7 @@ class AuthController extends Controller
|
|||||||
$usuario = trim((string) $request->input('usuario'));
|
$usuario = trim((string) $request->input('usuario'));
|
||||||
$contra = (string) $request->input('contra');
|
$contra = (string) $request->input('contra');
|
||||||
|
|
||||||
$credencial = CredencialProfesional::where('usuario', $usuario)->first();
|
$credencial = CredencialProfesional::with(['administrador', 'profesional'])->where('usuario', $usuario)->first();
|
||||||
if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
|
if (!$credencial || !$this->credencialValida($contra, (string) $credencial->contra)) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
@@ -114,11 +226,32 @@ class AuthController extends Controller
|
|||||||
], 401);
|
], 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strtoupper((string) $credencial->rol) !== 'ADMIN' && (int) ($credencial->profesional?->baja_id ?? 0) !== 1) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Usted está dado de baja. Comuníquese con el administrador',
|
||||||
|
], 403);
|
||||||
|
}
|
||||||
|
|
||||||
$token = Str::random(64);
|
$token = Str::random(64);
|
||||||
$credencial->token = $token;
|
$credencial->token = $token;
|
||||||
$credencial->fecha_hora = now();
|
$credencial->fecha_hora = now();
|
||||||
$credencial->save();
|
$credencial->save();
|
||||||
|
|
||||||
|
$rol = strtoupper((string) $credencial->rol);
|
||||||
|
|
||||||
|
$this->registrarAccionSeguridad(
|
||||||
|
$rol === 'ADMIN'
|
||||||
|
? (int) ($credencial->administrador?->persona_id ?? 0)
|
||||||
|
: (int) ($credencial->profesional?->persona_id ?? 0),
|
||||||
|
$rol === 'ADMIN' ? 'ADMIN' : 'PROFESIONAL',
|
||||||
|
(string) $request->ip(),
|
||||||
|
'Inició sesión',
|
||||||
|
($rol === 'ADMIN' ? 'La administradora ID ' . (int) ($credencial->administrador?->id ?? 0) : 'El profesional ID ' . (int) ($credencial->profesional?->id ?? 0)) . ' inició sesión.'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->limpiarThrottle($request, 'login-personal-api');
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'data' => [
|
'data' => [
|
||||||
@@ -147,13 +280,13 @@ class AuthController extends Controller
|
|||||||
$tipoDetectado = null;
|
$tipoDetectado = null;
|
||||||
|
|
||||||
if ($tipo === 'cliente') {
|
if ($tipo === 'cliente') {
|
||||||
$credencial = CredencialCliente::where('correo', $identificador)->first();
|
$credencial = CredencialCliente::with('cliente')->where('correo', $identificador)->first();
|
||||||
$tipoDetectado = 'cliente';
|
$tipoDetectado = 'cliente';
|
||||||
} elseif ($tipo === 'profesional') {
|
} elseif ($tipo === 'profesional') {
|
||||||
$credencial = CredencialProfesional::where('usuario', $identificador)->first();
|
$credencial = CredencialProfesional::where('usuario', $identificador)->first();
|
||||||
$tipoDetectado = 'personal';
|
$tipoDetectado = 'personal';
|
||||||
} else {
|
} else {
|
||||||
$credencial = CredencialCliente::where('correo', $identificador)->first();
|
$credencial = CredencialCliente::with('cliente')->where('correo', $identificador)->first();
|
||||||
$tipoDetectado = $credencial ? 'cliente' : null;
|
$tipoDetectado = $credencial ? 'cliente' : null;
|
||||||
|
|
||||||
if (!$credencial) {
|
if (!$credencial) {
|
||||||
@@ -169,11 +302,52 @@ class AuthController extends Controller
|
|||||||
], 401);
|
], 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($credencial instanceof CredencialCliente
|
||||||
|
&& (int) ($credencial->cliente?->baja_id ?? 0) !== 1) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'No es posible iniciar sesion. Comuníquese con un profesional de Abogadas del Litoral',
|
||||||
|
], 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($credencial instanceof CredencialProfesional
|
||||||
|
&& strtoupper((string) $credencial->rol) !== 'ADMIN'
|
||||||
|
&& (int) ($credencial->profesional?->baja_id ?? 0) !== 1) {
|
||||||
|
return response()->json([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Usted está dado de baja. Comuníquese con el administrador',
|
||||||
|
], 403);
|
||||||
|
}
|
||||||
|
|
||||||
$token = Str::random(64);
|
$token = Str::random(64);
|
||||||
$credencial->token = $token;
|
$credencial->token = $token;
|
||||||
$credencial->fecha_hora = now();
|
$credencial->fecha_hora = now();
|
||||||
$credencial->save();
|
$credencial->save();
|
||||||
|
|
||||||
|
if ($credencial instanceof CredencialCliente) {
|
||||||
|
$this->registrarAccionSeguridad(
|
||||||
|
(int) ($credencial->cliente?->persona_id ?? 0),
|
||||||
|
'CLIENTE',
|
||||||
|
(string) $request->ip(),
|
||||||
|
'Inició sesión',
|
||||||
|
'El cliente ID ' . (int) ($credencial->cliente?->id ?? 0) . ' inició sesión.'
|
||||||
|
);
|
||||||
|
} elseif ($credencial instanceof CredencialProfesional) {
|
||||||
|
$rolDescripcion = strtoupper((string) $credencial->rol) === 'ADMIN' ? 'ADMIN' : 'PROFESIONAL';
|
||||||
|
|
||||||
|
$this->registrarAccionSeguridad(
|
||||||
|
$rolDescripcion === 'ADMIN'
|
||||||
|
? (int) ($credencial->administrador?->persona_id ?? 0)
|
||||||
|
: (int) ($credencial->profesional?->persona_id ?? 0),
|
||||||
|
$rolDescripcion,
|
||||||
|
(string) $request->ip(),
|
||||||
|
'Inició sesión',
|
||||||
|
($rolDescripcion === 'ADMIN' ? 'La administradora ID ' . (int) ($credencial->administrador?->id ?? 0) : 'El profesional ID ' . (int) ($credencial->profesional?->id ?? 0)) . ' inició sesión.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->limpiarThrottle($request, 'login-api-general');
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'data' => [
|
'data' => [
|
||||||
@@ -186,6 +360,11 @@ class AuthController extends Controller
|
|||||||
], 200);
|
], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function honeypotValido(Request $request): bool
|
||||||
|
{
|
||||||
|
return trim((string) $request->input('website', '')) === '';
|
||||||
|
}
|
||||||
|
|
||||||
public function logout(Request $request): JsonResponse
|
public function logout(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$token = (string) $request->input('token', '');
|
$token = (string) $request->input('token', '');
|
||||||
@@ -196,8 +375,16 @@ class AuthController extends Controller
|
|||||||
], 422);
|
], 422);
|
||||||
}
|
}
|
||||||
|
|
||||||
$credencialCliente = CredencialCliente::where('token', $token)->first();
|
$credencialCliente = CredencialCliente::with('cliente')->where('token', $token)->first();
|
||||||
if ($credencialCliente) {
|
if ($credencialCliente) {
|
||||||
|
$this->registrarAccionSeguridad(
|
||||||
|
(int) ($credencialCliente->cliente?->persona_id ?? 0),
|
||||||
|
'CLIENTE',
|
||||||
|
(string) $request->ip(),
|
||||||
|
'Cerró sesión',
|
||||||
|
'El cliente ID ' . (int) ($credencialCliente->cliente?->id ?? 0) . ' cerró sesión.'
|
||||||
|
);
|
||||||
|
|
||||||
$credencialCliente->token = null;
|
$credencialCliente->token = null;
|
||||||
$credencialCliente->fecha_hora = now();
|
$credencialCliente->fecha_hora = now();
|
||||||
$credencialCliente->save();
|
$credencialCliente->save();
|
||||||
@@ -208,8 +395,20 @@ class AuthController extends Controller
|
|||||||
], 200);
|
], 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
$credencialProfesional = CredencialProfesional::where('token', $token)->first();
|
$credencialProfesional = CredencialProfesional::with(['administrador', 'profesional'])->where('token', $token)->first();
|
||||||
if ($credencialProfesional) {
|
if ($credencialProfesional) {
|
||||||
|
$rol = strtoupper((string) $credencialProfesional->rol);
|
||||||
|
|
||||||
|
$this->registrarAccionSeguridad(
|
||||||
|
$rol === 'ADMIN'
|
||||||
|
? (int) ($credencialProfesional->administrador?->persona_id ?? 0)
|
||||||
|
: (int) ($credencialProfesional->profesional?->persona_id ?? 0),
|
||||||
|
$rol === 'ADMIN' ? 'ADMIN' : 'PROFESIONAL',
|
||||||
|
(string) $request->ip(),
|
||||||
|
'Cerró sesión',
|
||||||
|
($rol === 'ADMIN' ? 'La administradora ID ' . (int) ($credencialProfesional->administrador?->id ?? 0) : 'El profesional ID ' . (int) ($credencialProfesional->profesional?->id ?? 0)) . ' cerró sesión.'
|
||||||
|
);
|
||||||
|
|
||||||
$credencialProfesional->token = null;
|
$credencialProfesional->token = null;
|
||||||
$credencialProfesional->fecha_hora = now();
|
$credencialProfesional->fecha_hora = now();
|
||||||
$credencialProfesional->save();
|
$credencialProfesional->save();
|
||||||
@@ -234,4 +433,31 @@ class AuthController extends Controller
|
|||||||
|
|
||||||
return Hash::check($contraIngresada, $contraGuardada);
|
return Hash::check($contraIngresada, $contraGuardada);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function limpiarThrottle(Request $request, string $prefijo): void
|
||||||
|
{
|
||||||
|
RateLimiter::clear(md5($prefijo . $request->ip()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function registrarAccionSeguridad(?int $personaId, string $rol, string $ipOrigen, string $accionDescripcion, string $descripcion): void
|
||||||
|
{
|
||||||
|
if (!$personaId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$accion = AccionLog::query()->firstWhere('descripcion', $accionDescripcion);
|
||||||
|
|
||||||
|
if (!$accion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogSeguridad::create([
|
||||||
|
'descripcion' => $descripcion,
|
||||||
|
'fechahora' => now(),
|
||||||
|
'IPorigen' => $ipOrigen,
|
||||||
|
'rol' => $rol,
|
||||||
|
'persona_id' => (int) $personaId,
|
||||||
|
'accion_id' => (int) $accion->id,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
|
||||||
|
|
||||||
use App\Models\EstadoProfesional;
|
|
||||||
use Illuminate\Http\JsonResponse;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
|
|
||||||
class EstadoProfesionalController extends Controller
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Display a listing of the resource.
|
|
||||||
*/
|
|
||||||
public function index(): JsonResponse
|
|
||||||
{
|
|
||||||
$items = EstadoProfesional::all();
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'success' => true,
|
|
||||||
'data' => $items,
|
|
||||||
'message' => 'Registros obtenidos correctamente',
|
|
||||||
], 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a newly created resource in storage.
|
|
||||||
*/
|
|
||||||
public function store(Request $request): JsonResponse
|
|
||||||
{
|
|
||||||
$payload = $request->only((new EstadoProfesional())->getFillable());
|
|
||||||
$estadoProfesional = EstadoProfesional::create($payload);
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'success' => true,
|
|
||||||
'data' => $estadoProfesional,
|
|
||||||
'message' => 'Registro creado correctamente',
|
|
||||||
], 201);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the specified resource.
|
|
||||||
*/
|
|
||||||
public function show(EstadoProfesional $estadoProfesional): JsonResponse
|
|
||||||
{
|
|
||||||
return response()->json([
|
|
||||||
'success' => true,
|
|
||||||
'data' => $estadoProfesional,
|
|
||||||
'message' => 'Registro obtenido correctamente',
|
|
||||||
], 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the specified resource in storage.
|
|
||||||
*/
|
|
||||||
public function update(Request $request, EstadoProfesional $estadoProfesional): JsonResponse
|
|
||||||
{
|
|
||||||
$payload = $request->only((new EstadoProfesional())->getFillable());
|
|
||||||
$estadoProfesional->update($payload);
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'success' => true,
|
|
||||||
'data' => $estadoProfesional,
|
|
||||||
'message' => 'Registro actualizado correctamente',
|
|
||||||
], 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the specified resource from storage.
|
|
||||||
*/
|
|
||||||
public function destroy(EstadoProfesional $estadoProfesional): JsonResponse
|
|
||||||
{
|
|
||||||
$estadoProfesional->delete();
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'success' => true,
|
|
||||||
'message' => 'Registro eliminado correctamente',
|
|
||||||
], 200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Middleware;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
class SecurityHeaders
|
||||||
|
{
|
||||||
|
public function handle(Request $request, Closure $next): Response
|
||||||
|
{
|
||||||
|
$response = $next($request);
|
||||||
|
$entornoLocal = app()->environment(['local', 'development']);
|
||||||
|
$sesionAutenticada = (bool) $request->session()->get('cliente_auth', false)
|
||||||
|
|| (bool) $request->session()->get('personal_auth', false);
|
||||||
|
|
||||||
|
$styleSrc = $entornoLocal
|
||||||
|
? "style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net http:;"
|
||||||
|
: "style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net;";
|
||||||
|
|
||||||
|
$scriptSrc = $entornoLocal
|
||||||
|
? "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net http:;"
|
||||||
|
: "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net;";
|
||||||
|
|
||||||
|
$connectSrc = $entornoLocal
|
||||||
|
? "connect-src 'self' http: https: ws: wss:;"
|
||||||
|
: "connect-src 'self';";
|
||||||
|
|
||||||
|
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
|
||||||
|
$response->headers->set('X-Content-Type-Options', 'nosniff');
|
||||||
|
$response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
||||||
|
$response->headers->set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
|
||||||
|
$response->headers->set(
|
||||||
|
'Content-Security-Policy',
|
||||||
|
"default-src 'self'; base-uri 'self'; frame-ancestors 'self'; form-action 'self'; img-src 'self' data: https:; {$styleSrc} {$scriptSrc} font-src 'self' data: https:; {$connectSrc} frame-src 'self' https://maps.google.com https://www.google.com;"
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($request->isSecure()) {
|
||||||
|
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sesionAutenticada) {
|
||||||
|
$response->headers->set('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0, private');
|
||||||
|
$response->headers->set('Pragma', 'no-cache');
|
||||||
|
$response->headers->set('Expires', 'Thu, 01 Jan 1970 00:00:00 GMT');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,8 @@ class Administrador extends Model
|
|||||||
'persona_id',
|
'persona_id',
|
||||||
'dni',
|
'dni',
|
||||||
'correo',
|
'correo',
|
||||||
|
'pregunta_secreta_hash',
|
||||||
|
'respuesta_secreta_hash',
|
||||||
'credencialprofesional_id',
|
'credencialprofesional_id',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -232,10 +232,10 @@ class Agenda extends Model
|
|||||||
}
|
}
|
||||||
$diasPreferidosIds = array_values(array_unique($diasPreferidosIds));
|
$diasPreferidosIds = array_values(array_unique($diasPreferidosIds));
|
||||||
|
|
||||||
// Toma la duración de turno de la agenda; si no existe, usa 40 minutos.
|
// Toma la duración de turno de la agenda; si no existe, usa 30 minutos.
|
||||||
$duracionTurno = (int) (self::where('id', $agendaId)->value('duracionturno') ?? 40);
|
$duracionTurno = (int) (self::where('id', $agendaId)->value('duracionturno') ?? 30);
|
||||||
if ($duracionTurno <= 0) {
|
if ($duracionTurno <= 0) {
|
||||||
$duracionTurno = 40;
|
$duracionTurno = 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define el punto de inicio de la búsqueda desde el día siguiente (para evitar asignar turnos inmediatos del día actual).
|
// Define el punto de inicio de la búsqueda desde el día siguiente (para evitar asignar turnos inmediatos del día actual).
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class AsistenteSinRespuesta extends Model
|
||||||
|
{
|
||||||
|
public $timestamps = false;
|
||||||
|
|
||||||
|
protected $table = 'asistente_sin_respuesta';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'consulta',
|
||||||
|
'revisado',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'revisado' => 'boolean',
|
||||||
|
'created_at' => 'datetime',
|
||||||
|
];
|
||||||
|
}
|
||||||
+1
-1
@@ -11,7 +11,7 @@ class Baja extends Model
|
|||||||
protected $table = 'bajas';
|
protected $table = 'bajas';
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'motivo',
|
'descripcion',
|
||||||
];
|
];
|
||||||
|
|
||||||
//tiene una
|
//tiene una
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class Cliente extends Model
|
|||||||
|
|
||||||
public function profesionales()
|
public function profesionales()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(Profesional::class, 'profesional_cliente','cliente_id', 'profesional_id')
|
return $this->belongsToMany(Profesional::class, 'profesionales_clientes','cliente_id', 'profesional_id')
|
||||||
->withPivot('estadorelacion')
|
->withPivot('estadorelacion')
|
||||||
->withTimestamps();
|
->withTimestamps();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ class CredencialCliente extends Model
|
|||||||
'correo',
|
'correo',
|
||||||
'token',
|
'token',
|
||||||
'fecha_hora',
|
'fecha_hora',
|
||||||
|
'reset_token',
|
||||||
|
'reset_expira_en',
|
||||||
];
|
];
|
||||||
|
|
||||||
//tiene un
|
//tiene un
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ class CredencialProfesional extends Model
|
|||||||
'rol',
|
'rol',
|
||||||
'token',
|
'token',
|
||||||
'fecha_hora',
|
'fecha_hora',
|
||||||
|
'reset_token',
|
||||||
|
'reset_expira_en',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function profesional()
|
public function profesional()
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ use HasFactory;
|
|||||||
protected $table = 'diasdeatenciones';
|
protected $table = 'diasdeatenciones';
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'descripcion',
|
|
||||||
'agenda_id',
|
'agenda_id',
|
||||||
'dia_id',
|
'dia_id',
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Models;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
||||||
|
|
||||||
class EstadoProfesional extends Model
|
|
||||||
{
|
|
||||||
use HasFactory;
|
|
||||||
protected $table = 'estadosprofesionales';
|
|
||||||
|
|
||||||
protected $fillable = [
|
|
||||||
'descripcion',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function profesional()
|
|
||||||
{
|
|
||||||
return $this->hasMany(Profesional::class, 'estadoprofesional_id', 'id');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class FaqAsistente extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $table = 'faq_asistentes';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'intencion',
|
||||||
|
'palabras_clave',
|
||||||
|
'respuesta',
|
||||||
|
'orden',
|
||||||
|
'activo',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'palabras_clave' => 'array',
|
||||||
|
'activo' => 'boolean',
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ class Formulario extends Model
|
|||||||
'nombrecompleto',
|
'nombrecompleto',
|
||||||
'correo',
|
'correo',
|
||||||
'celular',
|
'celular',
|
||||||
|
'ip_origen',
|
||||||
'estado',
|
'estado',
|
||||||
'profesion_id',
|
'profesion_id',
|
||||||
'servicio_id',
|
'servicio_id',
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class LogSeguridad extends Model
|
|||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected $table = 'logseguridades';
|
protected $table = 'logseguridades';
|
||||||
|
public $timestamps = false;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'descripcion',
|
'descripcion',
|
||||||
@@ -18,6 +19,8 @@ class LogSeguridad extends Model
|
|||||||
'rol',
|
'rol',
|
||||||
'persona_id',
|
'persona_id',
|
||||||
'accion_id',
|
'accion_id',
|
||||||
|
'accion_descripcion',
|
||||||
|
'responsable_nombre',
|
||||||
];
|
];
|
||||||
|
|
||||||
//pertenece a
|
//pertenece a
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Notificacion extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $table = 'notificaciones';
|
||||||
|
protected $primaryKey = 'notificacion_id';
|
||||||
|
public $timestamps = false;
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'tipo',
|
||||||
|
'mensaje_inicio',
|
||||||
|
'mensaje_final',
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -20,4 +20,9 @@ class Profesion extends Model
|
|||||||
{
|
{
|
||||||
return $this->hasMany(Profesional::class, 'profesion_id', 'id');
|
return $this->hasMany(Profesional::class, 'profesion_id', 'id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function profesionalesAsociados()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Profesional::class, 'profesionales_profesiones', 'profesion_id', 'profesional_id');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ class Profesional extends Model
|
|||||||
'correo',
|
'correo',
|
||||||
'dni',
|
'dni',
|
||||||
'credencialprofesional_id',
|
'credencialprofesional_id',
|
||||||
'estadoprofesional_id',
|
|
||||||
'persona_id',
|
'persona_id',
|
||||||
'baja_id',
|
'baja_id',
|
||||||
'profesion_id',
|
'profesion_id',
|
||||||
@@ -30,16 +29,16 @@ class Profesional extends Model
|
|||||||
return $this->belongsTo(Profesion::class, 'profesion_id', 'id');
|
return $this->belongsTo(Profesion::class, 'profesion_id', 'id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function profesiones()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Profesion::class, 'profesionales_profesiones', 'profesional_id', 'profesion_id');
|
||||||
|
}
|
||||||
|
|
||||||
public function credencialProfesional()
|
public function credencialProfesional()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(CredencialProfesional::class, 'credencialprofesional_id', 'id');
|
return $this->belongsTo(CredencialProfesional::class, 'credencialprofesional_id', 'id');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function estadoProfesional()
|
|
||||||
{
|
|
||||||
return $this->belongsTo(EstadoProfesional::class, 'estadoprofesional_id', 'id');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function persona()
|
public function persona()
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Persona::class, 'persona_id');
|
return $this->belongsTo(Persona::class, 'persona_id');
|
||||||
@@ -86,7 +85,7 @@ class Profesional extends Model
|
|||||||
|
|
||||||
public function clientes()
|
public function clientes()
|
||||||
{
|
{
|
||||||
return $this->belongsToMany(Cliente::class, 'profesionales_cliente', 'profesional_id', 'cliente_id')
|
return $this->belongsToMany(Cliente::class, 'profesionales_clientes', 'profesional_id', 'cliente_id')
|
||||||
->withPivot('estadorelacion')
|
->withPivot('estadorelacion')
|
||||||
->withTimestamps();
|
->withTimestamps();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ class Servicio extends Model
|
|||||||
'titulo',
|
'titulo',
|
||||||
'estado',
|
'estado',
|
||||||
'descripcion',
|
'descripcion',
|
||||||
|
'visibleenweb',
|
||||||
'contenidoweb_id',
|
'contenidoweb_id',
|
||||||
'profesion_id',
|
'profesion_id',
|
||||||
'foto_id',
|
'foto_id',
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ class Turno extends Model
|
|||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'inicio',
|
'inicio',
|
||||||
'correo',
|
'correo',
|
||||||
|
'celular',
|
||||||
'nombrecompleto',
|
'nombrecompleto',
|
||||||
'descripcion',
|
'descripcion',
|
||||||
'cliente_id',
|
'cliente_id',
|
||||||
|
|||||||
@@ -2,7 +2,15 @@
|
|||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Models\Bug;
|
||||||
|
use App\Models\Profesional;
|
||||||
|
use Illuminate\Cache\RateLimiting\Limit;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Pagination\Paginator;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\RateLimiter;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
use Illuminate\Support\Facades\View;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
@@ -19,6 +27,107 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
*/
|
*/
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
//
|
Paginator::useBootstrapFive();
|
||||||
|
|
||||||
|
RateLimiter::for('login-cliente-web', fn (Request $request) => Limit::perHour(5)->by($request->ip()));
|
||||||
|
RateLimiter::for('login-personal-web', fn (Request $request) => Limit::perHour(5)->by($request->ip()));
|
||||||
|
RateLimiter::for('login-cliente-api', fn (Request $request) => Limit::perHour(5)->by($request->ip()));
|
||||||
|
RateLimiter::for('login-personal-api', fn (Request $request) => Limit::perHour(5)->by($request->ip()));
|
||||||
|
RateLimiter::for('login-api-general', fn (Request $request) => Limit::perHour(5)->by($request->ip()));
|
||||||
|
RateLimiter::for('recuperar-cliente', fn (Request $request) => Limit::perHour(5)->by($request->ip()));
|
||||||
|
RateLimiter::for('recuperar-personal', fn (Request $request) => Limit::perHour(5)->by($request->ip()));
|
||||||
|
RateLimiter::for('recuperar-admin', fn (Request $request) => Limit::perHour(5)->by($request->ip()));
|
||||||
|
RateLimiter::for('recuperar-admin-pregunta', fn (Request $request) => Limit::perHour(5)->by($request->ip()));
|
||||||
|
RateLimiter::for('reportar-bugs', fn (Request $request) => Limit::perHour(5)->by($request->ip()));
|
||||||
|
RateLimiter::for('asistente-chat', fn (Request $request) => Limit::perMinute(20)->by($request->ip()));
|
||||||
|
|
||||||
|
View::composer('profesional.*', function ($view): void {
|
||||||
|
$credencialId = (int) session('personal_credencial_id', 0);
|
||||||
|
$profesionalId = 0;
|
||||||
|
|
||||||
|
if ($credencialId > 0) {
|
||||||
|
$profesionalId = (int) Profesional::query()
|
||||||
|
->where('credencialprofesional_id', $credencialId)
|
||||||
|
->value('id');
|
||||||
|
}
|
||||||
|
|
||||||
|
$formulariosPendientesCount = 0;
|
||||||
|
|
||||||
|
if ($profesionalId > 0) {
|
||||||
|
$formulariosPendientesCount = DB::table('profesionales_formularios as pf')
|
||||||
|
->join('formularios as f', 'f.id', '=', 'pf.formulario_id')
|
||||||
|
->where('pf.profesional_id', $profesionalId)
|
||||||
|
->whereRaw("LOWER(TRIM(pf.estado)) = 'pendiente'")
|
||||||
|
->whereRaw("LOWER(TRIM(f.estado)) = 'pendiente'")
|
||||||
|
->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
$view->with('formulariosPendientesCount', $formulariosPendientesCount);
|
||||||
|
|
||||||
|
$notificacionesClaves = [];
|
||||||
|
if ($profesionalId > 0) {
|
||||||
|
$hoy = now()->toDateString();
|
||||||
|
$manana = now()->addDay()->toDateString();
|
||||||
|
|
||||||
|
$turnosHoy = DB::table('turnos as t')
|
||||||
|
->where('t.profesional_id', $profesionalId)
|
||||||
|
->whereDate('t.inicio', $hoy)
|
||||||
|
->select('t.nombrecompleto', 't.inicio')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
foreach ($turnosHoy as $turno) {
|
||||||
|
$nombre = trim((string) ($turno->nombrecompleto ?? 'Sin nombre'));
|
||||||
|
$hora = $turno->inicio ? \Illuminate\Support\Carbon::parse((string) $turno->inicio)->format('H:i') : '-';
|
||||||
|
$titulo = 'Turno hoy a las ' . $hora . ' — ' . $nombre;
|
||||||
|
$fecha = 'Hoy ' . $hora;
|
||||||
|
$notificacionesClaves[] = base64_encode('turno_hoy|' . $titulo . '|' . $fecha);
|
||||||
|
}
|
||||||
|
|
||||||
|
$turnosManana = DB::table('turnos as t')
|
||||||
|
->where('t.profesional_id', $profesionalId)
|
||||||
|
->whereDate('t.inicio', $manana)
|
||||||
|
->select('t.nombrecompleto', 't.inicio')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
foreach ($turnosManana as $turno) {
|
||||||
|
$nombre = trim((string) ($turno->nombrecompleto ?? 'Sin nombre'));
|
||||||
|
$hora = $turno->inicio ? \Illuminate\Support\Carbon::parse((string) $turno->inicio)->format('H:i') : '-';
|
||||||
|
$titulo = 'Turno mañana a las ' . $hora . ' — ' . $nombre;
|
||||||
|
$fecha = 'Mañana ' . $hora;
|
||||||
|
$notificacionesClaves[] = base64_encode('turno_manana|' . $titulo . '|' . $fecha);
|
||||||
|
}
|
||||||
|
|
||||||
|
$estadoCanceladoId = (int) DB::table('estadosturnos')->whereRaw('LOWER(TRIM(descripcion)) = ?', ['cancelado'])->value('id');
|
||||||
|
if ($estadoCanceladoId > 0) {
|
||||||
|
$turnosCancelados = DB::table('turnos as t')
|
||||||
|
->where('t.profesional_id', $profesionalId)
|
||||||
|
->where('t.estadoturno_id', $estadoCanceladoId)
|
||||||
|
->where('t.updated_at', '>=', now()->subDays(7))
|
||||||
|
->select('t.nombrecompleto', 't.inicio', 't.updated_at')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
foreach ($turnosCancelados as $turno) {
|
||||||
|
$nombre = trim((string) ($turno->nombrecompleto ?? 'Sin nombre'));
|
||||||
|
$fecha = $turno->updated_at
|
||||||
|
? \Illuminate\Support\Carbon::parse((string) $turno->updated_at)->format('d/m/Y')
|
||||||
|
: '-';
|
||||||
|
$titulo = 'Turno cancelado — ' . $nombre;
|
||||||
|
$notificacionesClaves[] = base64_encode('turno_cancelado|' . $titulo . '|' . $fecha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$notificacionesCount = count($notificacionesClaves);
|
||||||
|
$view->with('notificacionesCount', $notificacionesCount);
|
||||||
|
$view->with('notificacionesClaves', $notificacionesClaves);
|
||||||
|
});
|
||||||
|
|
||||||
|
View::composer('administrador.*', function ($view): void {
|
||||||
|
$bugsPendientesCount = Bug::query()
|
||||||
|
->whereRaw("LOWER(TRIM(estado)) = 'pendiente'")
|
||||||
|
->count();
|
||||||
|
|
||||||
|
$view->with('bugsPendientesCount', $bugsPendientesCount);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user