Médicos: rediseño del modal (header con stats, KPI cards, contacto compacto)
This commit is contained in:
@@ -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" /> },
|
||||||
|
|||||||
Reference in New Issue
Block a user