UX Pacientes: drawer de ficha clicable global, cumpleanos en dashboard/agenda, badges VIP/alergias/adeudo en ficha y POS
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import type { FC } from 'react';
|
||||
import { useEffect, useState, useCallback, useRef } from 'react';
|
||||
import { Clock, CheckCircle, XCircle, CheckCheck, UserCheck, UserX, Plus, PackageCheck, Sparkles, RotateCcw, LayoutGrid, List, Rows3, CalendarRange, CalendarClock, ChevronLeft, ChevronRight, Ban, Trash2, Phone } from 'lucide-react';
|
||||
import { Clock, CheckCircle, XCircle, CheckCheck, UserCheck, UserX, Plus, PackageCheck, Sparkles, RotateCcw, LayoutGrid, List, Rows3, CalendarRange, CalendarClock, ChevronLeft, ChevronRight, Ban, Trash2, Phone, Cake } from 'lucide-react';
|
||||
import Layout from '../components/Layout';
|
||||
import { usePatientDrawer } from '../components/PatientDrawer';
|
||||
import {
|
||||
Card,
|
||||
Button,
|
||||
@@ -44,6 +45,7 @@ const QUARTER_TIME_OPTIONS = (() => {
|
||||
const Agenda: FC = () => {
|
||||
const [searchParams] = useSearchParams();
|
||||
const { user } = useAuth();
|
||||
const { openPatient } = usePatientDrawer();
|
||||
// Rol médico: agenda restringida a su propio hr.employee (filtro también en servidor)
|
||||
const isMedico = user?.role === 'medico';
|
||||
const medicoEmployeeId = isMedico && user?.employee_id ? user.employee_id : null;
|
||||
@@ -202,6 +204,12 @@ const Agenda: FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
// Icono de cumpleaños: el paciente cumple años el día de la cita (viene del API)
|
||||
const birthdayIcon = (apt: Appointment, size = 11) =>
|
||||
apt.patient_birthday ? (
|
||||
<Cake size={size} className="inline shrink-0 text-pink-500" aria-label="Cumpleaños del paciente" />
|
||||
) : null;
|
||||
|
||||
const confirmTitle = (apt: Appointment): string => {
|
||||
if (['arrived', 'in_progress', 'done'].includes(apt.state)) return '2da confirmación: llegó a la clínica';
|
||||
if (apt.phone_confirmed) {
|
||||
@@ -1260,6 +1268,7 @@ const Agenda: FC = () => {
|
||||
>
|
||||
<div className="font-semibold truncate">
|
||||
<span title={confirmTitle(apt)} className="mr-1 align-middle">{confirmIndicator(apt)}</span>
|
||||
{apt.patient_birthday && <span title="Cumpleaños del paciente" className="mr-0.5 align-middle">{birthdayIcon(apt)}</span>}
|
||||
{apt.time} · {apt.patient}
|
||||
</div>
|
||||
<div className="truncate opacity-75">{apt.service}</div>
|
||||
@@ -1366,6 +1375,7 @@ const Agenda: FC = () => {
|
||||
>
|
||||
<div className="font-semibold truncate">
|
||||
<span title={confirmTitle(apt)} className="mr-0.5 align-middle">{confirmIndicator(apt, 10)}</span>
|
||||
{apt.patient_birthday && <span title="Cumpleaños del paciente" className="mr-0.5 align-middle">{birthdayIcon(apt, 10)}</span>}
|
||||
{apt.time} · {firstName(apt.patient)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1461,6 +1471,7 @@ const Agenda: FC = () => {
|
||||
className={`mb-0.5 rounded border-l-2 px-1 py-0.5 text-[10px] leading-tight shadow-card cursor-pointer transition-shadow hover:shadow-soft ${aptCategoryBar(apt)} ${gridStateClass[apt.state] || 'bg-theme-bg text-theme-heading'}`}
|
||||
>
|
||||
<span className="mr-0.5 align-middle">{confirmIndicator(apt, 10)}</span>
|
||||
{apt.patient_birthday && <span title="Cumpleaños del paciente" className="mr-0.5 align-middle">{birthdayIcon(apt, 10)}</span>}
|
||||
<span className="font-semibold">{apt.time}</span> {firstName(apt.patient)}
|
||||
</div>
|
||||
))
|
||||
@@ -1496,7 +1507,17 @@ const Agenda: FC = () => {
|
||||
<Clock size={14} className="inline mr-1 text-theme-muted" />
|
||||
{apt.time}
|
||||
</td>
|
||||
<td className="p-3 text-sm font-medium text-theme-heading">{apt.patient}</td>
|
||||
<td className="p-3 text-sm font-medium text-theme-heading">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openPatient(apt.patient_id)}
|
||||
className="hover:underline hover:text-theme-accent text-left"
|
||||
title="Abrir ficha del paciente"
|
||||
>
|
||||
{apt.patient_birthday && <span title="Cumpleaños del paciente" className="mr-1 align-middle">{birthdayIcon(apt, 12)}</span>}
|
||||
{apt.patient}
|
||||
</button>
|
||||
</td>
|
||||
<td className="p-3 text-sm text-theme-muted hidden sm:table-cell">
|
||||
<button type="button" onClick={() => openAptDetail(apt)} className="hover:underline hover:text-theme-heading text-left">
|
||||
{apt.service}
|
||||
@@ -1911,7 +1932,19 @@ const Agenda: FC = () => {
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="p-3 bg-theme-bg rounded-xl">
|
||||
<p className="text-xs text-theme-muted mb-1">Paciente</p>
|
||||
<p className="text-sm font-medium text-theme-heading">{detailApt.patient}</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => openPatient(detailApt.patient_id)}
|
||||
className="text-sm font-medium text-theme-heading underline underline-offset-2 hover:text-theme-accent text-left"
|
||||
title="Abrir ficha del paciente"
|
||||
>
|
||||
{detailApt.patient}
|
||||
</button>
|
||||
{detailApt.patient_birthday && (
|
||||
<span className="ml-1.5 inline-flex items-center gap-1 rounded-full bg-pink-50 border border-pink-300 px-2 py-0.5 text-[10px] font-medium text-pink-600 align-middle">
|
||||
{birthdayIcon(detailApt, 10)} Cumpleaños hoy
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="p-3 bg-theme-bg rounded-xl">
|
||||
<p className="text-xs text-theme-muted mb-1">Servicio</p>
|
||||
|
||||
Reference in New Issue
Block a user