Médicos: rediseño del modal (header con stats, KPI cards, contacto compacto)

This commit is contained in:
2026-08-14 09:33:56 +00:00
parent 9aaa927339
commit f9fc66b343

View File

@@ -1,6 +1,6 @@
import type { FC } from 'react'; import type { FC } from 'react';
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { Mail, Phone, Stethoscope, Briefcase, Save, Percent, Eye, CalendarDays, FileText, ShoppingCart, Users } from 'lucide-react'; import { Mail, Phone, Stethoscope, Briefcase, Save, Percent, Eye, CalendarDays, FileText, ShoppingCart, Users, ClipboardList, CreditCard, DollarSign } from 'lucide-react';
import Layout from '../components/Layout'; import Layout from '../components/Layout';
import { Card, Badge, EmptyState, PageHeader, Skeleton, MobileCard, Input, Button, Modal, toast } from '../components/ui'; import { Card, Badge, EmptyState, PageHeader, Skeleton, MobileCard, Input, Button, Modal, toast } from '../components/ui';
import { odooApi, type Doctor, type DoctorHistory } from '../services/odoo'; import { odooApi, type Doctor, type DoctorHistory } from '../services/odoo';
@@ -265,16 +265,32 @@ const Medicos: FC = () => {
{selected && ( {selected && (
<div> <div>
{/* Header */} {/* Header */}
<div className="flex items-center gap-3 mb-5"> <div className="flex flex-wrap items-center justify-between gap-4 mb-5">
<span className="flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-brand-lilac text-sm font-bold text-theme-heading"> <div className="flex items-center gap-4 min-w-0">
{initials(selected.name)} <span className="flex h-14 w-14 shrink-0 items-center justify-center rounded-full bg-brand-lilac text-base font-bold text-theme-heading">
</span> {initials(selected.name)}
<div className="min-w-0"> </span>
<h3 className="font-heading text-xl text-theme-heading truncate">{selected.name}</h3> <div className="min-w-0">
<Badge variant="default" className="inline-flex items-center mt-1"> <h3 className="font-heading text-2xl text-theme-heading truncate">{selected.name}</h3>
<Briefcase size={12} className="mr-1" /> <Badge variant="default" className="inline-flex items-center mt-1">
{selected.job_title || 'Sin puesto'} <Briefcase size={12} className="mr-1" />
</Badge> {selected.job_title || 'Sin puesto'}
</Badge>
</div>
</div>
<div className="flex items-center gap-5">
<div className="text-right">
<p className="text-xs font-medium text-theme-muted uppercase">Pacientes</p>
<p className="font-heading text-xl text-theme-heading">{fmtNum(selected.patient_count ?? 0, 0)}</p>
</div>
<div className="text-right">
<p className="text-xs font-medium text-theme-muted uppercase">Citas</p>
<p className="font-heading text-xl text-theme-heading">{histLoading || !hist ? '…' : fmtNum(hist.totales.citas_total, 0)}</p>
</div>
<div className="text-right">
<p className="text-xs font-medium text-theme-muted uppercase">Visitas</p>
<p className="font-heading text-xl text-theme-heading">{histLoading || !hist ? '…' : fmtNum(hist.totales.visitas_total, 0)}</p>
</div>
</div> </div>
</div> </div>
@@ -295,29 +311,55 @@ const Medicos: FC = () => {
</div> </div>
{modalTab === 'info' ? ( {modalTab === 'info' ? (
<div className="space-y-3"> <div className="space-y-4">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3"> <div>
<div className="p-3 bg-theme-bg rounded-xl"> <p className="text-xs font-medium text-theme-muted uppercase mb-2">Resumen de actividad</p>
<p className="text-xs text-theme-muted mb-0.5">Teléfono</p> <div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
<p className="text-sm text-theme-heading flex items-center"><Phone size={14} className="mr-1.5 text-theme-muted" />{selected.work_phone || '—'}</p> <div className="p-3 bg-theme-bg rounded-xl">
<p className="text-xs text-theme-muted flex items-center gap-1 mb-1"><Users size={12} /> Pacientes asignados</p>
<p className="font-heading text-xl text-theme-heading">{fmtNum(selected.patient_count ?? 0, 0)}</p>
</div>
<div className="p-3 bg-theme-bg rounded-xl" title="Ventas con líneas recetadas por el médico, últimos 30 días">
<p className="text-xs text-theme-muted flex items-center gap-1 mb-1"><ShoppingCart size={12} /> Ventas 30d</p>
<p className="font-heading text-xl text-theme-heading">{selected.ventas_30d ?? 0}</p>
</div>
<div className="p-3 bg-theme-bg rounded-xl" title="Subtotal de líneas recetadas, últimos 30 días">
<p className="text-xs text-theme-muted flex items-center gap-1 mb-1"><DollarSign size={12} /> Total 30d</p>
<p className="font-heading text-xl text-theme-heading">{fmtMoney(selected.total_30d ?? 0)}</p>
</div>
<div className="p-3 bg-theme-bg rounded-xl" title="Pagado con tarjeta (terminal física), últimos 30 días">
<p className="text-xs text-theme-muted flex items-center gap-1 mb-1"><CreditCard size={12} /> Tarjeta 30d</p>
<p className="font-heading text-xl text-theme-heading">{fmtMoney(selected.card_30d ?? 0)}</p>
</div>
</div> </div>
<div className="p-3 bg-theme-bg rounded-xl"> <div className="mt-2">
<p className="text-xs text-theme-muted mb-0.5">Email</p> <span
<p className="text-sm text-theme-heading flex items-center truncate"><Mail size={14} className="mr-1.5 text-theme-muted shrink-0" />{selected.work_email || '—'}</p> className="inline-flex items-center rounded px-1.5 py-0.5 text-[11px] text-white"
</div> style={{ backgroundColor: '#605ca8' }}
<div className="p-3 bg-theme-bg rounded-xl"> title="Equivalente en USD al TC vigente"
<p className="text-xs text-theme-muted mb-0.5">% Comisión</p> >
<p className="text-sm text-theme-heading">{selected.commission_pct ?? 0}%</p> {selected.total_30d_usd == null ? '—' : `${fmtNum(selected.total_30d_usd)}`} usd
<p className="text-xs text-theme-muted mt-0.5">Se edita inline en la tabla</p> </span>
</div>
<div className="p-3 bg-theme-bg rounded-xl">
<p className="text-xs text-theme-muted mb-0.5">Pacientes asignados</p>
<p className="text-sm text-theme-heading flex items-center"><Users size={14} className="mr-1.5 text-theme-muted" />{selected.patient_count ?? 0}</p>
</div> </div>
</div> </div>
<div className="p-3 bg-theme-bg rounded-xl">
<p className="text-xs text-theme-muted mb-2">Últimos 30 días</p> <div>
<StatBadges d={selected} /> <p className="text-xs font-medium text-theme-muted uppercase mb-2">Contacto</p>
<div className="p-4 bg-theme-bg rounded-xl grid grid-cols-1 sm:grid-cols-3 gap-4">
<div>
<p className="text-xs text-theme-muted flex items-center gap-1 mb-0.5"><Phone size={12} /> Teléfono</p>
<p className="text-sm font-medium text-theme-heading">{selected.work_phone || '—'}</p>
</div>
<div className="min-w-0">
<p className="text-xs text-theme-muted flex items-center gap-1 mb-0.5"><Mail size={12} /> Email</p>
<p className="text-sm font-medium text-theme-heading truncate">{selected.work_email || '—'}</p>
</div>
<div>
<p className="text-xs text-theme-muted flex items-center gap-1 mb-0.5"><Percent size={12} /> Comisión</p>
<p className="text-sm font-medium text-theme-heading">{selected.commission_pct ?? 0}%</p>
<p className="text-[11px] text-theme-muted">Se edita inline en la tabla</p>
</div>
</div>
</div> </div>
</div> </div>
) : ( ) : (
@@ -328,9 +370,24 @@ const Medicos: FC = () => {
<EmptyState title="Sin historial" subtitle="No se pudo cargar la actividad del médico." /> <EmptyState title="Sin historial" subtitle="No se pudo cargar la actividad del médico." />
) : ( ) : (
<> <>
<p className="text-sm text-theme-muted mb-3"> <div className="grid grid-cols-2 sm:grid-cols-4 gap-2 mb-4">
{hist.totales.citas_total} citas en total · {hist.totales.visitas_total} visitas · {hist.totales.ventas_total_lineas} líneas recetadas por {fmtMoney(hist.totales.monto_total)} <div className="p-2.5 bg-theme-bg rounded-xl">
</p> <p className="text-[11px] text-theme-muted flex items-center gap-1 mb-0.5"><CalendarDays size={12} /> Citas totales</p>
<p className="font-heading text-base text-theme-heading">{fmtNum(hist.totales.citas_total, 0)}</p>
</div>
<div className="p-2.5 bg-theme-bg rounded-xl">
<p className="text-[11px] text-theme-muted flex items-center gap-1 mb-0.5"><ClipboardList size={12} /> Visitas totales</p>
<p className="font-heading text-base text-theme-heading">{fmtNum(hist.totales.visitas_total, 0)}</p>
</div>
<div className="p-2.5 bg-theme-bg rounded-xl">
<p className="text-[11px] text-theme-muted flex items-center gap-1 mb-0.5"><ShoppingCart size={12} /> Líneas recetadas</p>
<p className="font-heading text-base text-theme-heading">{fmtNum(hist.totales.ventas_total_lineas, 0)}</p>
</div>
<div className="p-2.5 bg-theme-bg rounded-xl">
<p className="text-[11px] text-theme-muted flex items-center gap-1 mb-0.5"><DollarSign size={12} /> Monto recetado</p>
<p className="font-heading text-base text-theme-heading">{fmtMoney(hist.totales.monto_total)}</p>
</div>
</div>
<div className="flex flex-wrap gap-1.5 mb-3"> <div className="flex flex-wrap gap-1.5 mb-3">
{([ {([
{ key: 'citas', label: 'Citas', icon: <CalendarDays size={14} className="mr-1" /> }, { key: 'citas', label: 'Citas', icon: <CalendarDays size={14} className="mr-1" /> },