189 lines
12 KiB
HTML
189 lines
12 KiB
HTML
{% extends 'base.html' %}
|
|
{% block content %}
|
|
<div class="page-toolbar">
|
|
<div>
|
|
<h1 class="h3 mb-1">Turnos</h1>
|
|
<p class="text-muted mb-0">Administrá la agenda del equipo con filtros superiores y formulario en modal.</p>
|
|
</div>
|
|
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#appointmentModal"><i
|
|
class="bi bi-plus-circle"></i> {{ 'Editar turno' if edit_appointment else 'Nuevo turno' }}</button>
|
|
</div>
|
|
<div class="card table-panel">
|
|
<div class="card-header">
|
|
<form method="get" class="row g-3 align-items-end filter-toolbar">
|
|
<div class="col-lg-4"><label class="form-label">Buscar</label><input class="form-control" name="q"
|
|
placeholder="Paciente, DNI, email o servicio" value="{{ request.args.get('q','') }}"></div>
|
|
<div class="col-lg-2"><label class="form-label">Desde</label><input class="form-control" type="date"
|
|
name="date_from" value="{{ request.args.get('date_from','') }}"></div>
|
|
<div class="col-lg-2"><label class="form-label">Hasta</label><input class="form-control" type="date"
|
|
name="date_to" value="{{ request.args.get('date_to','') }}"></div>
|
|
<div class="col-lg-2"><label class="form-label">Estado</label><select class="form-select searchable-select"
|
|
name="status" data-placeholder="Todos">
|
|
<option value="">Todos</option>{% for st in statuses %}<option value="{{ st }}" {% if
|
|
request.args.get('status')==st %}selected{% endif %}>{{ st }}</option>{% endfor %}
|
|
</select></div>
|
|
<div class="col-lg-2 d-flex gap-2"><button class="btn btn-outline-primary flex-fill">Filtrar</button><a
|
|
class="btn btn-outline-secondary" href="{{ url_for('admin_appointments') }}">Limpiar</a></div>
|
|
</form>
|
|
</div>
|
|
<div class="table-responsive wide-table">
|
|
<table class="table align-middle mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th>Fecha</th>
|
|
<th>Paciente</th>
|
|
<th>Servicio</th>
|
|
<th>Profesional</th>
|
|
<th>Estado</th>
|
|
<th class="text-end">Acciones</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for item in appointments %}
|
|
<tr>
|
|
<td>{{ item.appointment_date.strftime('%d/%m/%Y') }}<br><span class="small text-muted">{{
|
|
item.start_time.strftime('%H:%M') }}</span></td>
|
|
<td>{{ item.client_name }}{% if item.patient %}<br><span class="small text-muted">DNI {{
|
|
item.patient.documento }}</span>{% endif %}<br><span class="small text-muted">{{ item.client_email
|
|
}}</span></td>
|
|
<td>{{ item.service.name }}</td>
|
|
<td>{{ item.professional.display_name }}</td>
|
|
<td><span class="badge text-bg-secondary rounded-pill">{{ item.status }}</span></td>
|
|
<td class="text-end">
|
|
<div class="d-flex gap-2 justify-content-end flex-wrap"><a class="btn btn-sm btn-outline-primary"
|
|
href="{{ url_for('admin_appointments', edit=item.id) }}">Editar</a>{% if current_user.role in
|
|
['admin','receptionist'] %}<form method="post"
|
|
action="{{ url_for('admin_appointment_delete', appointment_id=item.id) }}"
|
|
onsubmit="return confirm('¿Eliminar?')"><button class="btn btn-sm btn-outline-danger">Eliminar</button>
|
|
</form>{% endif %}</div>
|
|
</td>
|
|
</tr>
|
|
{% else %}
|
|
<tr>
|
|
<td colspan="6" class="text-center text-muted py-4">Sin resultados.</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% if pagination.pages > 1 %}
|
|
<div class="card-body border-top">
|
|
<nav>
|
|
<ul class="pagination mb-0 justify-content-end">
|
|
<li class="page-item {% if not pagination.has_prev %}disabled{% endif %}"><a class="page-link"
|
|
href="{{ url_for('admin_appointments', page=pagination.prev_num, q=request.args.get('q',''), date_from=request.args.get('date_from',''), date_to=request.args.get('date_to',''), status=request.args.get('status','')) }}">Anterior</a>
|
|
</li>{% for page_num in pagination.iter_pages() %}{% if page_num %}<li
|
|
class="page-item {% if page_num == pagination.page %}active{% endif %}"><a class="page-link"
|
|
href="{{ url_for('admin_appointments', page=page_num, q=request.args.get('q',''), date_from=request.args.get('date_from',''), date_to=request.args.get('date_to',''), status=request.args.get('status','')) }}">{{
|
|
page_num }}</a></li>{% else %}<li class="page-item disabled"><span class="page-link">…</span></li>{% endif
|
|
%}{% endfor %}<li class="page-item {% if not pagination.has_next %}disabled{% endif %}"><a class="page-link"
|
|
href="{{ url_for('admin_appointments', page=pagination.next_num, q=request.args.get('q',''), date_from=request.args.get('date_from',''), date_to=request.args.get('date_to',''), status=request.args.get('status','')) }}">Siguiente</a>
|
|
</li>
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="modal fade" id="appointmentModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-xl modal-dialog-scrollable">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">{{ 'Editar turno' if edit_appointment else 'Nuevo turno' }}</h5><button type="button"
|
|
class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<form method="post" id="appointmentForm">
|
|
<div class="modal-body">
|
|
<div class="row g-4 modal-form-grid">
|
|
{% if edit_appointment %}<input type="hidden" name="appointment_id" value="{{ edit_appointment.id }}">{%
|
|
endif %}
|
|
<div class="col-12">
|
|
<div class="section-card">
|
|
<div class="section-title">Paciente y contacto</div>
|
|
<div class="row g-3">
|
|
<div class="col-lg-6"><label class="form-label">Paciente / Cliente</label><select
|
|
class="form-select searchable-select patient-select" id="appointmentPatientSelect"
|
|
name="patient_select" data-placeholder="Buscar por apellido, nombre o DNI">
|
|
<option value="">Seleccionar</option>{% for p in patients %}<option value="{{ p.id }}"
|
|
data-email="{{ p.email or '' }}" data-phone="{{ p.telefono or '' }}" {% if edit_appointment and
|
|
edit_appointment.patient_id==p.id %}selected{% endif %}>{{ p.apellido }}, {{ p.nombre }} · DNI
|
|
{{ p.documento }}</option>{% endfor %}
|
|
</select><input type="hidden" name="patient_id" class="patient-id"
|
|
value="{{ edit_appointment.patient_id if edit_appointment else '' }}"></div>
|
|
<div class="col-md-3"><label class="form-label">Email</label><input
|
|
class="form-control patient-email field-placeholder" name="client_email" type="email"
|
|
placeholder="paciente@correo.com"
|
|
value="{{ edit_appointment.client_email if edit_appointment else '' }}" required></div>
|
|
<div class="col-md-3"><label class="form-label">Teléfono</label><input
|
|
class="form-control patient-phone field-placeholder" name="client_phone"
|
|
placeholder="11 5555-5555"
|
|
value="{{ edit_appointment.client_phone if edit_appointment else '' }}"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-12">
|
|
<div class="section-card h-100">
|
|
<div class="section-title">Agenda</div>
|
|
<div class="row g-3">
|
|
<div class="col-6"><label class="form-label">Servicio</label><select
|
|
class="form-select searchable-select" id="appointmentServiceSelect" name="service_id"
|
|
data-placeholder="Seleccionar servicio">{% for s in services %}<option value="{{ s.id }}" {% if
|
|
edit_appointment and edit_appointment.service_id==s.id %}selected{% endif %}>{{ s.name }}
|
|
</option>{% endfor %}</select></div>
|
|
<div class="col-6"><label class="form-label">Profesional</label><select
|
|
class="form-select searchable-select" id="appointmentProfessionalSelect" name="professional_id"
|
|
data-placeholder="Seleccionar profesional">{% for p in professionals %}<option value="{{ p.id }}"
|
|
{% if edit_appointment and edit_appointment.professional_id==p.id %}selected{% endif %}>{{
|
|
p.display_name }}</option>{% endfor %}</select></div>
|
|
<div class="col-md-6"><label class="form-label">Fecha</label><input class="form-control"
|
|
id="appointmentDateInput" type="date" name="appointment_date"
|
|
value="{{ edit_appointment.appointment_date.isoformat() if edit_appointment else today_iso() }}">
|
|
</div>
|
|
<div class="col-md-6"><label class="form-label">Turnos disponibles</label><select
|
|
class="form-select searchable-select" id="appointmentSlotSelect"
|
|
data-placeholder="Seleccionar horario">
|
|
<option value="{{ edit_appointment.start_time.strftime('%H:%M') if edit_appointment else '' }}">{{
|
|
edit_appointment.start_time.strftime('%H:%M') if edit_appointment else 'Seleccionar' }}</option>
|
|
</select>
|
|
<div class="form-hint mt-1" id="appointmentSlotHelp">Elegí servicio, profesional y fecha para ver
|
|
los horarios disponibles.</div><input type="hidden" id="appointmentTimeInput" name="start_time"
|
|
value="{{ edit_appointment.start_time.strftime('%H:%M') if edit_appointment else '09:00' }}">
|
|
</div>
|
|
<div class="col-md-6"><label class="form-label">Estado</label><select
|
|
class="form-select searchable-select" name="status">{% set current_status =
|
|
edit_appointment.status if edit_appointment else 'confirmed' %}{% for st in statuses %}<option
|
|
value="{{ st }}" {% if current_status==st %}selected{% endif %}>{{ st }}</option>{% endfor
|
|
%}</select></div>
|
|
<div class="col-md-6"><label class="form-label">Origen</label><input
|
|
class="form-control field-placeholder" name="booking_source"
|
|
value="{{ edit_appointment.booking_source if edit_appointment else 'admin' }}"
|
|
placeholder="admin"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-12">
|
|
<div class="section-card">
|
|
<div class="section-title">Notas</div>
|
|
<div class="row g-3">
|
|
<div class="col-md-6"><label class="form-label">Notas cliente</label><textarea
|
|
class="form-control field-placeholder" name="notes" rows="3"
|
|
placeholder="Observaciones visibles del turno">{{ edit_appointment.notes if edit_appointment else '' }}</textarea>
|
|
</div>
|
|
<div class="col-md-6"><label class="form-label">Notas internas</label><textarea
|
|
class="form-control field-placeholder" name="internal_notes" rows="3"
|
|
placeholder="Comentarios internos del equipo">{{ edit_appointment.internal_notes if edit_appointment else '' }}</textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer"><button type="button" class="btn btn-outline-secondary"
|
|
data-bs-dismiss="modal">Cancelar</button><button class="btn btn-primary">Guardar turno</button></div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
{% block scripts %}{{ super() }}{% if edit_appointment %}
|
|
<script>document.addEventListener('DOMContentLoaded', function () { new bootstrap.Modal(document.getElementById('appointmentModal')).show(); });</script>
|
|
{% endif %}{% endblock %} |