Vista principal welcome
This commit is contained in:
@@ -1,50 +1,787 @@
|
|||||||
@extends('layouts.app')
|
<!doctype html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Pagina Principal</title>
|
||||||
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
|
<style>
|
||||||
|
.profesional-card-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 260px;
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: center;
|
||||||
|
}
|
||||||
|
|
||||||
@section('title', 'Pagina Principal')
|
.assistant-toggle {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
border-radius: 50%;
|
||||||
|
position: fixed;
|
||||||
|
left: 1.2rem;
|
||||||
|
bottom: 4.8rem;
|
||||||
|
z-index: 1050;
|
||||||
|
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-panel {
|
||||||
|
position: fixed;
|
||||||
|
left: 1.2rem;
|
||||||
|
bottom: 8.8rem;
|
||||||
|
width: min(360px, calc(100vw - 2.4rem));
|
||||||
|
max-height: 70vh;
|
||||||
|
z-index: 1050;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||||
|
border-radius: 14px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: none;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-panel.open {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-tip {
|
||||||
|
position: fixed;
|
||||||
|
left: 1.2rem;
|
||||||
|
bottom: 9.8rem;
|
||||||
|
z-index: 1050;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 0.65rem 0.8rem;
|
||||||
|
box-shadow: 0 8px 18px rgba(0, 0, 0, 0.16);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
max-width: min(280px, calc(100vw - 2.4rem));
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-tip.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-messages {
|
||||||
|
height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: #fffdf8;
|
||||||
|
padding: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-msg {
|
||||||
|
padding: 0.55rem 0.7rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-bottom: 0.6rem;
|
||||||
|
max-width: 90%;
|
||||||
|
line-height: 1.35;
|
||||||
|
font-size: 0.92rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-msg.bot {
|
||||||
|
background: #f3f4f6;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-msg.user {
|
||||||
|
background: #ffdeaf;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-chips {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.4rem;
|
||||||
|
padding: 0 0.2rem 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-chip {
|
||||||
|
font-size: 0.82rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-cards .carousel-control-prev,
|
||||||
|
.carousel-cards .carousel-control-next {
|
||||||
|
width: 2.5rem;
|
||||||
|
background: rgba(0, 0, 0, 0.15);
|
||||||
|
border-radius: 6px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carousel-cards .carousel-inner {
|
||||||
|
padding: 0 2.8rem;
|
||||||
|
}
|
||||||
|
</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">
|
||||||
|
<a class="navbar-brand d-flex align-items-center" href="/">
|
||||||
|
<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>
|
||||||
|
</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="#servicios">Servicios</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="#quienes-somos">Quienes Somos</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="#equipo">Equipo</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="#ubicacion">Ubicacion</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="#formulario">Pedir Turno</a></li>
|
||||||
|
<li class="nav-item"><a class="btn app-navbar-link" href="/instrucciones-uso" title="Instrucciones para utilizar el sitio" aria-label="Instrucciones para utilizar el sitio">¿Necesitas Ayuda?</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a class="btn app-navbar-link" href="/login/cliente">Iniciar Sesion</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
@section('content')
|
|
||||||
<main class="container py-5 flex-grow-1 text-center d-flex flex-column align-items-center">
|
<main class="container py-5 flex-grow-1 text-center d-flex flex-column align-items-center">
|
||||||
<section id="servicios" class="mb-5 w-100">
|
<section id="servicios" class="mb-5 w-100">
|
||||||
<h2 class="h4">Servicios</h2>
|
<h2 class="h4">Servicios</h2>
|
||||||
<p class="text-muted mb-0">Espacio para describir servicios.</p>
|
@if(collect($servicios)->isEmpty())
|
||||||
|
<p class="text-muted mt-2">No hay servicios disponibles.</p>
|
||||||
|
@else
|
||||||
|
<div id="carouselServicios" class="carousel slide carousel-cards mt-2" data-bs-theme="dark">
|
||||||
|
<div class="carousel-inner">
|
||||||
|
@foreach(collect($servicios)->chunk(3) as $chunk)
|
||||||
|
<div class="carousel-item {{ $loop->first ? 'active' : '' }}">
|
||||||
|
<div class="row justify-content-center g-4">
|
||||||
|
@foreach($chunk as $servicio)
|
||||||
|
@php
|
||||||
|
$foto = $servicio->foto ?? null;
|
||||||
|
$fotoSrc = $foto ? asset($foto->ruta) : null;
|
||||||
|
@endphp
|
||||||
|
<div class="col-12 col-sm-6 col-md-4">
|
||||||
|
<div class="card h-100 mx-auto" style="max-width: 18rem;">
|
||||||
|
@if($fotoSrc)
|
||||||
|
<img src="{{ $fotoSrc }}" class="card-img-top servicio-card-img" alt="{{ $servicio->titulo }}">
|
||||||
|
@endif
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">{{ $servicio->titulo }}</h5>
|
||||||
|
<p class="card-text text-muted small">{{ $servicio->descripcion }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
@if(collect($servicios)->count() > 3)
|
||||||
|
<button class="carousel-control-prev" type="button" data-bs-target="#carouselServicios" data-bs-slide="prev">
|
||||||
|
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
|
||||||
|
<span class="visually-hidden">Anterior</span>
|
||||||
|
</button>
|
||||||
|
<button class="carousel-control-next" type="button" data-bs-target="#carouselServicios" data-bs-slide="next">
|
||||||
|
<span class="carousel-control-next-icon" aria-hidden="true"></span>
|
||||||
|
<span class="visually-hidden">Siguiente</span>
|
||||||
|
</button>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
</section>
|
</section>
|
||||||
<hr class="my-4 w-50 mx-auto border-2 border-dark opacity-100">
|
<hr class="my-4 w-50 mx-auto border-2 border-dark opacity-100">
|
||||||
|
|
||||||
<section id="quienes-somos" class="mb-5 w-100">
|
<section id="quienes-somos" class="mb-5 w-100">
|
||||||
<h2 class="h4">Quienes Somos</h2>
|
<h2 class="h4">Quienes Somos</h2>
|
||||||
<p class="text-muted mb-0">Espacio para la presentacion institucional.</p>
|
<p class="text-muted mb-0">
|
||||||
|
{!! nl2br(e($quienesSomos ?? 'Aun no hay contenido cargado para Quienes Somos.')) !!}
|
||||||
|
</p>
|
||||||
</section>
|
</section>
|
||||||
<hr class="my-4 w-50 mx-auto border-2 border-dark opacity-100">
|
<hr class="my-4 w-50 mx-auto border-2 border-dark opacity-100">
|
||||||
|
|
||||||
<section id="equipo" class="mb-5 w-100">
|
<section id="equipo" class="mb-5 w-100">
|
||||||
<h2 class="h4">Equipo</h2>
|
<h2 class="h4">Equipo</h2>
|
||||||
<div class="card mx-auto" style="width: 18rem;">
|
@if(collect($profesionales)->isEmpty())
|
||||||
<img src="..." class="card-img-top" alt="...">
|
<p class="text-muted mt-2">No hay profesionales registrados.</p>
|
||||||
<div class="card-body">
|
@else
|
||||||
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card’s content.</p>
|
<div id="carouselEquipo" class="carousel slide carousel-cards mt-2" data-bs-theme="dark">
|
||||||
|
<div class="carousel-inner">
|
||||||
|
@foreach(collect($profesionales)->chunk(3) as $chunk)
|
||||||
|
<div class="carousel-item {{ $loop->first ? 'active' : '' }}">
|
||||||
|
<div class="row justify-content-center g-4">
|
||||||
|
@foreach($chunk as $profesional)
|
||||||
|
@php
|
||||||
|
$persona = $profesional->persona;
|
||||||
|
$foto = $persona->Foto ?? null;
|
||||||
|
$fotoSrc = $foto ? asset($foto->ruta) : asset('images/avatar_default.png');
|
||||||
|
$profesionesTexto = $profesional->profesiones
|
||||||
|
->pluck('titulo')
|
||||||
|
->push($profesional->profesion?->titulo)
|
||||||
|
->filter()
|
||||||
|
->unique()
|
||||||
|
->implode(' | ');
|
||||||
|
@endphp
|
||||||
|
<div class="col-12 col-sm-6 col-md-4">
|
||||||
|
<div class="card h-100 mx-auto" style="max-width: 18rem;">
|
||||||
|
<img src="{{ $fotoSrc }}" class="card-img-top profesional-card-img" alt="{{ $persona->nombre }} {{ $persona->apellido }}">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">{{ $persona->nombre }} {{ $persona->apellido }}</h5>
|
||||||
|
<p class="card-text text-muted small">{{ $profesionesTexto }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
@if(collect($profesionales)->count() > 3)
|
||||||
|
<button class="carousel-control-prev" type="button" data-bs-target="#carouselEquipo" data-bs-slide="prev">
|
||||||
|
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
|
||||||
|
<span class="visually-hidden">Anterior</span>
|
||||||
|
</button>
|
||||||
|
<button class="carousel-control-next" type="button" data-bs-target="#carouselEquipo" data-bs-slide="next">
|
||||||
|
<span class="carousel-control-next-icon" aria-hidden="true"></span>
|
||||||
|
<span class="visually-hidden">Siguiente</span>
|
||||||
|
</button>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
@endif
|
||||||
</section>
|
</section>
|
||||||
<hr class="my-4 w-50 mx-auto border-2 border-dark opacity-100">
|
<hr class="my-4 w-50 mx-auto border-2 border-dark opacity-100">
|
||||||
|
|
||||||
<section id="ubicacion" class="mb-5 w-100">
|
<section id="ubicacion" class="mb-5 w-100">
|
||||||
<h2 class="h4">Ubicacion</h2>
|
<h2 class="h4">Ubicacion</h2>
|
||||||
<p class="text-muted mb-3">Pasteur 141</p>
|
<p class="text-muted mb-3">Pasteur 141</p>
|
||||||
<div class="ratio ratio-16x9 mx-auto" style="max-width: 900px;">
|
<div class="ratio ratio-16x9 mx-auto" style="max-width: 900px;" data-map-widget>
|
||||||
<iframe
|
<iframe
|
||||||
src="https://maps.google.com/maps?q=Pasteur%20141&t=&z=15&ie=UTF8&iwloc=&output=embed"
|
data-map-embed
|
||||||
|
data-map-src="https://maps.google.com/maps?q=Pasteur%20141&t=&z=15&ie=UTF8&iwloc=&output=embed"
|
||||||
style="border: 0;"
|
style="border: 0;"
|
||||||
allowfullscreen
|
allowfullscreen
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
referrerpolicy="no-referrer-when-downgrade"
|
referrerpolicy="no-referrer-when-downgrade"
|
||||||
title="Mapa de ubicacion - Pasteur 141"></iframe>
|
title="Mapa de ubicacion - Pasteur 141"></iframe>
|
||||||
|
<div class="d-none border rounded bg-light p-3 text-center d-flex align-items-center justify-content-center" data-map-fallback>
|
||||||
|
<div>
|
||||||
|
<p class="mb-2 fw-semibold">No se pudo cargar el mapa sin conexión.</p>
|
||||||
|
<p class="mb-2">Dirección: Dr. Luis Pasteur 141, Paraná, Entre Ríos, Argentina.</p>
|
||||||
|
<a href="https://www.google.com/maps/place/Dr.+Luis+Pasteur+141,+Paran%C3%A1,+Entre+R%C3%ADos,+Argentina" target="_blank" rel="noopener noreferrer">Abrir en Google Maps</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<hr class="my-4 w-50 mx-auto border-2 border-dark opacity-100">
|
<hr class="my-4 w-50 mx-auto border-2 border-dark opacity-100">
|
||||||
|
|
||||||
<section id="formulario" class="w-100">
|
<section id="formulario" class="w-100">
|
||||||
<h2 class="h4">Formulario</h2>
|
<h2 class="h4">Pedir un turno</h2>
|
||||||
<p class="text-muted mb-0">Espacio para formulario de contacto.</p>
|
<p class="text-muted mb-3">Si sos cliente, inicia sesión y pedí turno desde tu cuenta para tener mayor prioridad.</p>
|
||||||
|
<div class="card shadow-sm mx-auto text-start" style="max-width: 920px;">
|
||||||
|
<div class="card-body p-4 p-md-5">
|
||||||
|
@if(session('form_success'))
|
||||||
|
<div class="alert alert-success" role="alert">
|
||||||
|
{{ session('form_success') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
<form method="POST" action="/formulario" class="row g-3">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<label for="nombre" class="form-label">Nombre</label>
|
||||||
|
<input type="text" id="nombre" name="nombre" class="form-control" value="{{ old('nombre') }}" pattern="[A-Za-zÁÉÍÓÚáéíóúÑñ\s]+" oninput="this.value=this.value.replace(/[^A-Za-zÁÉÍÓÚáéíóúÑñ\s]/g,'')" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<label for="apellido" class="form-label">Apellido</label>
|
||||||
|
<input type="text" id="apellido" name="apellido" class="form-control" value="{{ old('apellido') }}" pattern="[A-Za-zÁÉÍÓÚáéíóúÑñ\s]+" oninput="this.value=this.value.replace(/[^A-Za-zÁÉÍÓÚáéíóúÑñ\s]/g,'')" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<label for="correo" class="form-label">Correo</label>
|
||||||
|
<input type="email" id="correo" name="correo" class="form-control" value="{{ old('correo') }}" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<label for="celular" class="form-label">Celular</label>
|
||||||
|
<input type="text" id="celular" name="celular" class="form-control" value="{{ old('celular') }}" inputmode="numeric" pattern="[0-9]+" oninput="this.value=this.value.replace(/\D/g,'')" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<label for="profesion_id" class="form-label">Profesion</label>
|
||||||
|
<select id="profesion_id" name="profesion_id" class="form-select" required>
|
||||||
|
<option value="">Seleccionar profesion</option>
|
||||||
|
@foreach($profesiones as $profesion)
|
||||||
|
<option value="{{ $profesion->id }}" @selected(old('profesion_id') == $profesion->id)>
|
||||||
|
{{ $profesion->titulo }}
|
||||||
|
</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<label for="servicio_id" class="form-label">Servicio</label>
|
||||||
|
<select id="servicio_id" name="servicio_id" class="form-select" required>
|
||||||
|
<option value="" data-default-text="Seleccionar servicio">Primero elegi una profesion</option>
|
||||||
|
@foreach($servicios as $servicio)
|
||||||
|
<option
|
||||||
|
value="{{ $servicio->id }}"
|
||||||
|
data-profesion-ids="{{ $servicio->profesion_id }}"
|
||||||
|
@selected(old('servicio_id') == $servicio->id)
|
||||||
|
>
|
||||||
|
{{ $servicio->titulo }}
|
||||||
|
</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
<div class="form-text">Solo se muestran servicios de la profesion seleccionada.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<label for="profesional_id" class="form-label">Profesional</label>
|
||||||
|
<select id="profesional_id" name="profesional_id" class="form-select" required>
|
||||||
|
<option value="" data-default-text="Seleccionar profesional">Primero elegi una profesion</option>
|
||||||
|
<option value="INDISTINTO" data-indistinto="1" @selected(old('profesional_id') === 'INDISTINTO')>INDISTINTO</option>
|
||||||
|
@foreach($profesionales as $profesional)
|
||||||
|
@php
|
||||||
|
$profesionesIds = $profesional->profesiones
|
||||||
|
->pluck('id')
|
||||||
|
->push((int) $profesional->profesion_id)
|
||||||
|
->unique()
|
||||||
|
->implode(',');
|
||||||
|
@endphp
|
||||||
|
<option
|
||||||
|
value="{{ $profesional->id }}"
|
||||||
|
data-profesion-ids="{{ $profesionesIds }}"
|
||||||
|
@selected(old('profesional_id') == $profesional->id)
|
||||||
|
>
|
||||||
|
{{ $profesional->persona->nombre }} {{ $profesional->persona->apellido }}
|
||||||
|
</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
<div class="form-text">Solo se muestran profesionales de la profesion seleccionada o la opcion INDISTINTO.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="hidden" name="modalidad_id" value="1">
|
||||||
|
|
||||||
|
<div class="col-12 col-md-8">
|
||||||
|
<label class="form-label d-block">Dias de preferencia</label>
|
||||||
|
@php
|
||||||
|
$diasDisponibles = ['Lunes', 'Martes', 'Miercoles', 'Jueves', 'Viernes', 'Indistinto'];
|
||||||
|
$diasSeleccionados = old('dias_preferencia', []);
|
||||||
|
@endphp
|
||||||
|
<div class="d-flex flex-wrap gap-3" id="dias-preferencia-container">
|
||||||
|
@foreach($diasDisponibles as $dia)
|
||||||
|
<div class="form-check">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
id="dia_{{ strtolower($dia) }}"
|
||||||
|
name="dias_preferencia[]"
|
||||||
|
value="{{ $dia }}"
|
||||||
|
@checked(in_array($dia, $diasSeleccionados, true))
|
||||||
|
>
|
||||||
|
<label class="form-check-label" for="dia_{{ strtolower($dia) }}">{{ $dia }}</label>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
<div class="form-text">Si elegis Indistinto, no podes combinarlo con otros dias.</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-4">
|
||||||
|
<label for="horario_preferencia" class="form-label">Horario de preferencia</label>
|
||||||
|
<select id="horario_preferencia" name="horario_preferencia" class="form-select" required>
|
||||||
|
<option value="">Seleccionar horario</option>
|
||||||
|
<option value="AM" @selected(old('horario_preferencia') == 'AM')>AM</option>
|
||||||
|
<option value="PM" @selected(old('horario_preferencia') == 'PM')>PM</option>
|
||||||
|
<option value="INDISTINTO" @selected(old('horario_preferencia') == 'INDISTINTO')>INDISTINTO</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
<label for="descripcion" class="form-label">Descripcion</label>
|
||||||
|
<textarea id="descripcion" placeholder="Describa brevemente el motivo de su consulta" name="descripcion" class="form-control" rows="4" maxlength="3000" required>{{ old('descripcion') }}</textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="position:absolute; left:-9999px; width:1px; height:1px; overflow:hidden;" aria-hidden="true">
|
||||||
|
<label for="website">No completar este campo</label>
|
||||||
|
<input type="text" id="website" name="website" value="{{ old('website') }}" tabindex="-1" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 d-grid d-md-flex justify-content-md-end">
|
||||||
|
<button type="button" class="btn btn-primary px-4" data-bs-toggle="modal" data-bs-target="#confirmarEnvioModal">Enviar</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<!-- Modal de confirmación de envío -->
|
||||||
|
<div class="modal fade" id="confirmarEnvioModal" tabindex="-1" aria-labelledby="confirmarEnvioModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="confirmarEnvioModalLabel">Confirmar envío</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
¿Deseás enviar el formulario?<br>
|
||||||
|
<span class="text-muted">Si se confirma tu turno, se te enviará un correo electronico con los detalles del mismo</span>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="btnConfirmarEnvioForm">Confirmar envío</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
@endsection
|
|
||||||
|
<footer class="app-footer mt-auto py-4">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row g-4 align-items-start text-center">
|
||||||
|
<div class="col-12 col-md-4">
|
||||||
|
<div class="d-inline-flex align-items-center justify-content-center" style="width: 120px; height: 52px;">
|
||||||
|
<img src="{{ asset('images/logo.png') }}" alt="Logo" class="img-fluid" style="max-height: 70px; width: auto; object-fit: contain;">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-4 text-center">
|
||||||
|
<h3 class="h6 mb-2">Redes Sociales</h3>
|
||||||
|
<p class="mb-2"><a href="https://www.instagram.com/abogadasdellitoral?utm_source=ig_web_button_share_sheet&igsh=ZDNlZDc0MzIxNw==" target="_blank" rel="noopener noreferrer">Instagram</a></p>
|
||||||
|
<p class="small mb-0">Desarrollado por Luciano Belini</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-4">
|
||||||
|
<h3 class="h6 mb-2">Ubicacion</h3>
|
||||||
|
<p class="mb-0"> <a href="https://www.google.com/maps/place/Dr.+Luis+Pasteur+141,+Paran%C3%A1,+Entre+R%C3%ADos,+Argentina" target="_blank" rel="noopener noreferrer">Dr. Luis Pasteur 141, Paraná, Entre Ríos, Argentina</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<a href="#top" class="btn btn-primary rounded-circle position-fixed bottom-0 end-0 m-4 d-flex align-items-center justify-content-center" style="width: 44px; height: 44px;" aria-label="Volver arriba" title="Volver arriba">
|
||||||
|
↑
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<button id="assistant-toggle" type="button" class="btn btn-warning assistant-toggle" aria-label="Abrir {{ $nombreAsistente }}" title="{{ $nombreAsistente }}">
|
||||||
|
🤖
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div id="assistant-tip" class="assistant-tip" role="status" aria-live="polite">
|
||||||
|
{{ $mensajeBurbujaAsistente }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section id="assistant-panel" class="assistant-panel shadow" aria-label="{{ $nombreAsistente }}">
|
||||||
|
<div class="bg-warning-subtle border-bottom px-3 py-2 d-flex justify-content-between align-items-center">
|
||||||
|
<strong>{{ $nombreAsistente }}</strong>
|
||||||
|
<button id="assistant-close" type="button" class="btn btn-sm btn-outline-secondary">Cerrar</button>
|
||||||
|
</div>
|
||||||
|
<div id="assistant-messages" class="assistant-messages">
|
||||||
|
<div class="assistant-msg bot">{{ $mensajeInicioPanelAsistente }}</div>
|
||||||
|
@if(!empty($chipsAsistente))
|
||||||
|
<div id="assistant-chips" class="assistant-chips">
|
||||||
|
@foreach($chipsAsistente as $chip)
|
||||||
|
<button type="button" class="btn btn-sm btn-outline-warning assistant-chip">{{ $chip }}</button>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<form id="assistant-form" class="p-2 border-top bg-white">
|
||||||
|
<div class="input-group">
|
||||||
|
<input id="assistant-input" type="text" class="form-control" maxlength="500" placeholder="Escribí tu consulta..." required>
|
||||||
|
<button class="btn btn-primary" type="submit">Enviar</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
const profesionSelect = document.getElementById('profesion_id');
|
||||||
|
const servicioSelect = document.getElementById('servicio_id');
|
||||||
|
const profesionalSelect = document.getElementById('profesional_id');
|
||||||
|
const diaIndistinto = document.getElementById('dia_indistinto');
|
||||||
|
const diasContainer = document.getElementById('dias-preferencia-container');
|
||||||
|
|
||||||
|
const normalizarTexto = (texto) => {
|
||||||
|
return (texto || '')
|
||||||
|
.normalize('NFD')
|
||||||
|
.replace(/[\u0300-\u036f]/g, '')
|
||||||
|
.toLowerCase()
|
||||||
|
.trim();
|
||||||
|
};
|
||||||
|
|
||||||
|
const inicializarSelectBuscable = (select) => {
|
||||||
|
if (!select || select.dataset.buscableInicializado === '1') {
|
||||||
|
return { refresh: () => {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
select.dataset.buscableInicializado = '1';
|
||||||
|
select.classList.add('d-none');
|
||||||
|
|
||||||
|
const wrapper = document.createElement('div');
|
||||||
|
wrapper.className = 'position-relative';
|
||||||
|
|
||||||
|
const trigger = document.createElement('button');
|
||||||
|
trigger.type = 'button';
|
||||||
|
trigger.className = 'form-select text-start';
|
||||||
|
trigger.setAttribute('aria-haspopup', 'listbox');
|
||||||
|
trigger.setAttribute('aria-expanded', 'false');
|
||||||
|
|
||||||
|
const dropdown = document.createElement('div');
|
||||||
|
dropdown.className = 'position-absolute w-100 bg-white border rounded shadow-sm mt-1 p-2';
|
||||||
|
dropdown.style.zIndex = '1080';
|
||||||
|
dropdown.style.display = 'none';
|
||||||
|
dropdown.style.maxHeight = '280px';
|
||||||
|
|
||||||
|
const searchInput = document.createElement('input');
|
||||||
|
searchInput.type = 'text';
|
||||||
|
searchInput.className = 'form-control form-control-sm mb-2';
|
||||||
|
searchInput.placeholder = 'Buscar...';
|
||||||
|
|
||||||
|
const list = document.createElement('div');
|
||||||
|
list.className = 'd-flex flex-column gap-1 overflow-auto';
|
||||||
|
list.style.maxHeight = '210px';
|
||||||
|
|
||||||
|
dropdown.appendChild(searchInput);
|
||||||
|
dropdown.appendChild(list);
|
||||||
|
wrapper.appendChild(trigger);
|
||||||
|
wrapper.appendChild(dropdown);
|
||||||
|
select.insertAdjacentElement('afterend', wrapper);
|
||||||
|
|
||||||
|
const obtenerLabelSeleccionado = () => {
|
||||||
|
const selected = select.options[select.selectedIndex];
|
||||||
|
if (!selected) {
|
||||||
|
return 'Seleccionar opcion';
|
||||||
|
}
|
||||||
|
return selected.textContent.trim();
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderOpciones = () => {
|
||||||
|
const termino = normalizarTexto(searchInput.value);
|
||||||
|
const opcionesVisibles = Array.from(select.options).filter((option) => !option.hidden && !option.disabled);
|
||||||
|
|
||||||
|
list.innerHTML = '';
|
||||||
|
opcionesVisibles
|
||||||
|
.filter((option) => {
|
||||||
|
if (!termino) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return normalizarTexto(option.textContent).includes(termino);
|
||||||
|
})
|
||||||
|
.forEach((option) => {
|
||||||
|
const item = document.createElement('button');
|
||||||
|
item.type = 'button';
|
||||||
|
item.className = 'btn btn-sm btn-light text-start';
|
||||||
|
item.textContent = option.textContent.trim();
|
||||||
|
item.dataset.value = option.value;
|
||||||
|
if (option.value === select.value) {
|
||||||
|
item.classList.add('active');
|
||||||
|
}
|
||||||
|
item.addEventListener('click', () => {
|
||||||
|
select.value = option.value;
|
||||||
|
select.dispatchEvent(new Event('change', { bubbles: true }));
|
||||||
|
dropdown.style.display = 'none';
|
||||||
|
trigger.setAttribute('aria-expanded', 'false');
|
||||||
|
});
|
||||||
|
list.appendChild(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!list.children.length) {
|
||||||
|
const vacio = document.createElement('div');
|
||||||
|
vacio.className = 'small text-muted px-1 py-1';
|
||||||
|
vacio.textContent = 'Sin resultados';
|
||||||
|
list.appendChild(vacio);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const refresh = () => {
|
||||||
|
trigger.textContent = obtenerLabelSeleccionado();
|
||||||
|
trigger.disabled = select.disabled;
|
||||||
|
if (select.disabled) {
|
||||||
|
dropdown.style.display = 'none';
|
||||||
|
trigger.setAttribute('aria-expanded', 'false');
|
||||||
|
}
|
||||||
|
renderOpciones();
|
||||||
|
};
|
||||||
|
|
||||||
|
trigger.addEventListener('click', () => {
|
||||||
|
if (trigger.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const abierto = dropdown.style.display === 'block';
|
||||||
|
dropdown.style.display = abierto ? 'none' : 'block';
|
||||||
|
trigger.setAttribute('aria-expanded', abierto ? 'false' : 'true');
|
||||||
|
if (!abierto) {
|
||||||
|
searchInput.focus();
|
||||||
|
searchInput.select();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
searchInput.addEventListener('input', renderOpciones);
|
||||||
|
select.addEventListener('change', refresh);
|
||||||
|
|
||||||
|
document.addEventListener('click', (event) => {
|
||||||
|
if (!wrapper.contains(event.target)) {
|
||||||
|
dropdown.style.display = 'none';
|
||||||
|
trigger.setAttribute('aria-expanded', 'false');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
return { refresh };
|
||||||
|
};
|
||||||
|
|
||||||
|
if (profesionSelect && servicioSelect && profesionalSelect) {
|
||||||
|
const servicioOptions = Array.from(servicioSelect.querySelectorAll('option[value]'));
|
||||||
|
const profesionalOptions = Array.from(profesionalSelect.querySelectorAll('option[value]'));
|
||||||
|
|
||||||
|
const buscadorProfesion = inicializarSelectBuscable(profesionSelect);
|
||||||
|
const buscadorServicio = inicializarSelectBuscable(servicioSelect);
|
||||||
|
const buscadorProfesional = inicializarSelectBuscable(profesionalSelect);
|
||||||
|
|
||||||
|
const aplicarFiltro = (select, options, profesionId) => {
|
||||||
|
const selectedOption = select.options[select.selectedIndex];
|
||||||
|
const placeholderOption = select.querySelector('option[value=""]');
|
||||||
|
|
||||||
|
options.forEach((option) => {
|
||||||
|
const esIndistinto = option.dataset.indistinto === '1';
|
||||||
|
const profesionesDelProfesional = (option.dataset.profesionIds || '')
|
||||||
|
.split(',')
|
||||||
|
.map((id) => id.trim())
|
||||||
|
.filter((id) => id !== '');
|
||||||
|
const coincide = Boolean(profesionId) && (esIndistinto || profesionesDelProfesional.includes(profesionId));
|
||||||
|
option.hidden = !coincide;
|
||||||
|
option.disabled = !coincide;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (placeholderOption) {
|
||||||
|
const defaultText = placeholderOption.dataset.defaultText || 'Seleccionar opcion';
|
||||||
|
placeholderOption.textContent = profesionId ? defaultText : 'Primero elegi una profesion';
|
||||||
|
}
|
||||||
|
|
||||||
|
select.disabled = !profesionId;
|
||||||
|
|
||||||
|
if (selectedOption && selectedOption.value && (selectedOption.disabled || selectedOption.hidden)) {
|
||||||
|
select.value = '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const filtrarPorProfesion = () => {
|
||||||
|
const profesionId = profesionSelect.value;
|
||||||
|
|
||||||
|
aplicarFiltro(servicioSelect, servicioOptions, profesionId);
|
||||||
|
aplicarFiltro(profesionalSelect, profesionalOptions, profesionId);
|
||||||
|
buscadorProfesion.refresh();
|
||||||
|
buscadorServicio.refresh();
|
||||||
|
buscadorProfesional.refresh();
|
||||||
|
};
|
||||||
|
|
||||||
|
filtrarPorProfesion();
|
||||||
|
profesionSelect.addEventListener('change', filtrarPorProfesion);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diaIndistinto && diasContainer) {
|
||||||
|
const checkboxes = Array.from(diasContainer.querySelectorAll('input[type="checkbox"]'));
|
||||||
|
const otrosDias = checkboxes.filter((checkbox) => checkbox !== diaIndistinto);
|
||||||
|
|
||||||
|
const syncDias = () => {
|
||||||
|
if (diaIndistinto.checked) {
|
||||||
|
otrosDias.forEach((checkbox) => {
|
||||||
|
checkbox.checked = false;
|
||||||
|
checkbox.disabled = true;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
otrosDias.forEach((checkbox) => {
|
||||||
|
checkbox.disabled = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
const algunOtroDiaSeleccionado = otrosDias.some((checkbox) => checkbox.checked);
|
||||||
|
diaIndistinto.disabled = algunOtroDiaSeleccionado;
|
||||||
|
};
|
||||||
|
|
||||||
|
checkboxes.forEach((checkbox) => checkbox.addEventListener('change', syncDias));
|
||||||
|
syncDias();
|
||||||
|
}
|
||||||
|
|
||||||
|
const assistantToggle = document.getElementById('assistant-toggle');
|
||||||
|
const assistantPanel = document.getElementById('assistant-panel');
|
||||||
|
const assistantClose = document.getElementById('assistant-close');
|
||||||
|
const assistantForm = document.getElementById('assistant-form');
|
||||||
|
const assistantInput = document.getElementById('assistant-input');
|
||||||
|
const assistantMessages = document.getElementById('assistant-messages');
|
||||||
|
const assistantTip = document.getElementById('assistant-tip');
|
||||||
|
|
||||||
|
const appendAssistantMessage = (text, type) => {
|
||||||
|
const msg = document.createElement('div');
|
||||||
|
msg.className = `assistant-msg ${type}`;
|
||||||
|
msg.textContent = text;
|
||||||
|
assistantMessages.appendChild(msg);
|
||||||
|
assistantMessages.scrollTop = assistantMessages.scrollHeight;
|
||||||
|
};
|
||||||
|
|
||||||
|
assistantToggle?.addEventListener('click', () => {
|
||||||
|
assistantPanel?.classList.add('open');
|
||||||
|
assistantTip?.classList.add('hidden');
|
||||||
|
assistantInput?.focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
assistantClose?.addEventListener('click', () => {
|
||||||
|
assistantPanel?.classList.remove('open');
|
||||||
|
});
|
||||||
|
|
||||||
|
document.querySelectorAll('.assistant-chip').forEach((chip) => {
|
||||||
|
chip.addEventListener('click', () => {
|
||||||
|
const texto = chip.textContent.trim();
|
||||||
|
if (!texto) return;
|
||||||
|
assistantInput.value = texto;
|
||||||
|
assistantForm.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
assistantForm?.addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const mensaje = assistantInput.value.trim();
|
||||||
|
if (!mensaje) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
appendAssistantMessage(mensaje, 'user');
|
||||||
|
assistantInput.value = '';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch('/asistente/chat', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ mensaje }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error('No se pudo obtener respuesta.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
appendAssistantMessage(data.respuesta || 'No pude responder en este momento.', 'bot');
|
||||||
|
} catch (error) {
|
||||||
|
appendAssistantMessage('Estoy con inconvenientes técnicos. Probá nuevamente en unos segundos.', 'bot');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
@include('partials.reportar-falla-boton')
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user