Vistas del panel de profesionales
This commit is contained in:
@@ -0,0 +1,235 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Agregar Cliente - Profesional</title>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const root = document.documentElement;
|
||||||
|
root.classList.add('sidebar-nav-pending');
|
||||||
|
|
||||||
|
const releasePending = function () {
|
||||||
|
root.classList.remove('sidebar-nav-pending');
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeoutId = window.setTimeout(releasePending, 1200);
|
||||||
|
|
||||||
|
if (document.querySelector('.admin-sidebar')) {
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new MutationObserver(function () {
|
||||||
|
if (!document.querySelector('.admin-sidebar')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.disconnect();
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.documentElement, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
html.sidebar-nav-pending main {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
|
</head>
|
||||||
|
<body id="top" class="d-flex flex-column min-vh-100 bg-light">
|
||||||
|
<header class="app-navbar">
|
||||||
|
<nav class="navbar navbar-expand-lg">
|
||||||
|
<div class="container">
|
||||||
|
@php
|
||||||
|
$nombreSesion = trim((string) session('personal_nombre', 'Profesional'));
|
||||||
|
$nombreSaludo = $nombreSesion !== '' ? explode(' ', $nombreSesion)[0] : 'Profesional';
|
||||||
|
@endphp
|
||||||
|
<a class="navbar-brand d-flex align-items-center gap-2" href="/profesional/dashboard">
|
||||||
|
<div class="d-flex align-items-center justify-content-center" style="width: 130px; height: 52px;">
|
||||||
|
<img src="{{ asset('images/logo.png') }}" alt="Logo" class="img-fluid" style="max-height: 70px; width: auto; object-fit: contain;">
|
||||||
|
</div>
|
||||||
|
<span class="fw-semibold">¡Hola, {{ $nombreSaludo }}!</span>
|
||||||
|
</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="btn app-navbar-link" href="/profesional/dashboard">Mi Agenda</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/clientes">Mis Clientes</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/mis-datos">Mis Datos</a></li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn app-navbar-link position-relative" href="/profesional/formularios">
|
||||||
|
Revisar Formularios
|
||||||
|
@if(($formulariosPendientesCount ?? 0) > 0)
|
||||||
|
<span class="position-absolute top-0 start-100 translate-middle p-1 bg-danger rounded-circle js-notificaciones-badge">
|
||||||
|
<span class="visually-hidden">Hay formularios pendientes</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn app-navbar-link position-relative" href="/profesional/notificaciones" data-notificaciones-claves='@json($notificacionesClaves ?? [])'>
|
||||||
|
Notificaciones
|
||||||
|
@if(($notificacionesCount ?? 0) > 0)
|
||||||
|
<span class="position-absolute top-0 start-100 translate-middle p-1 bg-danger rounded-circle js-notificaciones-badge d-none">
|
||||||
|
<span class="visually-hidden">Hay notificaciones</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a class="btn app-navbar-link" href="/logout">Cerrar Sesion</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container py-4 flex-grow-1">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<h1 class="h4 mb-0">Agregar Nuevo Cliente</h1>
|
||||||
|
<a href="/profesional/clientes" class="btn btn-outline-secondary btn-sm">Volver</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-body">
|
||||||
|
<form action="/profesional/clientes/crear" method="GET" class="mb-4">
|
||||||
|
<label for="dni_consulta" class="form-label">DNI <span class="text-danger">*</span></label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control @error('dni') is-invalid @enderror" id="dni_consulta" name="dni" value="{{ $dniConsultado ?? '' }}" pattern="[0-9A-Za-z]{7,20}" maxlength="20" title="Números y letras: 7 a 20 caracteres" required>
|
||||||
|
<button type="submit" class="btn btn-primary">Verificar DNI</button>
|
||||||
|
</div>
|
||||||
|
@error('dni')
|
||||||
|
<div class="invalid-feedback d-block">{{ $message }}</div>
|
||||||
|
@enderror
|
||||||
|
</form>
|
||||||
|
|
||||||
|
@if ($errors->any())
|
||||||
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||||
|
<strong>¡Error!</strong>
|
||||||
|
<ul class="mb-0">
|
||||||
|
@foreach ($errors->all() as $error)
|
||||||
|
<li>{{ $error }}</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if (($estadoDni ?? null) === 'cliente-existente')
|
||||||
|
<div class="alert alert-warning mb-0" role="alert">
|
||||||
|
Esta persona ya es cliente.
|
||||||
|
</div>
|
||||||
|
@elseif (($estadoDni ?? null) === 'persona-existente')
|
||||||
|
<div class="alert alert-info" role="alert">
|
||||||
|
El DNI pertenece a {{ trim(($personaDni?->nombre ?? '') . ' ' . ($personaDni?->apellido ?? '')) }}. Solo se necesita el correo. La contrasena inicial sera el DNI del cliente.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form action="/profesional/clientes/store" method="POST">
|
||||||
|
@csrf
|
||||||
|
<input type="hidden" name="dni" value="{{ $dniConsultado }}">
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="correo" class="form-label">Correo Electrónico <span class="text-danger">*</span></label>
|
||||||
|
<input type="email" class="form-control @error('correo') is-invalid @enderror" id="correo" name="correo" value="{{ old('correo') }}" required>
|
||||||
|
@error('correo')
|
||||||
|
<div class="invalid-feedback">{{ $message }}</div>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex gap-2 justify-content-center">
|
||||||
|
<button type="submit" class="btn btn-primary">Guardar Cliente</button>
|
||||||
|
<a href="/profesional/clientes" class="btn btn-secondary">Cancelar</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
@elseif (($estadoDni ?? null) === 'persona-nueva')
|
||||||
|
<div class="alert alert-info" role="alert">
|
||||||
|
No existe una persona con ese DNI. Complete los datos para crear persona, cliente, credenciales y telefono. La contrasena inicial sera el DNI del cliente.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form action="/profesional/clientes/store" method="POST">
|
||||||
|
@csrf
|
||||||
|
<input type="hidden" name="dni" value="{{ $dniConsultado }}">
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="nombre" class="form-label">Nombre <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" class="form-control @error('nombre') is-invalid @enderror" id="nombre" name="nombre" value="{{ old('nombre') }}" pattern="[A-Za-zÁÉÍÓÚáéíóúÑñ\s]+" oninput="this.value=this.value.replace(/[^A-Za-zÁÉÍÓÚáéíóúÑñ\s]/g,'')" required>
|
||||||
|
@error('nombre')
|
||||||
|
<div class="invalid-feedback">{{ $message }}</div>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="apellido" class="form-label">Apellido <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" class="form-control @error('apellido') is-invalid @enderror" id="apellido" name="apellido" value="{{ old('apellido') }}" pattern="[A-Za-zÁÉÍÓÚáéíóúÑñ\s]+" oninput="this.value=this.value.replace(/[^A-Za-zÁÉÍÓÚáéíóúÑñ\s]/g,'')" required>
|
||||||
|
@error('apellido')
|
||||||
|
<div class="invalid-feedback">{{ $message }}</div>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="cuil" class="form-label">CUIL</label>
|
||||||
|
<input type="text" class="form-control @error('cuil') is-invalid @enderror" id="cuil" name="cuil" value="{{ old('cuil') }}" inputmode="numeric" pattern="[0-9]+" oninput="this.value=this.value.replace(/\D/g,'')">
|
||||||
|
@error('cuil')
|
||||||
|
<div class="invalid-feedback">{{ $message }}</div>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="fechanac" class="form-label">Fecha de Nacimiento</label>
|
||||||
|
<input type="date" class="form-control @error('fechanac') is-invalid @enderror" id="fechanac" name="fechanac" value="{{ old('fechanac') }}">
|
||||||
|
@error('fechanac')
|
||||||
|
<div class="invalid-feedback">{{ $message }}</div>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="telefono" class="form-label">Teléfono/Celular <span class="text-danger">*</span></label>
|
||||||
|
<input type="tel" class="form-control @error('telefono') is-invalid @enderror" id="telefono" name="telefono" value="{{ old('telefono') }}" inputmode="numeric" pattern="[0-9]+" oninput="this.value=this.value.replace(/\D/g,'')" placeholder="Ej: 3512345678" required>
|
||||||
|
@error('telefono')
|
||||||
|
<div class="invalid-feedback">{{ $message }}</div>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="correo" class="form-label">Correo Electrónico <span class="text-danger">*</span></label>
|
||||||
|
<input type="email" class="form-control @error('correo') is-invalid @enderror" id="correo" name="correo" value="{{ old('correo') }}" required>
|
||||||
|
@error('correo')
|
||||||
|
<div class="invalid-feedback">{{ $message }}</div>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex gap-2 justify-content-center">
|
||||||
|
<button type="submit" class="btn btn-primary">Guardar Cliente</button>
|
||||||
|
<a href="/profesional/clientes" class="btn btn-secondary">Cancelar</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
@include('partials.reportar-falla-boton')
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Ayuda - Profesional</title>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const root = document.documentElement;
|
||||||
|
root.classList.add('sidebar-nav-pending');
|
||||||
|
|
||||||
|
const releasePending = function () {
|
||||||
|
root.classList.remove('sidebar-nav-pending');
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeoutId = window.setTimeout(releasePending, 1200);
|
||||||
|
|
||||||
|
if (document.querySelector('.admin-sidebar')) {
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new MutationObserver(function () {
|
||||||
|
if (!document.querySelector('.admin-sidebar')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.disconnect();
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.documentElement, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
html.sidebar-nav-pending main {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
|
</head>
|
||||||
|
<body id="top" class="d-flex flex-column min-vh-100 bg-light">
|
||||||
|
<header class="app-navbar">
|
||||||
|
<nav class="navbar navbar-expand-lg">
|
||||||
|
<div class="container">
|
||||||
|
@php
|
||||||
|
$nombreSesion = trim((string) session('personal_nombre', 'Profesional'));
|
||||||
|
$nombreSaludo = $nombreSesion !== '' ? explode(' ', $nombreSesion)[0] : 'Profesional';
|
||||||
|
@endphp
|
||||||
|
<a class="navbar-brand d-flex align-items-center gap-2" href="/profesional/dashboard">
|
||||||
|
<div class="d-flex align-items-center justify-content-center" style="width: 130px; height: 52px;">
|
||||||
|
<img src="{{ asset('images/logo.png') }}" alt="Logo" class="img-fluid" style="max-height: 70px; width: auto; object-fit: contain;">
|
||||||
|
</div>
|
||||||
|
<span class="fw-semibold">¡Hola, {{ $nombreSaludo }}!</span>
|
||||||
|
</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="btn app-navbar-link" href="/profesional/dashboard">Mi Agenda</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/clientes">Mis Clientes</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/mis-datos">Mis Datos</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/formularios">Revisar Formularios</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/notificaciones">Notificaciones</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/ayuda" title="Ayuda para profesionales" aria-label="Ayuda para profesionales">¿Necesitas Ayuda?</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a class="btn app-navbar-link" href="/logout">Cerrar Sesion</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container py-4 flex-grow-1">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-12 col-xl-10">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<h1 class="h4 mb-0">Ayuda para usar el sistema (Profesionales)</h1>
|
||||||
|
<a href="/profesional/dashboard" class="btn btn-outline-secondary btn-sm">Volver</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card border shadow-sm mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="h6">Inicio de sesión</h2>
|
||||||
|
<p class="mb-2">Para iniciar sesión primero debe poseer credenciales de acceso que le brindará el administrador del sistema.</p>
|
||||||
|
<p class="mb-0">En la vista de inicio de sesión de profesionales, el profesional depositará sus credenciales de acceso, y si son correctas podrá acceder al sistema.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card border shadow-sm mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="h6">Recuperación de credenciales</h2>
|
||||||
|
<p class="mb-0">En caso de olvidar su contraseña, deberá presionar el botón debajo de la vista de inicio de sesión de profesionales "Recuperación para personal" y luego de llenar un pequeño formulario, tendrá la posibilidad de cambiar su contraseña mediante un enlace que se le enviará a su correo electrónico (debe ser el mismo correo con el que se registró en el sistema).</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card border shadow-sm mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="h6">Apartado Mi Agenda</h2>
|
||||||
|
<p class="mb-2">En este apartado se visualizará la agenda con todos los turnos cargados, días de atención, feriados, recesos y vacaciones. Se puede ver por día, semana o mes.</p>
|
||||||
|
<h3 class="h6 mt-3">Configurar mi agenda</h3>
|
||||||
|
<p class="mb-2">La agenda se puede configurar con el botón "configurar mi agenda". Aquí se puede personalizar los días y horarios de atención, duración de turnos, recesos, vacaciones y feriados.</p>
|
||||||
|
<h3 class="h6 mt-3">Agendar un turno</h3>
|
||||||
|
<p class="mb-2">Este botón le permite al profesional llenar un pequeño formulario para asignar un turno de forma manual.</p>
|
||||||
|
<h3 class="h6 mt-3">Visualización de turnos</h3>
|
||||||
|
<p class="mb-2">En la agenda figurarán todos los turnos del profesional. Al presionar sobre alguno de ellos, se abrirá en la parte derecha un panel con los detalles de este. En él habrá dos opciones: Cancelar turno y Reprogramar turno.</p>
|
||||||
|
<p class="mb-2"><strong>Cancelar turno:</strong> Si se presiona este botón, se cancelará el turno y se le notificará al damnificado sobre esta situación.</p>
|
||||||
|
<p class="mb-0"><strong>Reprogramar turno:</strong> Si se presiona este botón, se abrirá una ventana para poder asignar el nuevo día y horario del turno. En este caso también se le notificará al damnificado sobre la situación.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card border shadow-sm mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="h6">Mis Clientes</h2>
|
||||||
|
<p class="mb-2">En esta sección se visualizarán todos los clientes correspondientes a al profesional.</p>
|
||||||
|
<p class="mb-2"><strong>Filtro:</strong> Para buscar un cliente se podrá hacer uso del filtro. En él se puede filtrar por DNI, nombre, apellido, correo o estado (activo o inactivo).</p>
|
||||||
|
<p class="mb-2">Sobre los clientes se podrán hacer tres acciones: Dar de baja/alta, editar sus datos y/o subir documentación.</p>
|
||||||
|
<p class="mb-2"><strong>Dar de baja/alta:</strong> Al presionar este botón, se dará de baja un cliente (ya sea porque ya no es más cliente o por otros motivos). Si el cliente está de baja, se lo podrá dar de alta nuevamente presionando el mismo botón.</p>
|
||||||
|
<p class="mb-2"><strong>Editar datos:</strong> En caso de que el cliente cambie sus datos o si los datos están mal cargados, se lo podrá editar con este botón.</p>
|
||||||
|
<p class="mb-0"><strong>Subir documentación:</strong> Al presionar este botón, se podrá visualizar los documentos cargados para ese cliente. Además, se podrá subir nueva documentación, descargar y eliminar la documentación existente.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card border shadow-sm mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="h6">Mis Datos</h2>
|
||||||
|
<p class="mb-0">Aquí se podrá ver los datos del profesional en cuestión. Solamente podrá visualizar sus datos, no editarlos. Para editarlos deberá comunicarle al administrador su deseo de editar sus propios datos en caso de que corresponda.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card border shadow-sm mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="h6">Revisar Formularios</h2>
|
||||||
|
<p class="mb-2">En esta sección se visualizarán los formularios correspondientes a la profesión del profesional (ejemplo, si el profesional es abogado, podrá ver todos los formularios correspondientes a abogacía).</p>
|
||||||
|
<p class="mb-2">Podrá filtrar por nombre, correo, celular, estado, etc.</p>
|
||||||
|
<p class="mb-2"><strong>Visualizar formulario:</strong> Al ver un formulario, se podrá rechazar, asignar un turno manual o asignar un turno automático.</p>
|
||||||
|
<p class="mb-2">Al rechazar el formulario, le dará la posibilidad a otro profesional de aceptar o rechazar el formulario.</p>
|
||||||
|
<p class="mb-2">Al asignar un turno manual o automático, le quitará la posibilidad a otro profesional de interactuar con el formulario.</p>
|
||||||
|
<p class="mb-0"><strong>Aclaración:</strong> En caso de asignar un turno manual o automático, se le notificará por correo a la persona que envió el formulario los detalles de su turno.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card border shadow-sm mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="h6">Notificaciones</h2>
|
||||||
|
<p class="mb-2">Aquí aparecerán las siguientes notificaciones:</p>
|
||||||
|
<ul class="mb-0">
|
||||||
|
<li>Turnos de hoy: Informará al profesional todos los turnos (si hubiera) que tendrá en el día actual.</li>
|
||||||
|
<li>Turnos de mañana: Informará al profesional todos los turnos (si hubiera) que tendrá en el día siguiente.</li>
|
||||||
|
<li>Turnos cancelados: Se le informará al profesional en caso de que un cliente cancele un turno.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card border shadow-sm">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="h6">¿Qué hacer en caso de detectar un error en la página?</h2>
|
||||||
|
<p class="mb-0">Si se encuentra con un error en el sitio, agradeceríamos que lo reporte con el botón que aparece en la parte superior de la página con el símbolo 🐞. Al apretarlo se sacará una foto a su pantalla automáticamente y se le pedirá que complete un pequeño formulario explicando que fue lo que ocurrió.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
@include('partials.reportar-falla-boton')
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,422 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Configurar Mi Agenda</title>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const root = document.documentElement;
|
||||||
|
root.classList.add('sidebar-nav-pending');
|
||||||
|
|
||||||
|
const releasePending = function () {
|
||||||
|
root.classList.remove('sidebar-nav-pending');
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeoutId = window.setTimeout(releasePending, 1200);
|
||||||
|
|
||||||
|
if (document.querySelector('.admin-sidebar')) {
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new MutationObserver(function () {
|
||||||
|
if (!document.querySelector('.admin-sidebar')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.disconnect();
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.documentElement, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
html.sidebar-nav-pending main {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
|
<style>
|
||||||
|
.panel-agenda {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabla-config th,
|
||||||
|
.tabla-config td {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body id="top" class="d-flex flex-column min-vh-100 bg-light">
|
||||||
|
<header class="app-navbar">
|
||||||
|
<nav class="navbar navbar-expand-lg">
|
||||||
|
<div class="container">
|
||||||
|
@php
|
||||||
|
$nombreSesion = trim((string) session('personal_nombre', 'Profesional'));
|
||||||
|
$nombreSaludo = $nombreSesion !== '' ? explode(' ', $nombreSesion)[0] : 'Profesional';
|
||||||
|
@endphp
|
||||||
|
<a class="navbar-brand d-flex align-items-center gap-2" href="/profesional/dashboard">
|
||||||
|
<div class="d-flex align-items-center justify-content-center" style="width: 130px; height: 52px;">
|
||||||
|
<img src="{{ asset('images/logo.png') }}" alt="Logo" class="img-fluid" style="max-height: 70px; width: auto; object-fit: contain;">
|
||||||
|
</div>
|
||||||
|
<span class="fw-semibold">Hola, {{ $nombreSaludo }}</span>
|
||||||
|
</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="btn app-navbar-link" href="/profesional/dashboard">Mi Agenda</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/clientes">Mis Clientes</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/mis-datos">Mis Datos</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/formularios">Revisar Formularios</a></li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn app-navbar-link position-relative" href="/profesional/notificaciones" data-notificaciones-claves='@json($notificacionesClaves ?? [])'>
|
||||||
|
Notificaciones
|
||||||
|
@if(($notificacionesCount ?? 0) > 0)
|
||||||
|
<span class="position-absolute top-0 start-100 translate-middle p-1 bg-danger rounded-circle js-notificaciones-badge d-none">
|
||||||
|
<span class="visually-hidden">Hay notificaciones</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a class="btn app-navbar-link" href="/logout">Cerrar Sesion</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container py-4 flex-grow-1">
|
||||||
|
@php
|
||||||
|
$filasAtencion = old('dias', isset($bloquesAtencion) ? (is_array($bloquesAtencion) ? $bloquesAtencion : $bloquesAtencion->toArray()) : []);
|
||||||
|
$filasReceso = old('recesos', isset($bloquesReceso) ? (is_array($bloquesReceso) ? $bloquesReceso : $bloquesReceso->toArray()) : []);
|
||||||
|
$filasVacaciones = old('vacaciones', isset($vacaciones) ? (is_array($vacaciones) ? $vacaciones : $vacaciones->toArray()) : []);
|
||||||
|
$filasFeriados = old('feriados', isset($feriados) ? (is_array($feriados) ? $feriados : $feriados->toArray()) : []);
|
||||||
|
$duracionTurnoActual = (int) old('duracionturno', $duracionTurno ?? 30);
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">
|
||||||
|
<h1 class="h4 mb-0">Configurar Mi Agenda</h1>
|
||||||
|
<a href="/profesional/dashboard" class="btn btn-outline-secondary btn-sm">Volver a Mi Agenda</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if(session('agenda_success'))
|
||||||
|
<div class="alert alert-success">{{ session('agenda_success') }}</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if($errors->any())
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<strong>Se encontraron errores:</strong>
|
||||||
|
<ul class="mb-0 mt-2">
|
||||||
|
@foreach($errors->all() as $error)
|
||||||
|
<li>{{ $error }}</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<form method="POST" action="/profesional/configurar-agenda" class="d-grid gap-3">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<section class="panel-agenda">
|
||||||
|
<h2 class="h6 mb-3">Duración de los turnos</h2>
|
||||||
|
<div class="row g-3 align-items-end">
|
||||||
|
<div class="col-12 col-md-4 col-lg-3">
|
||||||
|
<label class="form-label" for="duracionturno">Duración por turno (minutos)</label>
|
||||||
|
<input id="duracionturno" name="duracionturno" type="number" class="form-control" min="10" max="180" step="5" value="{{ $duracionTurnoActual }}" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-8 col-lg-9">
|
||||||
|
<p class="small text-muted mb-0">Esta duración se usará para generar la disponibilidad y calcular el horario de fin de cada turno.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel-agenda">
|
||||||
|
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">
|
||||||
|
<h2 class="h6 mb-0">Dias de atenciones</h2>
|
||||||
|
<button type="button" id="btnAgregarDia" class="btn btn-sm btn-outline-primary">Agregar horario de atencion</button>
|
||||||
|
</div>
|
||||||
|
<p class="small text-muted mb-3">Hasta 10 horarios en total: maximo 5 AM y 5 PM.</p>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-sm tabla-config" id="tablaDiasAtencion">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="min-width: 160px;">Dia</th>
|
||||||
|
<th style="min-width: 130px;">Inicio</th>
|
||||||
|
<th style="min-width: 130px;">Fin</th>
|
||||||
|
<th style="min-width: 110px;">Tipo</th>
|
||||||
|
<th style="width: 70px;"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel-agenda">
|
||||||
|
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">
|
||||||
|
<h2 class="h6 mb-0">Receso</h2>
|
||||||
|
<button type="button" id="btnAgregarReceso" class="btn btn-sm btn-outline-primary">Agregar receso</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-sm tabla-config" id="tablaRecesos">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="min-width: 160px;">Dia</th>
|
||||||
|
<th style="min-width: 130px;">Inicio</th>
|
||||||
|
<th style="min-width: 130px;">Fin</th>
|
||||||
|
<th style="width: 70px;"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel-agenda">
|
||||||
|
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">
|
||||||
|
<h2 class="h6 mb-0">Vacaciones</h2>
|
||||||
|
<button type="button" id="btnAgregarVacacion" class="btn btn-sm btn-outline-primary">Agregar vacaciones</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-sm tabla-config" id="tablaVacaciones">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="min-width: 150px;">Fecha inicio</th>
|
||||||
|
<th style="min-width: 150px;">Fecha fin</th>
|
||||||
|
<th>Descripcion</th>
|
||||||
|
<th style="width: 70px;"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="panel-agenda">
|
||||||
|
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">
|
||||||
|
<h2 class="h6 mb-0">Feriados</h2>
|
||||||
|
<button type="button" id="btnAgregarFeriado" class="btn btn-sm btn-outline-primary">Agregar feriado</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-sm tabla-config" id="tablaFeriados">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="min-width: 160px;">Fecha</th>
|
||||||
|
<th>Descripcion</th>
|
||||||
|
<th style="width: 70px;"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-end gap-2">
|
||||||
|
<a href="/profesional/dashboard" class="btn btn-outline-secondary">Cancelar</a>
|
||||||
|
<button type="submit" class="btn btn-primary">Guardar configuracion</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<template id="tplDiaAtencion">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<select class="form-select form-select-sm" data-field="dia_id" required></select>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="time" class="form-control form-control-sm" data-field="horariocomienzo" required>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="time" class="form-control form-control-sm" data-field="horariofin" required>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<select class="form-select form-select-sm" data-field="tipo" required>
|
||||||
|
<option value="AM">AM</option>
|
||||||
|
<option value="PM">PM</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-danger" data-remove="row">X</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="tplReceso">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<select class="form-select form-select-sm" data-field="dia_id" required></select>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="time" class="form-control form-control-sm" data-field="comienzo" required>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="time" class="form-control form-control-sm" data-field="fin" required>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-danger" data-remove="row">X</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="tplVacacion">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<input type="date" class="form-control form-control-sm" data-field="inicio" required>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="date" class="form-control form-control-sm" data-field="fin" required>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" class="form-control form-control-sm" data-field="descripcion" maxlength="255" placeholder="Vacaciones">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-danger" data-remove="row">X</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template id="tplFeriado">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<input type="date" class="form-control form-control-sm" data-field="fecha" required>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" class="form-control form-control-sm" data-field="descripcion" maxlength="255" placeholder="Dia no laborable">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-danger" data-remove="row">X</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
const diasCatalogo = @json($diasCatalogo ?? []);
|
||||||
|
const datosIniciales = {
|
||||||
|
dias: @json($filasAtencion ?? []),
|
||||||
|
recesos: @json($filasReceso ?? []),
|
||||||
|
vacaciones: @json($filasVacaciones ?? []),
|
||||||
|
feriados: @json($filasFeriados ?? []),
|
||||||
|
};
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
dias: { tbodyId: 'tablaDiasAtencion', tplId: 'tplDiaAtencion', key: 'dias' },
|
||||||
|
recesos: { tbodyId: 'tablaRecesos', tplId: 'tplReceso', key: 'recesos' },
|
||||||
|
vacaciones: { tbodyId: 'tablaVacaciones', tplId: 'tplVacacion', key: 'vacaciones' },
|
||||||
|
feriados: { tbodyId: 'tablaFeriados', tplId: 'tplFeriado', key: 'feriados' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const getBody = (tbodyId) => document.querySelector('#' + tbodyId + ' tbody');
|
||||||
|
|
||||||
|
const rellenarSelectDias = (selectEl) => {
|
||||||
|
if (!selectEl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectEl.innerHTML = '<option value="">Seleccionar</option>';
|
||||||
|
diasCatalogo.forEach((dia) => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = String(dia.id);
|
||||||
|
option.textContent = dia.descripcion;
|
||||||
|
selectEl.appendChild(option);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const renumerar = (key) => {
|
||||||
|
const body = getBody(config[key].tbodyId);
|
||||||
|
if (!body) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.querySelectorAll('tr').forEach((tr, index) => {
|
||||||
|
tr.querySelectorAll('[data-field]').forEach((input) => {
|
||||||
|
const campo = input.getAttribute('data-field');
|
||||||
|
input.name = key + '[' + index + '][' + campo + ']';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const agregarFila = (key, valores = {}) => {
|
||||||
|
if (key === 'dias') {
|
||||||
|
const bodyDias = getBody(config.dias.tbodyId);
|
||||||
|
if (bodyDias && bodyDias.querySelectorAll('tr').length >= 10) {
|
||||||
|
alert('Solo puedes cargar hasta 10 horarios de atencion.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = getBody(config[key].tbodyId);
|
||||||
|
const tpl = document.getElementById(config[key].tplId);
|
||||||
|
|
||||||
|
if (!body || !tpl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fragment = tpl.content.cloneNode(true);
|
||||||
|
const row = fragment.querySelector('tr');
|
||||||
|
const selectDia = row.querySelector('select[data-field="dia_id"]');
|
||||||
|
|
||||||
|
if (selectDia) {
|
||||||
|
rellenarSelectDias(selectDia);
|
||||||
|
}
|
||||||
|
|
||||||
|
row.querySelectorAll('[data-field]').forEach((input) => {
|
||||||
|
const field = input.getAttribute('data-field');
|
||||||
|
const valor = valores[field] ?? '';
|
||||||
|
input.value = valor;
|
||||||
|
});
|
||||||
|
|
||||||
|
const removeBtn = row.querySelector('[data-remove="row"]');
|
||||||
|
if (removeBtn) {
|
||||||
|
removeBtn.addEventListener('click', () => {
|
||||||
|
row.remove();
|
||||||
|
renumerar(key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
body.appendChild(row);
|
||||||
|
renumerar(key);
|
||||||
|
};
|
||||||
|
|
||||||
|
document.getElementById('btnAgregarDia')?.addEventListener('click', () => agregarFila('dias'));
|
||||||
|
document.getElementById('btnAgregarReceso')?.addEventListener('click', () => agregarFila('recesos'));
|
||||||
|
document.getElementById('btnAgregarVacacion')?.addEventListener('click', () => agregarFila('vacaciones'));
|
||||||
|
document.getElementById('btnAgregarFeriado')?.addEventListener('click', () => agregarFila('feriados'));
|
||||||
|
|
||||||
|
(datosIniciales.dias || []).forEach((fila) => agregarFila('dias', fila));
|
||||||
|
(datosIniciales.recesos || []).forEach((fila) => agregarFila('recesos', fila));
|
||||||
|
(datosIniciales.vacaciones || []).forEach((fila) => agregarFila('vacaciones', fila));
|
||||||
|
(datosIniciales.feriados || []).forEach((fila) => agregarFila('feriados', fila));
|
||||||
|
|
||||||
|
if ((datosIniciales.dias || []).length === 0) {
|
||||||
|
agregarFila('dias');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
@include('partials.reportar-falla-boton')
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,682 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Dashboard Profesional</title>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const root = document.documentElement;
|
||||||
|
root.classList.add('sidebar-nav-pending');
|
||||||
|
|
||||||
|
const releasePending = function () {
|
||||||
|
root.classList.remove('sidebar-nav-pending');
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeoutId = window.setTimeout(releasePending, 1200);
|
||||||
|
|
||||||
|
if (document.querySelector('.admin-sidebar')) {
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new MutationObserver(function () {
|
||||||
|
if (!document.querySelector('.admin-sidebar')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.disconnect();
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.documentElement, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
html.sidebar-nav-pending main {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
|
<style>
|
||||||
|
#agenda-profesional {
|
||||||
|
min-height: 620px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agenda-resumen {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-toolbar-title {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-bg-event.evento-disponibilidad {
|
||||||
|
opacity: 1 !important;
|
||||||
|
background-color: rgba(25, 135, 84, 0.24) !important;
|
||||||
|
border-color: rgba(25, 135, 84, 0.42) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-bg-event.evento-feriado {
|
||||||
|
opacity: 1 !important;
|
||||||
|
background-color: rgba(220, 53, 69, 0.30) !important;
|
||||||
|
border-color: rgba(220, 53, 69, 0.55) !important;
|
||||||
|
color: #5b1f23 !important;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-bg-event.evento-receso,
|
||||||
|
.fc .fc-bg-event.evento-vacaciones {
|
||||||
|
opacity: 1 !important;
|
||||||
|
background-color: rgba(220, 53, 69, 0.30) !important;
|
||||||
|
border-color: rgba(220, 53, 69, 0.55) !important;
|
||||||
|
color: #5b1f23 !important;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-bg-event.evento-feriado .fc-event-title,
|
||||||
|
.fc .fc-bg-event.evento-feriado .fc-bg-event-title,
|
||||||
|
.fc .fc-bg-event.evento-receso .fc-event-title,
|
||||||
|
.fc .fc-bg-event.evento-receso .fc-bg-event-title,
|
||||||
|
.fc .fc-bg-event.evento-vacaciones .fc-event-title,
|
||||||
|
.fc .fc-bg-event.evento-vacaciones .fc-bg-event-title {
|
||||||
|
color: #5b1f23 !important;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agenda-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.35rem;
|
||||||
|
padding: 0.45rem 0.7rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 600;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agenda-badge-hoy {
|
||||||
|
background: #0d6efd;
|
||||||
|
color: #fff;
|
||||||
|
border-color: #0a58ca;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agenda-badge-futuro {
|
||||||
|
background: #e7f1ff;
|
||||||
|
color: #0a58ca;
|
||||||
|
border-color: #b6d4fe;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 991.98px) {
|
||||||
|
#agenda-profesional {
|
||||||
|
min-height: 420px;
|
||||||
|
padding: 0.25rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-toolbar {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-toolbar-title {
|
||||||
|
font-size: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-button-group,
|
||||||
|
.fc .fc-toolbar-chunk {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-button {
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-col-header-cell-cushion,
|
||||||
|
.fc .fc-timegrid-axis-cushion,
|
||||||
|
.fc .fc-timegrid-slot-label-cushion {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agenda-resumen {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 575.98px) {
|
||||||
|
.agenda-acciones {
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agenda-acciones .btn,
|
||||||
|
.agenda-acciones .agenda-badge {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-toolbar-title {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body id="top" class="d-flex flex-column min-vh-100 bg-light">
|
||||||
|
<header class="app-navbar">
|
||||||
|
<nav class="navbar navbar-expand-lg">
|
||||||
|
<div class="container">
|
||||||
|
@php
|
||||||
|
$nombreSesion = trim((string) session('personal_nombre', 'Profesional'));
|
||||||
|
$nombreSaludo = $nombreSesion !== '' ? explode(' ', $nombreSesion)[0] : 'Profesional';
|
||||||
|
@endphp
|
||||||
|
<a class="navbar-brand d-flex align-items-center gap-2" href="/profesional/dashboard">
|
||||||
|
<div class="d-flex align-items-center justify-content-center" style="width: 130px; height: 52px;">
|
||||||
|
<img src="{{ asset('images/logo.png') }}" alt="Logo" class="img-fluid" style="max-height: 70px; width: auto; object-fit: contain;">
|
||||||
|
</div>
|
||||||
|
<span class="fw-semibold">¡Hola, {{ $nombreSaludo }}!</span>
|
||||||
|
</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="btn app-navbar-link" href="/profesional/dashboard">Mi Agenda</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/clientes">Mis Clientes</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/mis-datos">Mis Datos</a></li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn app-navbar-link position-relative" href="/profesional/formularios">
|
||||||
|
Revisar Formularios
|
||||||
|
@if(($formulariosPendientesCount ?? 0) > 0)
|
||||||
|
<span class="position-absolute top-0 start-100 translate-middle p-1 bg-danger rounded-circle">
|
||||||
|
<span class="visually-hidden">Hay formularios pendientes</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn app-navbar-link position-relative" href="/profesional/notificaciones" data-notificaciones-claves='@json($notificacionesClaves ?? [])'>
|
||||||
|
Notificaciones
|
||||||
|
@if(($notificacionesCount ?? 0) > 0)
|
||||||
|
<span class="position-absolute top-0 start-100 translate-middle p-1 bg-danger rounded-circle js-notificaciones-badge d-none">
|
||||||
|
<span class="visually-hidden">Hay notificaciones</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a class="btn app-navbar-link" href="/logout">Cerrar Sesion</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container py-4 flex-grow-1">
|
||||||
|
@if(session('turno_directo_success'))
|
||||||
|
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||||
|
{{ session('turno_directo_success') }}
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Cerrar"></button>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if(session('turno_directo_error'))
|
||||||
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||||
|
{{ session('turno_directo_error') }}
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Cerrar"></button>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if(session('profesional_action_success'))
|
||||||
|
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||||
|
{{ session('profesional_action_success') }}
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Cerrar"></button>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if(session('profesional_action_error'))
|
||||||
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||||
|
{{ session('profesional_action_error') }}
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Cerrar"></button>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if($errors->any())
|
||||||
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||||
|
<strong>Errores en el formulario:</strong>
|
||||||
|
<ul class="mb-0 mt-1">
|
||||||
|
@foreach($errors->all() as $error)
|
||||||
|
<li>{{ $error }}</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Cerrar"></button>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">
|
||||||
|
<h1 class="h4 mb-0">Mi Agenda</h1>
|
||||||
|
<div class="d-flex align-items-center gap-2 flex-wrap agenda-acciones">
|
||||||
|
<a href="/profesional/configurar-agenda" class="btn btn-outline-secondary btn-sm">Configurar mi agenda</a>
|
||||||
|
<button type="button" class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#modalAsignarTurno">Agendar un turno</button>
|
||||||
|
<span class="agenda-badge agenda-badge-hoy">📅 Hoy: {{ $cantidadTurnosHoy ?? 0 }}</span>
|
||||||
|
<span class="agenda-badge agenda-badge-futuro">🗓️ Futuros: {{ $cantidadTurnosFuturos ?? 0 }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-12 col-xl-9">
|
||||||
|
<div id="agenda-profesional"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-xl-3">
|
||||||
|
<div class="agenda-resumen p-3 h-100">
|
||||||
|
<h2 class="h6 mb-3">Detalle del turno</h2>
|
||||||
|
<div id="agenda-detalle" class="small text-muted">
|
||||||
|
Seleccioná un turno del calendario para ver la información.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<form id="formCancelarTurno" method="POST" action="" class="d-none">
|
||||||
|
@csrf
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<button id="abrirModalReprogramarTurno" type="button" class="d-none" data-bs-toggle="modal" data-bs-target="#modalReprogramarTurno"></button>
|
||||||
|
<button id="abrirModalAdvertenciaTurnoDirecto" type="button" class="d-none" data-bs-toggle="modal" data-bs-target="#modalAdvertenciaTurnoDirecto"></button>
|
||||||
|
<button id="abrirModalAdvertenciaReprogramarTurno" type="button" class="d-none" data-bs-toggle="modal" data-bs-target="#modalAdvertenciaReprogramarTurno"></button>
|
||||||
|
|
||||||
|
<div class="modal fade" id="modalReprogramarTurno" tabindex="-1" aria-labelledby="modalReprogramarTurnoLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<form id="formReprogramarTurno" method="POST" action="">
|
||||||
|
@csrf
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="modalReprogramarTurnoLabel">Reprogramar turno</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="rt_fecha" class="form-label">Nueva fecha</label>
|
||||||
|
<input type="date" class="form-control" id="rt_fecha" name="fecha_turno" min="{{ now()->toDateString() }}" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-0">
|
||||||
|
<label for="rt_hora" class="form-label">Nuevo horario</label>
|
||||||
|
<input type="time" class="form-control" id="rt_hora" name="hora_turno" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancelar</button>
|
||||||
|
<button type="submit" class="btn btn-primary">Guardar reprogramación</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
const agendaEl = document.getElementById('agenda-profesional');
|
||||||
|
const detalleEl = document.getElementById('agenda-detalle');
|
||||||
|
const eventos = @json($eventosAgenda ?? []);
|
||||||
|
const modalReprogramarEl = document.getElementById('modalReprogramarTurno');
|
||||||
|
const formReprogramar = document.getElementById('formReprogramarTurno');
|
||||||
|
const inputFechaReprogramar = document.getElementById('rt_fecha');
|
||||||
|
const inputHoraReprogramar = document.getElementById('rt_hora');
|
||||||
|
const botonAbrirModalReprogramar = document.getElementById('abrirModalReprogramarTurno');
|
||||||
|
|
||||||
|
if (!agendaEl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const escaparHtml = (valor) => {
|
||||||
|
return String(valor ?? '-')
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, ''');
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderDetalle = (event) => {
|
||||||
|
if (!detalleEl || !event) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = event.extendedProps || {};
|
||||||
|
if (props.tipoEvento === 'disponibilidad') {
|
||||||
|
const inicioDisponibilidad = event.start
|
||||||
|
? event.start.toLocaleString('es-AR', {
|
||||||
|
dateStyle: 'short',
|
||||||
|
timeStyle: 'short'
|
||||||
|
})
|
||||||
|
: '-';
|
||||||
|
|
||||||
|
detalleEl.innerHTML = `
|
||||||
|
<p class="mb-1"><strong>Tipo:</strong> Disponibilidad</p>
|
||||||
|
<p class="mb-1"><strong>Estado:</strong> ${escaparHtml(props.estado || 'Disponible')}</p>
|
||||||
|
<p class="mb-1"><strong>Inicio:</strong> ${escaparHtml(inicioDisponibilidad)}</p>
|
||||||
|
<p class="mb-0"><strong>Descripción:</strong> ${escaparHtml(props.descripcion || '-')}</p>
|
||||||
|
`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const inicio = event.start
|
||||||
|
? event.start.toLocaleString('es-AR', {
|
||||||
|
dateStyle: 'short',
|
||||||
|
timeStyle: 'short'
|
||||||
|
})
|
||||||
|
: '-';
|
||||||
|
|
||||||
|
const turnoIdSeguro = Number.isInteger(Number(props.turnoId)) ? String(props.turnoId) : '';
|
||||||
|
const fechaSeguro = typeof event.startStr === 'string' ? escaparHtml(event.startStr.slice(0, 10)) : '';
|
||||||
|
const horaSeguro = typeof event.startStr === 'string' ? escaparHtml(event.startStr.slice(11, 16)) : '';
|
||||||
|
|
||||||
|
detalleEl.innerHTML = `
|
||||||
|
<p class="mb-1"><strong>Servicio:</strong> ${escaparHtml(props.servicio || '-')}</p>
|
||||||
|
<p class="mb-1"><strong>Cliente:</strong> ${escaparHtml(props.cliente || '-')}</p>
|
||||||
|
<p class="mb-1"><strong>Celular:</strong> ${escaparHtml(props.celular || '-')}</p>
|
||||||
|
<p class="mb-1"><strong>Correo:</strong> ${escaparHtml(props.correo || '-')}</p>
|
||||||
|
<p class="mb-1"><strong>Modalidad:</strong> ${escaparHtml(props.modalidad || '-')}</p>
|
||||||
|
<p class="mb-1"><strong>Estado:</strong> ${escaparHtml(props.estado || '-')}</p>
|
||||||
|
<p class="mb-1"><strong>Inicio:</strong> ${escaparHtml(inicio)}</p>
|
||||||
|
<p class="mb-3"><strong>Descripción:</strong> ${escaparHtml(props.descripcion || '-')}</p>
|
||||||
|
${props.puedeReprogramarse && turnoIdSeguro ? `
|
||||||
|
<button type="button" class="btn btn-outline-primary btn-sm w-100 mb-2" data-reprogramar-turno-id="${turnoIdSeguro}" data-reprogramar-turno-fecha="${fechaSeguro}" data-reprogramar-turno-hora="${horaSeguro}">
|
||||||
|
Reprogramar turno
|
||||||
|
</button>
|
||||||
|
` : ''}
|
||||||
|
${props.puedeCancelarse && turnoIdSeguro ? `
|
||||||
|
<button type="button" class="btn btn-outline-danger btn-sm w-100" data-cancelar-turno-id="${turnoIdSeguro}">
|
||||||
|
Cancelar turno
|
||||||
|
</button>
|
||||||
|
` : ''}
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
detalleEl?.addEventListener('click', function (event) {
|
||||||
|
const botonReprogramar = event.target.closest('[data-reprogramar-turno-id]');
|
||||||
|
if (botonReprogramar) {
|
||||||
|
const turnoId = botonReprogramar.getAttribute('data-reprogramar-turno-id');
|
||||||
|
if (!turnoId || !formReprogramar || !inputFechaReprogramar || !inputHoraReprogramar || !modalReprogramarEl || !botonAbrirModalReprogramar) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputFechaReprogramar.value = botonReprogramar.getAttribute('data-reprogramar-turno-fecha') || '';
|
||||||
|
inputHoraReprogramar.value = botonReprogramar.getAttribute('data-reprogramar-turno-hora') || '';
|
||||||
|
formReprogramar.action = '/profesional/turnos/' + turnoId + '/reprogramar';
|
||||||
|
botonAbrirModalReprogramar.click();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const boton = event.target.closest('[data-cancelar-turno-id]');
|
||||||
|
if (!boton) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const turnoId = boton.getAttribute('data-cancelar-turno-id');
|
||||||
|
if (!turnoId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!window.confirm('¿Querés cancelar este turno?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const form = document.getElementById('formCancelarTurno');
|
||||||
|
if (!form) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
form.action = '/profesional/turnos/' + turnoId + '/cancelar';
|
||||||
|
form.submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
const esPantallaChica = window.innerWidth < 768;
|
||||||
|
|
||||||
|
const calendar = new FullCalendar.Calendar(agendaEl, {
|
||||||
|
locale: window.FullCalendarLocales?.es || 'es',
|
||||||
|
plugins: [
|
||||||
|
window.FullCalendarPlugins?.dayGridPlugin,
|
||||||
|
window.FullCalendarPlugins?.timeGridPlugin,
|
||||||
|
window.FullCalendarPlugins?.interactionPlugin,
|
||||||
|
].filter(Boolean),
|
||||||
|
initialView: esPantallaChica ? 'timeGridDay' : 'timeGridWeek',
|
||||||
|
headerToolbar: {
|
||||||
|
left: 'prev,next today',
|
||||||
|
center: 'title',
|
||||||
|
right: 'dayGridMonth,timeGridWeek,timeGridDay'
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
today: 'Hoy',
|
||||||
|
month: 'Mes',
|
||||||
|
week: 'Semana',
|
||||||
|
day: 'Día'
|
||||||
|
},
|
||||||
|
height: esPantallaChica ? 650 : 'auto',
|
||||||
|
contentHeight: esPantallaChica ? 650 : 'auto',
|
||||||
|
navLinks: true,
|
||||||
|
events: eventos,
|
||||||
|
eventDisplay: 'block',
|
||||||
|
eventOverlap: true,
|
||||||
|
eventOrder: 'start,-duration,allDay,title',
|
||||||
|
eventDidMount: function (info) {
|
||||||
|
const props = info.event.extendedProps || {};
|
||||||
|
if (props.tipoEvento) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const harness = info.el.closest('.fc-timegrid-event-harness');
|
||||||
|
if (harness) {
|
||||||
|
harness.style.left = '0px';
|
||||||
|
harness.style.right = '0px';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
eventClick: function (info) {
|
||||||
|
renderDetalle(info.event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
calendar.render();
|
||||||
|
|
||||||
|
if (eventos.length > 0) {
|
||||||
|
const primerEvento = calendar.getEventById(String(eventos[0].id));
|
||||||
|
if (primerEvento) {
|
||||||
|
renderDetalle(primerEvento);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{{-- Boton oculto para reabrir modal si hay errores de validacion --}}
|
||||||
|
@if($errors->any())
|
||||||
|
<button id="reabrirModalTurnoDirecto" class="d-none" data-bs-toggle="modal" data-bs-target="#modalAsignarTurno"></button>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- Modal: Asignar un turno --}}
|
||||||
|
<div class="modal fade" id="modalAsignarTurno" tabindex="-1" aria-labelledby="modalAsignarTurnoLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<form method="POST" action="/profesional/asignar-turno-directo">
|
||||||
|
@csrf
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="modalAsignarTurnoLabel">Asignar un turno</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="at_nombrecompleto" class="form-label">Nombre completo cliente</label>
|
||||||
|
<input type="text" class="form-control @error('nombrecompleto') is-invalid @enderror" id="at_nombrecompleto" name="nombrecompleto" value="{{ old('nombrecompleto') }}" required maxlength="200">
|
||||||
|
@error('nombrecompleto')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="at_correo" class="form-label">Correo electronico</label>
|
||||||
|
<input type="email" class="form-control @error('correo') is-invalid @enderror" id="at_correo" name="correo" value="{{ old('correo') }}" required maxlength="255">
|
||||||
|
@error('correo')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="at_celular" class="form-label">Celular del cliente</label>
|
||||||
|
<input type="text" class="form-control @error('celular') is-invalid @enderror" id="at_celular" name="celular" value="{{ old('celular') }}" required maxlength="15" inputmode="numeric" pattern="[0-9]{8,15}" title="Ingresá entre 8 y 15 números.">
|
||||||
|
@error('celular')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="at_descripcion" class="form-label">Descripción</label>
|
||||||
|
<textarea class="form-control @error('descripcion') is-invalid @enderror" id="at_descripcion" name="descripcion" rows="3" maxlength="1000" placeholder="Ingrese una descripción del turno">{{ old('descripcion') }}</textarea>
|
||||||
|
@error('descripcion')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="at_servicio" class="form-label">Servicio</label>
|
||||||
|
<select class="form-select @error('servicio_id') is-invalid @enderror" id="at_servicio" name="servicio_id" required>
|
||||||
|
<option value="">Seleccionar servicio</option>
|
||||||
|
@forelse($servicios ?? [] as $servicio)
|
||||||
|
<option value="{{ $servicio->id }}" {{ old('servicio_id') == $servicio->id ? 'selected' : '' }}>{{ $servicio->titulo }}</option>
|
||||||
|
@empty
|
||||||
|
<option value="" disabled>No hay servicios activos para tu profesión</option>
|
||||||
|
@endforelse
|
||||||
|
</select>
|
||||||
|
<div class="form-text">Solo se muestran servicios correspondientes a tu profesión.</div>
|
||||||
|
@error('servicio_id')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="at_modalidad" class="form-label">Modalidad</label>
|
||||||
|
<select class="form-select @error('modalidad_id') is-invalid @enderror" id="at_modalidad" name="modalidad_id" required>
|
||||||
|
<option value="">Seleccionar modalidad</option>
|
||||||
|
@foreach($modalidades ?? [] as $modalidad)
|
||||||
|
<option value="{{ $modalidad->id }}" {{ old('modalidad_id') == $modalidad->id ? 'selected' : '' }}>{{ $modalidad->descripcion }}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
@error('modalidad_id')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<label for="at_fecha" class="form-label">Fecha del turno</label>
|
||||||
|
<input type="date" class="form-control @error('fecha_turno') is-invalid @enderror" id="at_fecha" name="fecha_turno" value="{{ old('fecha_turno') }}" min="{{ now()->toDateString() }}" required>
|
||||||
|
@error('fecha_turno')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<label for="at_hora" class="form-label">Horario</label>
|
||||||
|
<input type="time" class="form-control @error('hora_turno') is-invalid @enderror" id="at_hora" name="hora_turno" value="{{ old('hora_turno') }}" required>
|
||||||
|
@error('hora_turno')<div class="invalid-feedback">{{ $message }}</div>@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancelar</button>
|
||||||
|
<button type="submit" class="btn btn-primary">Guardar turno</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@php
|
||||||
|
$turnoDirectoWarning = session('turno_directo_warning');
|
||||||
|
$reprogramarTurnoWarning = session('reprogramar_turno_warning');
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div class="modal fade" id="modalAdvertenciaTurnoDirecto" tabindex="-1" aria-labelledby="modalAdvertenciaTurnoDirectoLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="modalAdvertenciaTurnoDirectoLabel">Advertencia al asignar turno</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p class="mb-2">El día u horario elegido coincide con restricciones de agenda.</p>
|
||||||
|
<ul class="mb-0 ps-3">
|
||||||
|
@foreach(($turnoDirectoWarning['advertencias'] ?? []) as $advertencia)
|
||||||
|
<li>{{ $advertencia }}</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">No aceptar</button>
|
||||||
|
<form method="POST" action="/profesional/asignar-turno-directo" class="m-0">
|
||||||
|
@csrf
|
||||||
|
<input type="hidden" name="omitir_restricciones" value="1">
|
||||||
|
<input type="hidden" name="nombrecompleto" value="{{ $turnoDirectoWarning['inputs']['nombrecompleto'] ?? '' }}">
|
||||||
|
<input type="hidden" name="correo" value="{{ $turnoDirectoWarning['inputs']['correo'] ?? '' }}">
|
||||||
|
<input type="hidden" name="celular" value="{{ $turnoDirectoWarning['inputs']['celular'] ?? '' }}">
|
||||||
|
<input type="hidden" name="servicio_id" value="{{ $turnoDirectoWarning['inputs']['servicio_id'] ?? '' }}">
|
||||||
|
<input type="hidden" name="modalidad_id" value="{{ $turnoDirectoWarning['inputs']['modalidad_id'] ?? '' }}">
|
||||||
|
<input type="hidden" name="descripcion" value="{{ $turnoDirectoWarning['inputs']['descripcion'] ?? '' }}">
|
||||||
|
<input type="hidden" name="fecha_turno" value="{{ $turnoDirectoWarning['inputs']['fecha_turno'] ?? '' }}">
|
||||||
|
<input type="hidden" name="hora_turno" value="{{ $turnoDirectoWarning['inputs']['hora_turno'] ?? '' }}">
|
||||||
|
<button type="submit" class="btn btn-primary">Aceptar igualmente</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="modalAdvertenciaReprogramarTurno" tabindex="-1" aria-labelledby="modalAdvertenciaReprogramarTurnoLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="modalAdvertenciaReprogramarTurnoLabel">Advertencia al reprogramar turno</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p class="mb-2">El día u horario elegido coincide con restricciones de agenda.</p>
|
||||||
|
<ul class="mb-0 ps-3">
|
||||||
|
@foreach(($reprogramarTurnoWarning['advertencias'] ?? []) as $advertencia)
|
||||||
|
<li>{{ $advertencia }}</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">No aceptar</button>
|
||||||
|
<form method="POST" action="/profesional/turnos/{{ $reprogramarTurnoWarning['turno_id'] ?? 0 }}/reprogramar" class="m-0">
|
||||||
|
@csrf
|
||||||
|
<input type="hidden" name="omitir_restricciones" value="1">
|
||||||
|
<input type="hidden" name="fecha_turno" value="{{ $reprogramarTurnoWarning['inputs']['fecha_turno'] ?? '' }}">
|
||||||
|
<input type="hidden" name="hora_turno" value="{{ $reprogramarTurnoWarning['inputs']['hora_turno'] ?? '' }}">
|
||||||
|
<button type="submit" class="btn btn-primary">Aceptar igualmente</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
document.getElementById('reabrirModalTurnoDirecto')?.click();
|
||||||
|
|
||||||
|
@if($turnoDirectoWarning)
|
||||||
|
document.getElementById('abrirModalAdvertenciaTurnoDirecto')?.click();
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if($reprogramarTurnoWarning)
|
||||||
|
document.getElementById('abrirModalAdvertenciaReprogramarTurno')?.click();
|
||||||
|
@endif
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
@include('partials.reportar-falla-boton')
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,496 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Detalle Formulario - Profesional</title>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const root = document.documentElement;
|
||||||
|
root.classList.add('sidebar-nav-pending');
|
||||||
|
|
||||||
|
const releasePending = function () {
|
||||||
|
root.classList.remove('sidebar-nav-pending');
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeoutId = window.setTimeout(releasePending, 1200);
|
||||||
|
|
||||||
|
if (document.querySelector('.admin-sidebar')) {
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new MutationObserver(function () {
|
||||||
|
if (!document.querySelector('.admin-sidebar')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.disconnect();
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.documentElement, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
html.sidebar-nav-pending main {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
|
<style>
|
||||||
|
#agenda-modal-profesional {
|
||||||
|
min-height: 460px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-bg-event.evento-feriado {
|
||||||
|
opacity: 1 !important;
|
||||||
|
background-color: rgba(220, 53, 69, 0.30) !important;
|
||||||
|
border-color: rgba(220, 53, 69, 0.55) !important;
|
||||||
|
color: #5b1f23 !important;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-bg-event.evento-receso,
|
||||||
|
.fc .fc-bg-event.evento-vacaciones {
|
||||||
|
opacity: 1 !important;
|
||||||
|
background-color: rgba(220, 53, 69, 0.30) !important;
|
||||||
|
border-color: rgba(220, 53, 69, 0.55) !important;
|
||||||
|
color: #5b1f23 !important;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-bg-event.evento-disponibilidad {
|
||||||
|
opacity: 1 !important;
|
||||||
|
background-color: rgba(25, 135, 84, 0.24) !important;
|
||||||
|
border-color: rgba(25, 135, 84, 0.42) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc .fc-bg-event.evento-feriado .fc-event-title,
|
||||||
|
.fc .fc-bg-event.evento-feriado .fc-bg-event-title,
|
||||||
|
.fc .fc-bg-event.evento-receso .fc-event-title,
|
||||||
|
.fc .fc-bg-event.evento-receso .fc-bg-event-title,
|
||||||
|
.fc .fc-bg-event.evento-vacaciones .fc-event-title,
|
||||||
|
.fc .fc-bg-event.evento-vacaciones .fc-bg-event-title {
|
||||||
|
color: #5b1f23 !important;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body id="top" class="d-flex flex-column min-vh-100 bg-light">
|
||||||
|
<header class="app-navbar">
|
||||||
|
<nav class="navbar navbar-expand-lg">
|
||||||
|
<div class="container">
|
||||||
|
@php
|
||||||
|
$nombreSesion = trim((string) session('personal_nombre', 'Profesional'));
|
||||||
|
$nombreSaludo = $nombreSesion !== '' ? explode(' ', $nombreSesion)[0] : 'Profesional';
|
||||||
|
@endphp
|
||||||
|
<a class="navbar-brand d-flex align-items-center gap-2" href="/profesional/dashboard">
|
||||||
|
<div class="d-flex align-items-center justify-content-center" style="width: 130px; height: 52px;">
|
||||||
|
<img src="{{ asset('images/logo.png') }}" alt="Logo" class="img-fluid" style="max-height: 70px; width: auto; object-fit: contain;">
|
||||||
|
</div>
|
||||||
|
<span class="fw-semibold">¡Hola, {{ $nombreSaludo }}!</span>
|
||||||
|
</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="btn app-navbar-link" href="/profesional/dashboard">Mi Agenda</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/clientes">Mis Clientes</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/mis-datos">Mis Datos</a></li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn app-navbar-link position-relative" href="/profesional/formularios">
|
||||||
|
Revisar Formularios
|
||||||
|
@if(($formulariosPendientesCount ?? 0) > 0)
|
||||||
|
<span class="position-absolute top-0 start-100 translate-middle p-1 bg-danger rounded-circle js-notificaciones-badge">
|
||||||
|
<span class="visually-hidden">Hay formularios pendientes</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn app-navbar-link position-relative" href="/profesional/notificaciones" data-notificaciones-claves='@json($notificacionesClaves ?? [])'>
|
||||||
|
Notificaciones
|
||||||
|
@if(($notificacionesCount ?? 0) > 0)
|
||||||
|
<span class="position-absolute top-0 start-100 translate-middle p-1 bg-danger rounded-circle js-notificaciones-badge d-none">
|
||||||
|
<span class="visually-hidden">Hay notificaciones</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a class="btn app-navbar-link" href="/logout">Cerrar Sesion</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container py-4 flex-grow-1">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<h1 class="h4 mb-0">Detalle del Formulario</h1>
|
||||||
|
<a href="/profesional/formularios" class="btn btn-outline-secondary btn-sm">Volver a formularios</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if($errors->any())
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
<ul class="mb-0 ps-3">
|
||||||
|
@foreach($errors->all() as $error)
|
||||||
|
<li>{{ $error }}</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if(session('profesional_action_success'))
|
||||||
|
<div class="alert alert-success" role="alert">
|
||||||
|
{{ session('profesional_action_success') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if(session('profesional_action_error'))
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
{{ session('profesional_action_error') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if(session('formulario_turno_manual_warning'))
|
||||||
|
<div class="alert alert-warning" role="alert">
|
||||||
|
<p class="mb-2"><strong>Advertencias de agenda:</strong></p>
|
||||||
|
<ul class="mb-0 ps-3">
|
||||||
|
@foreach((session('formulario_turno_manual_warning')['advertencias'] ?? []) as $advertencia)
|
||||||
|
<li>{{ $advertencia }}</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="card border shadow-sm">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<p class="mb-1 text-muted small">Nombre</p>
|
||||||
|
<p class="mb-0 fw-semibold">{{ $formulario->nombrecompleto ?? '-' }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<p class="mb-1 text-muted small">Fecha de envío</p>
|
||||||
|
<p class="mb-0 fw-semibold">
|
||||||
|
{{ $formulario->created_at ? $formulario->created_at->format('d/m/Y H:i:s') : ($formulario->fechaenvio ? \Illuminate\Support\Carbon::parse($formulario->fechaenvio)->format('d/m/Y') : '-') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<p class="mb-1 text-muted small">Profesional de preferencia</p>
|
||||||
|
<p class="mb-0 fw-semibold">
|
||||||
|
@if($profesionalPreferido)
|
||||||
|
{{ trim(($profesionalPreferido->persona?->nombre ?? '') . ' ' . ($profesionalPreferido->persona?->apellido ?? '')) ?: ('Profesional #' . $profesionalPreferido->id) }}
|
||||||
|
@else
|
||||||
|
Indistinto
|
||||||
|
@endif
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<p class="mb-1 text-muted small">Mi estado en este formulario</p>
|
||||||
|
<p class="mb-0 fw-semibold">
|
||||||
|
@if(($formularioAceptadoPorOtro ?? false) && strcasecmp($estadoProfesionalFormulario, 'Aceptado') !== 0)
|
||||||
|
Aceptado por otro
|
||||||
|
@elseif(strcasecmp($estadoProfesionalFormulario, 'Rechazado') === 0)
|
||||||
|
Rechazado por mí
|
||||||
|
@elseif(strcasecmp($estadoProfesionalFormulario, 'Aceptado') === 0)
|
||||||
|
Aceptado por mí
|
||||||
|
@else
|
||||||
|
Pendiente
|
||||||
|
@endif
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<p class="mb-1 text-muted small">Estado del formulario</p>
|
||||||
|
@php
|
||||||
|
$estadoBaseFormulario = trim((string) ($formulario->estado ?? ''));
|
||||||
|
$estadoFormulario = (($formularioAceptadoPorOtro ?? false)
|
||||||
|
&& strcasecmp($estadoProfesionalFormulario, 'Aceptado') !== 0
|
||||||
|
&& strcasecmp($estadoBaseFormulario, 'Aceptado') === 0)
|
||||||
|
? 'Aceptado por otro'
|
||||||
|
: $estadoBaseFormulario;
|
||||||
|
$estadoClase = match (mb_strtolower($estadoFormulario)) {
|
||||||
|
'rechazado por todos' => 'text-bg-danger',
|
||||||
|
'aceptado' => 'text-bg-success',
|
||||||
|
'aceptado por otro' => 'text-bg-secondary',
|
||||||
|
default => 'text-bg-warning',
|
||||||
|
};
|
||||||
|
@endphp
|
||||||
|
<p class="mb-0">
|
||||||
|
<span class="badge {{ $estadoClase }}">{{ $estadoFormulario !== '' ? $estadoFormulario : '-' }}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<p class="mb-1 text-muted small">Descripción del formulario</p>
|
||||||
|
<div class="border rounded p-3 bg-light-subtle">
|
||||||
|
{{ $formulario->descripcion ?: 'Sin descripción.' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<p class="mb-1 text-muted small">Días de preferencia</p>
|
||||||
|
@if($formulario->diasPreferidos->isNotEmpty())
|
||||||
|
<div class="d-flex flex-wrap gap-2">
|
||||||
|
@foreach($formulario->diasPreferidos as $dia)
|
||||||
|
<span class="badge text-bg-primary">{{ $dia->descripcion }}</span>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
<p class="mb-0">-</p>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<p class="mb-1 text-muted small">Horario de preferencia</p>
|
||||||
|
@if($formulario->horariosPreferidos->isNotEmpty())
|
||||||
|
<div class="d-flex flex-wrap gap-2">
|
||||||
|
@foreach($formulario->horariosPreferidos as $horario)
|
||||||
|
<span class="badge text-bg-secondary">{{ $horario->descripcion }}</span>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
<p class="mb-0">-</p>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if($formularioAceptadoPorOtro ?? false)
|
||||||
|
<div class="alert alert-secondary mt-4 mb-0" role="alert">
|
||||||
|
Este formulario ya fue aceptado por otro profesional. No podés rechazarlo ni asignarle un turno.
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="d-flex flex-wrap justify-content-center gap-2 mt-4">
|
||||||
|
@if(!($formularioAceptadoPorOtro ?? false))
|
||||||
|
@if(strcasecmp($estadoProfesionalFormulario, 'Aceptado') === 0)
|
||||||
|
<form method="POST" action="/profesional/formularios/{{ $formulario->id }}/rechazar" onsubmit="return confirm('¿Confirmás devolver este formulario a pendiente?');">
|
||||||
|
@csrf
|
||||||
|
<button type="submit" class="btn btn-outline-secondary">Devolver formulario</button>
|
||||||
|
</form>
|
||||||
|
@else
|
||||||
|
<form method="POST" action="/profesional/formularios/{{ $formulario->id }}/rechazar" onsubmit="return confirm('¿Confirmás rechazar este formulario?');">
|
||||||
|
@csrf
|
||||||
|
<button type="submit" class="btn btn-outline-danger">Rechazar</button>
|
||||||
|
</form>
|
||||||
|
<button type="button" class="btn btn-outline-warning" data-bs-toggle="modal" data-bs-target="#modalAsignarTurnoManual">Asignar turno manual</button>
|
||||||
|
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#modalAsignarTurnoAutomatico">Asignar turno automático</button>
|
||||||
|
@endif
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<div class="modal fade" id="modalAsignarTurnoAutomatico" tabindex="-1" aria-labelledby="modalAsignarTurnoAutomaticoLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2 class="modal-title fs-5" id="modalAsignarTurnoAutomaticoLabel">Asignación automática de turno</h2>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p class="mb-3">La sugerencia se calcula desde el día siguiente y respetando la agenda configurada.</p>
|
||||||
|
|
||||||
|
@if($turnoAutomaticoSugerido)
|
||||||
|
<div class="alert alert-info mb-0" role="alert">
|
||||||
|
<p class="mb-1"><strong>Día sugerido:</strong> {{ $turnoAutomaticoSugerido['inicio']->format('d/m/Y') }}</p>
|
||||||
|
<p class="mb-0"><strong>Horario sugerido:</strong> {{ $turnoAutomaticoSugerido['inicio']->format('H:i') }}</p>
|
||||||
|
</div>
|
||||||
|
@else
|
||||||
|
<div class="alert alert-warning mb-0" role="alert">
|
||||||
|
No se encontró un turno disponible en los próximos 90 días desde mañana.
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">No aceptar</button>
|
||||||
|
@if($turnoAutomaticoSugerido)
|
||||||
|
<form method="POST" action="/profesional/formularios/{{ $formulario->id }}/asignar-turno-automatico" class="m-0">
|
||||||
|
@csrf
|
||||||
|
<button type="submit" class="btn btn-primary">Aceptar y asignar turno</button>
|
||||||
|
</form>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="modalAsignarTurnoManual" tabindex="-1" aria-labelledby="modalAsignarTurnoManualLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<form method="POST" action="/profesional/formularios/{{ $formulario->id }}/asignar-turno-manual">
|
||||||
|
@csrf
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2 class="modal-title fs-5" id="modalAsignarTurnoManualLabel">Asignar turno manual</h2>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="fecha_turno" class="form-label">Fecha de turno</label>
|
||||||
|
<input type="date" class="form-control" id="fecha_turno" name="fecha_turno" value="{{ old('fecha_turno') }}" min="{{ now()->toDateString() }}" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-0">
|
||||||
|
<label for="hora_turno" class="form-label">Horario</label>
|
||||||
|
<input type="time" class="form-control" id="hora_turno" name="hora_turno" value="{{ old('hora_turno') }}" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-outline-primary" data-bs-target="#modalAgendaProfesional" data-bs-toggle="modal" data-bs-dismiss="modal">Ver agenda</button>
|
||||||
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Cancelar</button>
|
||||||
|
<button type="submit" class="btn btn-primary">Guardar turno</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@php
|
||||||
|
$formularioTurnoManualWarning = session('formulario_turno_manual_warning');
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div class="modal fade" id="modalAdvertenciaAsignarTurnoManual" tabindex="-1" aria-labelledby="modalAdvertenciaAsignarTurnoManualLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2 class="modal-title fs-5" id="modalAdvertenciaAsignarTurnoManualLabel">Advertencia al asignar turno</h2>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p class="mb-2">El día u horario elegido coincide con restricciones de agenda.</p>
|
||||||
|
<ul class="mb-0 ps-3">
|
||||||
|
@foreach(($formularioTurnoManualWarning['advertencias'] ?? []) as $advertencia)
|
||||||
|
<li>{{ $advertencia }}</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">No aceptar</button>
|
||||||
|
<form method="POST" action="/profesional/formularios/{{ $formulario->id }}/asignar-turno-manual" class="m-0">
|
||||||
|
@csrf
|
||||||
|
<input type="hidden" name="omitir_restricciones" value="1">
|
||||||
|
<input type="hidden" name="fecha_turno" value="{{ $formularioTurnoManualWarning['inputs']['fecha_turno'] ?? '' }}">
|
||||||
|
<input type="hidden" name="hora_turno" value="{{ $formularioTurnoManualWarning['inputs']['hora_turno'] ?? '' }}">
|
||||||
|
<button type="submit" class="btn btn-primary">Aceptar igualmente</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="modalAgendaProfesional" tabindex="-1" aria-labelledby="modalAgendaProfesionalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-xl modal-dialog-scrollable">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2 class="modal-title fs-5" id="modalAgendaProfesionalLabel">Agenda del profesional</h2>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div id="agenda-modal-profesional"></div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-outline-secondary" data-bs-target="#modalAsignarTurnoManual" data-bs-toggle="modal" data-bs-dismiss="modal">Volver a asignar turno</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="button" id="openAsignarTurnoModal" class="d-none" data-bs-toggle="modal" data-bs-target="#modalAsignarTurnoManual"></button>
|
||||||
|
<button type="button" id="openAdvertenciaTurnoManualModal" class="d-none" data-bs-toggle="modal" data-bs-target="#modalAdvertenciaAsignarTurnoManual"></button>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
const eventosAgenda = @json($agendaEventosProfesional ?? []);
|
||||||
|
const modalAgendaEl = document.getElementById('modalAgendaProfesional');
|
||||||
|
const agendaEl = document.getElementById('agenda-modal-profesional');
|
||||||
|
|
||||||
|
let calendar = null;
|
||||||
|
|
||||||
|
if (modalAgendaEl && agendaEl) {
|
||||||
|
modalAgendaEl.addEventListener('shown.bs.modal', function () {
|
||||||
|
if (!calendar) {
|
||||||
|
calendar = new FullCalendar.Calendar(agendaEl, {
|
||||||
|
locale: window.FullCalendarLocales?.es || 'es',
|
||||||
|
plugins: [
|
||||||
|
window.FullCalendarPlugins?.dayGridPlugin,
|
||||||
|
window.FullCalendarPlugins?.timeGridPlugin,
|
||||||
|
window.FullCalendarPlugins?.interactionPlugin,
|
||||||
|
].filter(Boolean),
|
||||||
|
initialView: 'timeGridWeek',
|
||||||
|
headerToolbar: {
|
||||||
|
left: 'prev,next today',
|
||||||
|
center: 'title',
|
||||||
|
right: 'dayGridMonth,timeGridWeek,timeGridDay'
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
today: 'Hoy',
|
||||||
|
month: 'Mes',
|
||||||
|
week: 'Semana',
|
||||||
|
day: 'Día'
|
||||||
|
},
|
||||||
|
height: 'auto',
|
||||||
|
contentHeight: 'auto',
|
||||||
|
eventDisplay: 'block',
|
||||||
|
eventOverlap: true,
|
||||||
|
eventOrder: 'start,-duration,allDay,title',
|
||||||
|
eventDidMount: function (info) {
|
||||||
|
const props = info.event.extendedProps || {};
|
||||||
|
if (props.tipoEvento) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const harness = info.el.closest('.fc-timegrid-event-harness');
|
||||||
|
if (harness) {
|
||||||
|
harness.style.left = '0px';
|
||||||
|
harness.style.right = '0px';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
events: eventosAgenda,
|
||||||
|
});
|
||||||
|
calendar.render();
|
||||||
|
} else {
|
||||||
|
calendar.updateSize();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const abrirModalSeguro = (modalElement, triggerElement) => {
|
||||||
|
if (!modalElement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.bootstrap?.Modal) {
|
||||||
|
window.bootstrap.Modal.getOrCreateInstance(modalElement).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerElement?.click();
|
||||||
|
};
|
||||||
|
|
||||||
|
@if($formularioTurnoManualWarning)
|
||||||
|
const modalAdvertencia = document.getElementById('modalAdvertenciaAsignarTurnoManual');
|
||||||
|
const botonAbrirAdvertencia = document.getElementById('openAdvertenciaTurnoManualModal');
|
||||||
|
abrirModalSeguro(modalAdvertencia, botonAbrirAdvertencia);
|
||||||
|
@elseif($errors->any())
|
||||||
|
const openModalBtn = document.getElementById('openAsignarTurnoModal');
|
||||||
|
const modalAsignarTurnoManual = document.getElementById('modalAsignarTurnoManual');
|
||||||
|
abrirModalSeguro(modalAsignarTurnoManual, openModalBtn);
|
||||||
|
@endif
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
@include('partials.reportar-falla-boton')
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Documentación del Cliente - Profesional</title>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const root = document.documentElement;
|
||||||
|
root.classList.add('sidebar-nav-pending');
|
||||||
|
|
||||||
|
const releasePending = function () {
|
||||||
|
root.classList.remove('sidebar-nav-pending');
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeoutId = window.setTimeout(releasePending, 1200);
|
||||||
|
|
||||||
|
if (document.querySelector('.admin-sidebar')) {
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new MutationObserver(function () {
|
||||||
|
if (!document.querySelector('.admin-sidebar')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.disconnect();
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.documentElement, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
html.sidebar-nav-pending main {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
|
</head>
|
||||||
|
<body id="top" class="d-flex flex-column min-vh-100 bg-light">
|
||||||
|
<header class="app-navbar">
|
||||||
|
<nav class="navbar navbar-expand-lg">
|
||||||
|
<div class="container">
|
||||||
|
@php
|
||||||
|
$nombreSesion = trim((string) session('personal_nombre', 'Profesional'));
|
||||||
|
$nombreSaludo = $nombreSesion !== '' ? explode(' ', $nombreSesion)[0] : 'Profesional';
|
||||||
|
@endphp
|
||||||
|
<a class="navbar-brand d-flex align-items-center gap-2" href="/profesional/dashboard">
|
||||||
|
<div class="d-flex align-items-center justify-content-center" style="width: 130px; height: 52px;">
|
||||||
|
<img src="{{ asset('images/logo.png') }}" alt="Logo" class="img-fluid" style="max-height: 70px; width: auto; object-fit: contain;">
|
||||||
|
</div>
|
||||||
|
<span class="fw-semibold">¡Hola, {{ $nombreSaludo }}!</span>
|
||||||
|
</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="btn app-navbar-link" href="/profesional/dashboard">Mi Agenda</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/clientes">Mis Clientes</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/mis-datos">Mis Datos</a></li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn app-navbar-link position-relative" href="/profesional/formularios">
|
||||||
|
Revisar Formularios
|
||||||
|
@if(($formulariosPendientesCount ?? 0) > 0)
|
||||||
|
<span class="position-absolute top-0 start-100 translate-middle p-1 bg-danger rounded-circle js-notificaciones-badge">
|
||||||
|
<span class="visually-hidden">Hay formularios pendientes</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn app-navbar-link position-relative" href="/profesional/notificaciones" data-notificaciones-claves='@json($notificacionesClaves ?? [])'>
|
||||||
|
Notificaciones
|
||||||
|
@if(($notificacionesCount ?? 0) > 0)
|
||||||
|
<span class="position-absolute top-0 start-100 translate-middle p-1 bg-danger rounded-circle js-notificaciones-badge d-none">
|
||||||
|
<span class="visually-hidden">Hay notificaciones</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a class="btn app-navbar-link" href="/logout">Cerrar Sesion</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container py-4 flex-grow-1">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<div>
|
||||||
|
<h1 class="h4 mb-1">Documentación del Cliente</h1>
|
||||||
|
<p class="text-muted mb-0">
|
||||||
|
{{ trim(($cliente->persona?->nombre ?? '') . ' ' . ($cliente->persona?->apellido ?? '')) !== '' ? trim(($cliente->persona?->nombre ?? '') . ' ' . ($cliente->persona?->apellido ?? '')) : 'Cliente' }}
|
||||||
|
· DNI: {{ $cliente->dni }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<a href="/profesional/clientes" class="btn btn-outline-secondary btn-sm">Volver</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (session('success'))
|
||||||
|
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||||
|
{{ session('success') }}
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if ($errors->any())
|
||||||
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||||
|
<strong>¡Error!</strong>
|
||||||
|
<ul class="mb-0">
|
||||||
|
@foreach ($errors->all() as $error)
|
||||||
|
<li>{{ $error }}</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="row g-4">
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="h5 mb-3">Agregar documentación</h2>
|
||||||
|
<form action="/profesional/clientes/{{ $cliente->id }}/documentacion" method="POST" enctype="multipart/form-data" class="d-grid gap-3">
|
||||||
|
@csrf
|
||||||
|
<div>
|
||||||
|
<label for="documentos" class="form-label">Seleccionar archivos <span class="text-danger">*</span></label>
|
||||||
|
<input type="file" class="form-control @error('documentos') is-invalid @enderror @error('documentos.*') is-invalid @enderror" id="documentos" name="documentos[]" accept=".pdf,.jpg,.jpeg,.png,.webp,.doc,.docx,.xlsx,.txt" multiple required>
|
||||||
|
<div class="form-text">Podés subir PDF, imágenes, Word, Excel o TXT. Máximo 5 MB por archivo.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary">Subir documentación</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="h5 mb-3">Documentación actual</h2>
|
||||||
|
|
||||||
|
@if($documentos->isEmpty())
|
||||||
|
<div class="alert alert-secondary mb-0">Este cliente aún no tiene documentación cargada.</div>
|
||||||
|
@else
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped align-middle mb-0">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th>Archivo</th>
|
||||||
|
<th>Tipo</th>
|
||||||
|
<th>Tamaño</th>
|
||||||
|
<th>Fecha</th>
|
||||||
|
<th>Acciones</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach($documentos as $documento)
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="fw-semibold">{{ $documento->nombre_visible ?? $documento->nombre }}</div>
|
||||||
|
<small class="text-muted">{{ $documento->nombre }}</small>
|
||||||
|
</td>
|
||||||
|
<td>{{ strtoupper((string) $documento->extension) }}</td>
|
||||||
|
<td>{{ number_format(((int) $documento->tamanio_bytes) / 1024, 1, ',', '.') }} KB</td>
|
||||||
|
<td>{{ $documento->created_at ? $documento->created_at->format('d/m/Y H:i') : '-' }}</td>
|
||||||
|
<td>
|
||||||
|
<div class="d-flex flex-wrap gap-2">
|
||||||
|
@if($documento->archivo_existe)
|
||||||
|
<a href="{{ $documento->url_archivo }}" target="_blank" rel="noopener" class="btn btn-sm btn-outline-primary">Ver</a>
|
||||||
|
<a href="/profesional/clientes/{{ $cliente->id }}/documentacion/{{ $documento->id }}/descargar" class="btn btn-sm btn-outline-success">Descargar</a>
|
||||||
|
@else
|
||||||
|
<span class="badge text-bg-warning">Archivo no encontrado</span>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<form action="/profesional/clientes/{{ $cliente->id }}/documentacion/{{ $documento->id }}/eliminar" method="POST" onsubmit="return confirm('¿Confirmás eliminar este documento?');" class="d-inline">
|
||||||
|
@csrf
|
||||||
|
<button type="submit" class="btn btn-sm btn-outline-danger">Eliminar</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
@include('partials.reportar-falla-boton')
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Editar Cliente - Profesional</title>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const root = document.documentElement;
|
||||||
|
root.classList.add('sidebar-nav-pending');
|
||||||
|
|
||||||
|
const releasePending = function () {
|
||||||
|
root.classList.remove('sidebar-nav-pending');
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeoutId = window.setTimeout(releasePending, 1200);
|
||||||
|
|
||||||
|
if (document.querySelector('.admin-sidebar')) {
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new MutationObserver(function () {
|
||||||
|
if (!document.querySelector('.admin-sidebar')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.disconnect();
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.documentElement, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
html.sidebar-nav-pending main {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
|
</head>
|
||||||
|
<body id="top" class="d-flex flex-column min-vh-100 bg-light">
|
||||||
|
<header class="app-navbar">
|
||||||
|
<nav class="navbar navbar-expand-lg">
|
||||||
|
<div class="container">
|
||||||
|
@php
|
||||||
|
$nombreSesion = trim((string) session('personal_nombre', 'Profesional'));
|
||||||
|
$nombreSaludo = $nombreSesion !== '' ? explode(' ', $nombreSesion)[0] : 'Profesional';
|
||||||
|
@endphp
|
||||||
|
<a class="navbar-brand d-flex align-items-center gap-2" href="/profesional/dashboard">
|
||||||
|
<div class="d-flex align-items-center justify-content-center" style="width: 130px; height: 52px;">
|
||||||
|
<img src="{{ asset('images/logo.png') }}" alt="Logo" class="img-fluid" style="max-height: 70px; width: auto; object-fit: contain;">
|
||||||
|
</div>
|
||||||
|
<span class="fw-semibold">¡Hola, {{ $nombreSaludo }}!</span>
|
||||||
|
</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="btn app-navbar-link" href="/profesional/dashboard">Mi Agenda</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/clientes">Mis Clientes</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/mis-datos">Mis Datos</a></li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn app-navbar-link position-relative" href="/profesional/formularios">
|
||||||
|
Revisar Formularios
|
||||||
|
@if(($formulariosPendientesCount ?? 0) > 0)
|
||||||
|
<span class="position-absolute top-0 start-100 translate-middle p-1 bg-danger rounded-circle js-notificaciones-badge">
|
||||||
|
<span class="visually-hidden">Hay formularios pendientes</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn app-navbar-link position-relative" href="/profesional/notificaciones" data-notificaciones-claves='@json($notificacionesClaves ?? [])'>
|
||||||
|
Notificaciones
|
||||||
|
@if(($notificacionesCount ?? 0) > 0)
|
||||||
|
<span class="position-absolute top-0 start-100 translate-middle p-1 bg-danger rounded-circle js-notificaciones-badge d-none">
|
||||||
|
<span class="visually-hidden">Hay notificaciones</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a class="btn app-navbar-link" href="/logout">Cerrar Sesion</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container py-4 flex-grow-1">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<h1 class="h4 mb-0">Editar Cliente</h1>
|
||||||
|
<a href="/profesional/clientes" class="btn btn-outline-secondary btn-sm">Volver</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-body">
|
||||||
|
@if ($errors->any())
|
||||||
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||||
|
<strong>¡Error!</strong>
|
||||||
|
<ul class="mb-0">
|
||||||
|
@foreach ($errors->all() as $error)
|
||||||
|
<li>{{ $error }}</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<form action="/profesional/clientes/{{ $cliente->id }}/actualizar" method="POST">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="dni" class="form-label">DNI <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" class="form-control @error('dni') is-invalid @enderror" id="dni" name="dni" value="{{ old('dni', $cliente->dni) }}" required maxlength="20" pattern="[0-9A-Za-z]{7,20}" title="Números y letras, mínimo 7 y máximo 20 caracteres">
|
||||||
|
@error('dni')
|
||||||
|
<div class="invalid-feedback d-block">{{ $message }}</div>
|
||||||
|
@else
|
||||||
|
<small class="text-muted d-block mt-1">Números y letras: 7 a 20 caracteres</small>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="correo" class="form-label">Correo Electrónico <span class="text-danger">*</span></label>
|
||||||
|
<input type="email" class="form-control @error('correo') is-invalid @enderror" id="correo" name="correo" value="{{ old('correo', $cliente->correo) }}" required maxlength="255" title="Debe ser un correo electrónico válido (ej: usuario@ejemplo.com)">
|
||||||
|
@error('correo')
|
||||||
|
<div class="invalid-feedback d-block">{{ $message }}</div>
|
||||||
|
@else
|
||||||
|
<small class="text-muted d-block mt-1">Formato: usuario@ejemplo.com</small>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="nombre" class="form-label">Nombre <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" class="form-control @error('nombre') is-invalid @enderror" id="nombre" name="nombre" value="{{ old('nombre', $cliente->persona?->nombre) }}" required maxlength="100" pattern="[A-Za-zÁÉÍÓÚáéíóúÑñ\s]+" title="Solo letras y espacios (máximo 100 caracteres)">
|
||||||
|
@error('nombre')
|
||||||
|
<div class="invalid-feedback d-block">{{ $message }}</div>
|
||||||
|
@else
|
||||||
|
<small class="text-muted d-block mt-1">Solo letras y espacios</small>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="apellido" class="form-label">Apellido <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" class="form-control @error('apellido') is-invalid @enderror" id="apellido" name="apellido" value="{{ old('apellido', $cliente->persona?->apellido) }}" required maxlength="100" pattern="[A-Za-zÁÉÍÓÚáéíóúÑñ\s]+" title="Solo letras y espacios (máximo 100 caracteres)">
|
||||||
|
@error('apellido')
|
||||||
|
<div class="invalid-feedback d-block">{{ $message }}</div>
|
||||||
|
@else
|
||||||
|
<small class="text-muted d-block mt-1">Solo letras y espacios</small>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="cuil" class="form-label">CUIL <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" class="form-control @error('cuil') is-invalid @enderror" id="cuil" name="cuil" value="{{ old('cuil', $cliente->persona?->cuil) }}" required maxlength="11" inputmode="numeric" pattern="[0-9]{11}" title="Solo números: exactamente 11 dígitos">
|
||||||
|
@error('cuil')
|
||||||
|
<div class="invalid-feedback d-block">{{ $message }}</div>
|
||||||
|
@else
|
||||||
|
<small class="text-muted d-block mt-1">Solo números: 11 dígitos exactos</small>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label for="fechanac" class="form-label">Fecha de Nacimiento <span class="text-danger">*</span></label>
|
||||||
|
<input type="date" class="form-control @error('fechanac') is-invalid @enderror" id="fechanac" name="fechanac" value="{{ old('fechanac', $cliente->persona?->fechanac) }}" required title="Selecciona una fecha válida, no puede ser futura">
|
||||||
|
@error('fechanac')
|
||||||
|
<div class="invalid-feedback d-block">{{ $message }}</div>
|
||||||
|
@else
|
||||||
|
<small class="text-muted d-block mt-1">Fecha válida, no puede ser futura</small>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="telefono" class="form-label">Teléfono/Celular <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" class="form-control @error('telefono') is-invalid @enderror" id="telefono" name="telefono" value="{{ old('telefono', $telefonoActual?->telefono) }}" required maxlength="15" inputmode="numeric" pattern="[0-9]{8,15}" title="Solo números: 8 a 15 dígitos">
|
||||||
|
@error('telefono')
|
||||||
|
<div class="invalid-feedback d-block">{{ $message }}</div>
|
||||||
|
@else
|
||||||
|
<small class="text-muted d-block mt-1">Solo números: 8 a 15 dígitos</small>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex gap-2 justify-content-center">
|
||||||
|
<button type="submit" class="btn btn-primary">Guardar cambios</button>
|
||||||
|
<a href="/profesional/clientes" class="btn btn-secondary">Cancelar</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
@include('partials.reportar-falla-boton')
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Mis Clientes - Profesional</title>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const root = document.documentElement;
|
||||||
|
root.classList.add('sidebar-nav-pending');
|
||||||
|
|
||||||
|
const releasePending = function () {
|
||||||
|
root.classList.remove('sidebar-nav-pending');
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeoutId = window.setTimeout(releasePending, 1200);
|
||||||
|
|
||||||
|
if (document.querySelector('.admin-sidebar')) {
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new MutationObserver(function () {
|
||||||
|
if (!document.querySelector('.admin-sidebar')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.disconnect();
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.documentElement, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
html.sidebar-nav-pending main {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
|
</head>
|
||||||
|
<body id="top" class="d-flex flex-column min-vh-100 bg-light">
|
||||||
|
<header class="app-navbar">
|
||||||
|
<nav class="navbar navbar-expand-lg">
|
||||||
|
<div class="container">
|
||||||
|
@php
|
||||||
|
$nombreSesion = trim((string) session('personal_nombre', 'Profesional'));
|
||||||
|
$nombreSaludo = $nombreSesion !== '' ? explode(' ', $nombreSesion)[0] : 'Profesional';
|
||||||
|
@endphp
|
||||||
|
<a class="navbar-brand d-flex align-items-center gap-2" href="/profesional/dashboard">
|
||||||
|
<div class="d-flex align-items-center justify-content-center" style="width: 130px; height: 52px;">
|
||||||
|
<img src="{{ asset('images/logo.png') }}" alt="Logo" class="img-fluid" style="max-height: 70px; width: auto; object-fit: contain;">
|
||||||
|
</div>
|
||||||
|
<span class="fw-semibold">¡Hola, {{ $nombreSaludo }}!</span>
|
||||||
|
</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="btn app-navbar-link" href="/profesional/dashboard">Mi Agenda</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/clientes">Mis Clientes</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/mis-datos">Mis Datos</a></li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn app-navbar-link position-relative" href="/profesional/formularios">
|
||||||
|
Revisar Formularios
|
||||||
|
@if(($formulariosPendientesCount ?? 0) > 0)
|
||||||
|
<span class="position-absolute top-0 start-100 translate-middle p-1 bg-danger rounded-circle js-notificaciones-badge">
|
||||||
|
<span class="visually-hidden">Hay formularios pendientes</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn app-navbar-link position-relative" href="/profesional/notificaciones" data-notificaciones-claves='@json($notificacionesClaves ?? [])'>
|
||||||
|
Notificaciones
|
||||||
|
@if(($notificacionesCount ?? 0) > 0)
|
||||||
|
<span class="position-absolute top-0 start-100 translate-middle p-1 bg-danger rounded-circle js-notificaciones-badge d-none">
|
||||||
|
<span class="visually-hidden">Hay notificaciones</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a class="btn app-navbar-link" href="/logout">Cerrar Sesion</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container py-4 flex-grow-1">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<h1 class="h4 mb-0">Mis Clientes</h1>
|
||||||
|
<a href="/profesional/dashboard" class="btn btn-outline-secondary btn-sm">Volver</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card border shadow-sm mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="GET" action="/profesional/clientes" class="row g-3 align-items-end">
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<label for="q" class="form-label">Buscar cliente</label>
|
||||||
|
<input id="q" name="q" type="text" class="form-control" value="{{ request('q') }}" placeholder="Nombre, apellido, DNI o correo">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-3">
|
||||||
|
<label for="estado" class="form-label">Estado</label>
|
||||||
|
<select id="estado" name="estado" class="form-select">
|
||||||
|
<option value="">Todos</option>
|
||||||
|
<option value="activos" @selected(request('estado') === 'activos')>Activos</option>
|
||||||
|
<option value="inactivos" @selected(request('estado') === 'inactivos')>Inactivos</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-3 d-flex gap-2">
|
||||||
|
<button type="submit" class="btn btn-primary w-100">Filtrar</button>
|
||||||
|
<a href="/profesional/clientes" class="btn btn-outline-secondary w-100">Limpiar</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive bg-white border rounded shadow-sm">
|
||||||
|
<table class="table table-striped table-hover mb-0 align-middle">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Nombre</th>
|
||||||
|
<th>Apellido</th>
|
||||||
|
<th>DNI</th>
|
||||||
|
<th>Correo</th>
|
||||||
|
<th>Estado</th>
|
||||||
|
<th>Acciones</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@forelse($clientes as $cliente)
|
||||||
|
@php
|
||||||
|
$persona = $cliente->persona;
|
||||||
|
@endphp
|
||||||
|
<tr>
|
||||||
|
<td>{{ $cliente->id }}</td>
|
||||||
|
<td>{{ $persona?->nombre ?? '-' }}</td>
|
||||||
|
<td>{{ $persona?->apellido ?? '-' }}</td>
|
||||||
|
<td>{{ $cliente->dni }}</td>
|
||||||
|
<td>{{ $cliente->correo }}</td>
|
||||||
|
<td>
|
||||||
|
@if((int) $cliente->baja_id === 1)
|
||||||
|
<span class="badge text-bg-success">Activo</span>
|
||||||
|
@else
|
||||||
|
<span class="badge text-bg-danger">Inactivo</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="d-flex flex-wrap gap-2">
|
||||||
|
<a href="/profesional/clientes/{{ $cliente->id }}/editar" class="btn btn-sm btn-outline-primary">Editar</a>
|
||||||
|
<form method="POST" action="/profesional/clientes/{{ $cliente->id }}/baja" onsubmit="return confirm('{{ (int) $cliente->baja_id !== 1 ? '¿Confirmás reactivar a este cliente?' : '¿Confirmás dar de baja a este cliente?' }}');" class="d-inline">
|
||||||
|
@csrf
|
||||||
|
<button type="submit" class="btn btn-sm {{ (int) $cliente->baja_id !== 1 ? 'btn-outline-success' : 'btn-outline-danger' }}">
|
||||||
|
{{ (int) $cliente->baja_id !== 1 ? 'Alta' : 'Baja' }}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<a href="/profesional/clientes/{{ $cliente->id }}/documentacion" class="btn btn-sm btn-outline-secondary">Docs</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@empty
|
||||||
|
<tr>
|
||||||
|
<td colspan="7" class="text-center text-muted py-4">No hay clientes cargados.</td>
|
||||||
|
</tr>
|
||||||
|
@endforelse
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if($clientes->hasPages())
|
||||||
|
<div class="d-flex justify-content-center mt-3">
|
||||||
|
{{ $clientes->links() }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="mt-3 text-center">
|
||||||
|
<a href="/profesional/clientes/crear" class="btn btn-primary">Agregar nuevo cliente</a>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
@include('partials.reportar-falla-boton')
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Mis Datos - Profesional</title>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const root = document.documentElement;
|
||||||
|
root.classList.add('sidebar-nav-pending');
|
||||||
|
|
||||||
|
const releasePending = function () {
|
||||||
|
root.classList.remove('sidebar-nav-pending');
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeoutId = window.setTimeout(releasePending, 1200);
|
||||||
|
|
||||||
|
if (document.querySelector('.admin-sidebar')) {
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new MutationObserver(function () {
|
||||||
|
if (!document.querySelector('.admin-sidebar')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.disconnect();
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.documentElement, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
html.sidebar-nav-pending main {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
|
</head>
|
||||||
|
<body id="top" class="d-flex flex-column min-vh-100 bg-light">
|
||||||
|
<header class="app-navbar">
|
||||||
|
<nav class="navbar navbar-expand-lg">
|
||||||
|
<div class="container">
|
||||||
|
@php
|
||||||
|
$nombreSesion = trim((string) session('personal_nombre', 'Profesional'));
|
||||||
|
$nombreSaludo = $nombreSesion !== '' ? explode(' ', $nombreSesion)[0] : 'Profesional';
|
||||||
|
@endphp
|
||||||
|
<a class="navbar-brand d-flex align-items-center gap-2" href="/profesional/dashboard">
|
||||||
|
<div class="d-flex align-items-center justify-content-center" style="width: 130px; height: 52px;">
|
||||||
|
<img src="{{ asset('images/logo.png') }}" alt="Logo" class="img-fluid" style="max-height: 70px; width: auto; object-fit: contain;">
|
||||||
|
</div>
|
||||||
|
<span class="fw-semibold">¡Hola, {{ $nombreSaludo }}!</span>
|
||||||
|
</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="btn app-navbar-link" href="/profesional/dashboard">Mi Agenda</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/clientes">Mis Clientes</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/mis-datos">Mis Datos</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/formularios">Revisar Formularios</a></li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn app-navbar-link position-relative" href="/profesional/notificaciones" data-notificaciones-claves='@json($notificacionesClaves ?? [])'>
|
||||||
|
Notificaciones
|
||||||
|
@if(($notificacionesCount ?? 0) > 0)
|
||||||
|
<span class="position-absolute top-0 start-100 translate-middle p-1 bg-danger rounded-circle js-notificaciones-badge d-none">
|
||||||
|
<span class="visually-hidden">Hay notificaciones</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a class="btn app-navbar-link" href="/logout">Cerrar Sesion</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container py-4 flex-grow-1">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-12 col-lg-8">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<h1 class="h4 mb-0">Mis Datos</h1>
|
||||||
|
<a href="/profesional/dashboard" class="btn btn-outline-secondary btn-sm">Volver</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card border shadow-sm">
|
||||||
|
<div class="card-body">
|
||||||
|
@php
|
||||||
|
$persona = $profesional->persona;
|
||||||
|
$fechaNac = $persona?->fechanac
|
||||||
|
? \Illuminate\Support\Carbon::parse($persona->fechanac)->format('d/m/Y')
|
||||||
|
: '';
|
||||||
|
$profesionTitulo = $profesional->profesion?->titulo ?? '';
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<label class="form-label">Nombre</label>
|
||||||
|
<input type="text" class="form-control bg-light" value="{{ $persona?->nombre ?? '' }}" readonly>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<label class="form-label">Apellido</label>
|
||||||
|
<input type="text" class="form-control bg-light" value="{{ $persona?->apellido ?? '' }}" readonly>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<label class="form-label">DNI</label>
|
||||||
|
<input type="text" class="form-control bg-light" value="{{ $profesional->dni }}" readonly>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<label class="form-label">CUIL</label>
|
||||||
|
<input type="text" class="form-control bg-light" value="{{ $persona?->cuil ?? '' }}" readonly>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<label class="form-label">Fecha de nacimiento</label>
|
||||||
|
<input type="text" class="form-control bg-light" value="{{ $fechaNac }}" readonly>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<label class="form-label">Correo</label>
|
||||||
|
<input type="text" class="form-control bg-light" value="{{ $profesional->correo }}" readonly>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<label class="form-label">Matrícula</label>
|
||||||
|
<input type="text" class="form-control bg-light" value="{{ $profesional->matricula }}" readonly>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<label class="form-label">Profesión</label>
|
||||||
|
<input type="text" class="form-control bg-light" value="{{ $profesionTitulo }}" readonly>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<label class="form-label">Usuario</label>
|
||||||
|
<input type="text" class="form-control bg-light" value="{{ $profesional->credencialProfesional?->usuario ?? '' }}" readonly>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<label class="form-label">Rol</label>
|
||||||
|
<input type="text" class="form-control bg-light" value="{{ $profesional->credencialProfesional?->rol ?? '' }}" readonly>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
@include('partials.reportar-falla-boton')
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,275 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Notificaciones - Profesional</title>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const root = document.documentElement;
|
||||||
|
root.classList.add('sidebar-nav-pending');
|
||||||
|
|
||||||
|
const releasePending = function () {
|
||||||
|
root.classList.remove('sidebar-nav-pending');
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeoutId = window.setTimeout(releasePending, 1200);
|
||||||
|
|
||||||
|
if (document.querySelector('.admin-sidebar')) {
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new MutationObserver(function () {
|
||||||
|
if (!document.querySelector('.admin-sidebar')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.disconnect();
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.documentElement, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
html.sidebar-nav-pending main {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
|
</head>
|
||||||
|
<body id="top" class="d-flex flex-column min-vh-100 bg-light">
|
||||||
|
<header class="app-navbar">
|
||||||
|
<nav class="navbar navbar-expand-lg">
|
||||||
|
<div class="container">
|
||||||
|
@php
|
||||||
|
$nombreSesion = trim((string) session('personal_nombre', 'Profesional'));
|
||||||
|
$nombreSaludo = $nombreSesion !== '' ? explode(' ', $nombreSesion)[0] : 'Profesional';
|
||||||
|
@endphp
|
||||||
|
<a class="navbar-brand d-flex align-items-center gap-2" href="/profesional/dashboard">
|
||||||
|
<div class="d-flex align-items-center justify-content-center" style="width: 130px; height: 52px;">
|
||||||
|
<img src="{{ asset('images/logo.png') }}" alt="Logo" class="img-fluid" style="max-height: 70px; width: auto; object-fit: contain;">
|
||||||
|
</div>
|
||||||
|
<span class="fw-semibold">¡Hola, {{ $nombreSaludo }}!</span>
|
||||||
|
</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="btn app-navbar-link" href="/profesional/dashboard">Mi Agenda</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/clientes">Mis Clientes</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/mis-datos">Mis Datos</a></li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn app-navbar-link position-relative" href="/profesional/formularios">
|
||||||
|
Revisar Formularios
|
||||||
|
@if(($formulariosPendientesCount ?? 0) > 0)
|
||||||
|
<span class="position-absolute top-0 start-100 translate-middle p-1 bg-danger rounded-circle js-notificaciones-badge">
|
||||||
|
<span class="visually-hidden">Hay formularios pendientes</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn app-navbar-link active position-relative" href="/profesional/notificaciones" data-notificaciones-claves='@json($notificacionesClaves ?? [])'>
|
||||||
|
Notificaciones
|
||||||
|
@if(($notificacionesCount ?? 0) > 0)
|
||||||
|
<span class="position-absolute top-0 start-100 translate-middle p-1 bg-danger rounded-circle js-notificaciones-badge d-none">
|
||||||
|
<span class="visually-hidden">Hay notificaciones</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a class="btn app-navbar-link" href="/logout">Cerrar Sesion</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container py-4 flex-grow-1">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<h1 class="h4 mb-0">Notificaciones</h1>
|
||||||
|
<a href="/profesional/dashboard" class="btn btn-outline-secondary btn-sm">Volver</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@php $totalNotificaciones = count($notificaciones ?? []); @endphp
|
||||||
|
|
||||||
|
<div id="contenedorNotificaciones">
|
||||||
|
@if($totalNotificaciones === 0)
|
||||||
|
<div class="alert alert-secondary" id="mensajeSinNotificaciones">No tenés notificaciones pendientes.</div>
|
||||||
|
@else
|
||||||
|
<div class="d-flex flex-column gap-3" id="listaNotificaciones">
|
||||||
|
@foreach($notificaciones as $index => $notif)
|
||||||
|
@php
|
||||||
|
$iconoClase = match($notif['tipo']) {
|
||||||
|
'turno_hoy' => 'bg-primary text-white',
|
||||||
|
'turno_manana' => 'bg-info text-dark',
|
||||||
|
'turno_cancelado' => 'bg-danger text-white',
|
||||||
|
default => 'bg-light text-dark',
|
||||||
|
};
|
||||||
|
$icono = match($notif['tipo']) {
|
||||||
|
'turno_hoy' => 'Hoy',
|
||||||
|
'turno_manana' => 'Manana',
|
||||||
|
'turno_cancelado' => 'X',
|
||||||
|
default => 'Aviso',
|
||||||
|
};
|
||||||
|
$claveNotif = $notif['clave'] ?? base64_encode($notif['tipo'] . '|' . $notif['titulo'] . '|' . $notif['fecha']);
|
||||||
|
@endphp
|
||||||
|
<div class="card border shadow-sm notif-card" data-clave="{{ $claveNotif }}">
|
||||||
|
<div class="card-body d-flex align-items-start gap-3">
|
||||||
|
<div class="rounded-circle d-flex align-items-center justify-content-center flex-shrink-0 {{ $iconoClase }}" style="width:42px;height:42px;font-size:1.2rem;">
|
||||||
|
{{ $icono }}
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<p class="fw-semibold mb-1">{{ $notif['titulo'] }}</p>
|
||||||
|
<p class="text-muted small mb-2">{{ $notif['descripcion'] }}</p>
|
||||||
|
@if(!empty($notif['enlace']))
|
||||||
|
<a href="{{ $notif['enlace'] }}" class="btn btn-sm btn-outline-primary">Ver detalle</a>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<div class="d-flex flex-column align-items-end gap-2 flex-shrink-0">
|
||||||
|
<span class="text-muted small">{{ $notif['fecha'] }}</span>
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-outline-secondary btn-sm btn-cerrar-notif"
|
||||||
|
data-clave="{{ $claveNotif }}"
|
||||||
|
title="Cerrar notificación">
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
<div class="alert alert-secondary mt-3 d-none" id="mensajeSinNotificaciones">No tenés notificaciones pendientes.</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
@include('partials.reportar-falla-boton')
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const STORAGE_KEY = 'notif_cerradas_profesional';
|
||||||
|
const STORAGE_KEY_VISTAS = 'notif_vistas_profesional';
|
||||||
|
|
||||||
|
function getCerradas() {
|
||||||
|
try {
|
||||||
|
return JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function guardarCerradas(arr) {
|
||||||
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(arr));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVistas() {
|
||||||
|
try {
|
||||||
|
return JSON.parse(localStorage.getItem(STORAGE_KEY_VISTAS) || '[]');
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function guardarVistas(arr) {
|
||||||
|
localStorage.setItem(STORAGE_KEY_VISTAS, JSON.stringify(Array.from(new Set(arr))));
|
||||||
|
}
|
||||||
|
|
||||||
|
function ocultarCerradas() {
|
||||||
|
const cerradas = getCerradas();
|
||||||
|
if (!cerradas.length) return;
|
||||||
|
document.querySelectorAll('.notif-card').forEach(function (card) {
|
||||||
|
if (cerradas.includes(card.dataset.clave)) {
|
||||||
|
card.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
verificarVacio();
|
||||||
|
if (typeof window.actualizarBadgeNotificaciones === 'function') {
|
||||||
|
window.actualizarBadgeNotificaciones();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function verificarVacio() {
|
||||||
|
const lista = document.getElementById('listaNotificaciones');
|
||||||
|
const mensaje = document.getElementById('mensajeSinNotificaciones');
|
||||||
|
if (lista && lista.querySelectorAll('.notif-card').length === 0 && mensaje) {
|
||||||
|
lista.classList.add('d-none');
|
||||||
|
mensaje.classList.remove('d-none');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function marcarActualesComoVistas() {
|
||||||
|
const clavesActuales = Array.from(document.querySelectorAll('.notif-card'))
|
||||||
|
.map((card) => card.dataset.clave)
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
if (!clavesActuales.length) {
|
||||||
|
if (typeof window.actualizarBadgeNotificaciones === 'function') {
|
||||||
|
window.actualizarBadgeNotificaciones();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const vistas = getVistas();
|
||||||
|
guardarVistas([...vistas, ...clavesActuales]);
|
||||||
|
|
||||||
|
if (typeof window.actualizarBadgeNotificaciones === 'function') {
|
||||||
|
window.actualizarBadgeNotificaciones();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ocultar al cargar
|
||||||
|
ocultarCerradas();
|
||||||
|
marcarActualesComoVistas();
|
||||||
|
|
||||||
|
// Botones de cierre
|
||||||
|
document.querySelectorAll('.btn-cerrar-notif').forEach(function (btn) {
|
||||||
|
btn.addEventListener('click', function () {
|
||||||
|
const clave = btn.dataset.clave;
|
||||||
|
const cerradas = getCerradas();
|
||||||
|
if (!cerradas.includes(clave)) {
|
||||||
|
cerradas.push(clave);
|
||||||
|
guardarCerradas(cerradas);
|
||||||
|
}
|
||||||
|
|
||||||
|
const vistas = getVistas();
|
||||||
|
if (!vistas.includes(clave)) {
|
||||||
|
vistas.push(clave);
|
||||||
|
guardarVistas(vistas);
|
||||||
|
}
|
||||||
|
|
||||||
|
const card = document.querySelector('.notif-card[data-clave="' + clave + '"]');
|
||||||
|
if (card) {
|
||||||
|
card.style.transition = 'opacity 0.2s';
|
||||||
|
card.style.opacity = '0';
|
||||||
|
setTimeout(function () {
|
||||||
|
card.remove();
|
||||||
|
verificarVacio();
|
||||||
|
if (typeof window.actualizarBadgeNotificaciones === 'function') {
|
||||||
|
window.actualizarBadgeNotificaciones();
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,231 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Revisar Formularios - Profesional</title>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const root = document.documentElement;
|
||||||
|
root.classList.add('sidebar-nav-pending');
|
||||||
|
|
||||||
|
const releasePending = function () {
|
||||||
|
root.classList.remove('sidebar-nav-pending');
|
||||||
|
};
|
||||||
|
|
||||||
|
const timeoutId = window.setTimeout(releasePending, 1200);
|
||||||
|
|
||||||
|
if (document.querySelector('.admin-sidebar')) {
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new MutationObserver(function () {
|
||||||
|
if (!document.querySelector('.admin-sidebar')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.disconnect();
|
||||||
|
window.clearTimeout(timeoutId);
|
||||||
|
releasePending();
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.documentElement, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
html.sidebar-nav-pending main {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
|
</head>
|
||||||
|
<body id="top" class="d-flex flex-column min-vh-100 bg-light">
|
||||||
|
<header class="app-navbar">
|
||||||
|
<nav class="navbar navbar-expand-lg">
|
||||||
|
<div class="container">
|
||||||
|
@php
|
||||||
|
$nombreSesion = trim((string) session('personal_nombre', 'Profesional'));
|
||||||
|
$nombreSaludo = $nombreSesion !== '' ? explode(' ', $nombreSesion)[0] : 'Profesional';
|
||||||
|
@endphp
|
||||||
|
<a class="navbar-brand d-flex align-items-center gap-2" href="/profesional/dashboard">
|
||||||
|
<div class="d-flex align-items-center justify-content-center" style="width: 130px; height: 52px;">
|
||||||
|
<img src="{{ asset('images/logo.png') }}" alt="Logo" class="img-fluid" style="max-height: 70px; width: auto; object-fit: contain;">
|
||||||
|
</div>
|
||||||
|
<span class="fw-semibold">¡Hola, {{ $nombreSaludo }}!</span>
|
||||||
|
</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="btn app-navbar-link" href="/profesional/dashboard">Mi Agenda</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/clientes">Mis Clientes</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/profesional/mis-datos">Mis Datos</a></li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn app-navbar-link position-relative" href="/profesional/formularios">
|
||||||
|
Revisar Formularios
|
||||||
|
@if(($formulariosPendientesCount ?? 0) > 0)
|
||||||
|
<span class="position-absolute top-0 start-100 translate-middle p-1 bg-danger rounded-circle js-notificaciones-badge">
|
||||||
|
<span class="visually-hidden">Hay formularios pendientes</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn app-navbar-link position-relative" href="/profesional/notificaciones" data-notificaciones-claves='@json($notificacionesClaves ?? [])'>
|
||||||
|
Notificaciones
|
||||||
|
@if(($notificacionesCount ?? 0) > 0)
|
||||||
|
<span class="position-absolute top-0 start-100 translate-middle p-1 bg-danger rounded-circle js-notificaciones-badge d-none">
|
||||||
|
<span class="visually-hidden">Hay notificaciones</span>
|
||||||
|
</span>
|
||||||
|
@endif
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a class="btn app-navbar-link" href="/logout">Cerrar Sesion</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container py-4 flex-grow-1">
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<h1 class="h4 mb-0">Revisar Formularios</h1>
|
||||||
|
<a href="/profesional/dashboard" class="btn btn-outline-secondary btn-sm">Volver</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if(session('profesional_action_error'))
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
{{ session('profesional_action_error') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if(session('profesional_action_success'))
|
||||||
|
<div class="alert alert-success" role="alert">
|
||||||
|
{{ session('profesional_action_success') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="card border shadow-sm mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="GET" action="/profesional/formularios" class="row g-3 align-items-end">
|
||||||
|
<div class="col-12 col-md-5">
|
||||||
|
<label for="q" class="form-label">Buscar</label>
|
||||||
|
<input id="q" name="q" type="text" class="form-control" value="{{ request('q') }}" placeholder="Nombre, correo, celular, servicio...">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-3">
|
||||||
|
<label for="estado" class="form-label">Estado</label>
|
||||||
|
<select id="estado" name="estado" class="form-select">
|
||||||
|
<option value="">Todos</option>
|
||||||
|
<option value="pendiente" @selected(request('estado') === 'pendiente')>Pendientes</option>
|
||||||
|
<option value="aceptado" @selected(request('estado') === 'aceptado')>Aceptados</option>
|
||||||
|
<option value="aceptado_por_otro" @selected(request('estado') === 'aceptado_por_otro')>Aceptados por otro</option>
|
||||||
|
<option value="rechazado" @selected(request('estado') === 'rechazado')>Rechazados</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-2">
|
||||||
|
<label for="cliente" class="form-label">Es cliente</label>
|
||||||
|
<select id="cliente" name="cliente" class="form-select">
|
||||||
|
<option value="">Todos</option>
|
||||||
|
<option value="si" @selected(request('cliente') === 'si')>Sí</option>
|
||||||
|
<option value="no" @selected(request('cliente') === 'no')>No</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-2 d-flex gap-2">
|
||||||
|
<button type="submit" class="btn btn-primary w-100">Filtrar</button>
|
||||||
|
<a href="/profesional/formularios" class="btn btn-outline-secondary w-100">Limpiar</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive bg-white border rounded shadow-sm">
|
||||||
|
<table class="table table-striped table-hover mb-0 align-middle">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th>Fecha</th>
|
||||||
|
<th>Nombre</th>
|
||||||
|
<th>Correo</th>
|
||||||
|
<th>Celular</th>
|
||||||
|
<th>Servicio</th>
|
||||||
|
<th>Es cliente</th>
|
||||||
|
<th>Estado</th>
|
||||||
|
<th>Acciones</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@forelse($formularios as $formulario)
|
||||||
|
@php
|
||||||
|
$fechaFormulario = $formulario->created_at
|
||||||
|
? $formulario->created_at->format('d/m/Y H:i:s')
|
||||||
|
: ($formulario->fechaenvio
|
||||||
|
? \Illuminate\Support\Carbon::parse($formulario->fechaenvio)->format('d/m/Y')
|
||||||
|
: '-');
|
||||||
|
@endphp
|
||||||
|
<tr>
|
||||||
|
<td>{{ $fechaFormulario }}</td>
|
||||||
|
<td>{{ $formulario->nombrecompleto ?? '-' }}</td>
|
||||||
|
<td>{{ $formulario->correo ?? '-' }}</td>
|
||||||
|
<td>{{ $formulario->celular ?? '-' }}</td>
|
||||||
|
<td>{{ $formulario->servicio?->titulo ?? '-' }}</td>
|
||||||
|
<td>
|
||||||
|
@if($formulario->cliente_id)
|
||||||
|
<span class="badge text-bg-success">Sí</span>
|
||||||
|
@else
|
||||||
|
<span class="badge text-bg-danger">No</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
@php
|
||||||
|
$estadoBaseFormulario = trim((string) ($formulario->estado ?? ''));
|
||||||
|
$estadoFormulario = (($formulario->aceptado_por_otro ?? false)
|
||||||
|
&& strcasecmp($estadoBaseFormulario, 'Aceptado') === 0)
|
||||||
|
? 'Aceptado por otro'
|
||||||
|
: $estadoBaseFormulario;
|
||||||
|
$estadoClase = match (mb_strtolower($estadoFormulario)) {
|
||||||
|
'rechazado por todos' => 'text-bg-danger',
|
||||||
|
'aceptado' => 'text-bg-success',
|
||||||
|
'aceptado por otro' => 'text-bg-secondary',
|
||||||
|
default => 'text-bg-warning',
|
||||||
|
};
|
||||||
|
@endphp
|
||||||
|
<span class="badge {{ $estadoClase }}">{{ $estadoFormulario !== '' ? $estadoFormulario : '-' }}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="/profesional/formularios/{{ $formulario->id }}" class="btn btn-sm btn-outline-primary">Ver</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@empty
|
||||||
|
<tr>
|
||||||
|
<td colspan="8" class="text-center text-muted py-4">No hay formularios cargados.</td>
|
||||||
|
</tr>
|
||||||
|
@endforelse
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if($formularios->hasPages())
|
||||||
|
<div class="d-flex justify-content-center mt-3">
|
||||||
|
{{ $formularios->links() }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</main>
|
||||||
|
@include('partials.reportar-falla-boton')
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user