Agenda: colores de estado mas fuertes y boton de En curso
- Badge y bloques de la cuadricula: cada estado con su color propio y fuerte (pendiente=ambar, confirmada=esmeralda, llego/en cabina=cielo, en curso=violeta, completada=gris, cancelo/no show=rosa). Antes varios compartian tonos pastel casi iguales. La leyenda del grid usa los mismos colores. - badgeForAppointmentState: llego e en_curso tenian variant de pendiente (se veian igual); ahora arrived y progress, y no_show/waitlist tienen las suyas. - Boton 'Marcar en curso' (accion start del backend) en las acciones rapidas de la vista de lista y tarjetas moviles, entre Llegada y Completar. - Mini-checklist del flujo en el tooltip de la cuadricula (solo lectura; click abre el detalle con el checklist completo interactivo).
This commit is contained in:
@@ -13,7 +13,9 @@ export type BadgeVariant =
|
||||
| 'warning'
|
||||
| 'danger'
|
||||
| 'success'
|
||||
| 'primary';
|
||||
| 'primary'
|
||||
| 'arrived'
|
||||
| 'progress';
|
||||
|
||||
interface BadgeProps {
|
||||
children: ReactNode;
|
||||
@@ -21,19 +23,24 @@ interface BadgeProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
// Colores de estado fuertes y distinguibles entre sí (petición de la agenda):
|
||||
// pendiente=ámbar, confirmada=esmeralda, llegó=cielo, en curso=violeta,
|
||||
// completada=gris neutro, cancelada/no-show=rosa.
|
||||
const variants: Record<BadgeVariant, string> = {
|
||||
confirmed: 'bg-theme-success text-theme-success-text',
|
||||
pending: 'bg-theme-accent-bg text-theme-heading',
|
||||
done: 'bg-theme-lilac text-theme-heading',
|
||||
cancelled: 'bg-rose-100 text-rose-700',
|
||||
paid: 'bg-theme-success text-theme-success-text',
|
||||
unpaid: 'bg-rose-100 text-rose-700',
|
||||
confirmed: 'bg-emerald-200 text-emerald-900',
|
||||
pending: 'bg-amber-100 text-amber-900',
|
||||
done: 'bg-slate-200 text-slate-700',
|
||||
cancelled: 'bg-rose-200 text-rose-900',
|
||||
paid: 'bg-emerald-200 text-emerald-900',
|
||||
unpaid: 'bg-rose-200 text-rose-900',
|
||||
info: 'bg-theme-sky text-theme-heading',
|
||||
default: 'bg-theme-accent-bg text-theme-muted',
|
||||
primary: 'bg-theme-accent text-theme-inverse',
|
||||
warning: 'bg-theme-warning text-theme-warning-text',
|
||||
danger: 'bg-rose-100 text-rose-700',
|
||||
success: 'bg-theme-success text-theme-success-text',
|
||||
warning: 'bg-amber-200 text-amber-900',
|
||||
danger: 'bg-rose-200 text-rose-900',
|
||||
success: 'bg-emerald-200 text-emerald-900',
|
||||
arrived: 'bg-sky-200 text-sky-900',
|
||||
progress: 'bg-violet-300 text-violet-950',
|
||||
};
|
||||
|
||||
const normalizeVariant = (variant: BadgeVariant): BadgeVariant => {
|
||||
@@ -41,6 +48,7 @@ const normalizeVariant = (variant: BadgeVariant): BadgeVariant => {
|
||||
if (variant === 'pending' || variant === 'warning') return variant;
|
||||
if (variant === 'done' || variant === 'info') return variant;
|
||||
if (variant === 'cancelled' || variant === 'danger' || variant === 'unpaid') return variant;
|
||||
if (variant === 'arrived' || variant === 'progress') return variant;
|
||||
return 'default';
|
||||
};
|
||||
|
||||
|
||||
@@ -4,11 +4,19 @@ export const badgeForAppointmentState = (state: string): BadgeVariant => {
|
||||
switch (state) {
|
||||
case 'confirmed':
|
||||
return 'confirmed';
|
||||
case 'arrived':
|
||||
return 'arrived';
|
||||
case 'in_progress':
|
||||
return 'progress';
|
||||
case 'done':
|
||||
case 'completed':
|
||||
return 'done';
|
||||
case 'cancelled':
|
||||
return 'cancelled';
|
||||
case 'no_show':
|
||||
return 'danger';
|
||||
case 'waitlist':
|
||||
return 'info';
|
||||
default:
|
||||
return 'pending';
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { FC } from 'react';
|
||||
import { useEffect, useState, useCallback, useRef, useMemo } from 'react';
|
||||
import { Clock, CheckCircle, XCircle, CheckCheck, UserCheck, UserX, Plus, PackageCheck, Sparkles, RotateCcw, LayoutGrid, List, Rows3, CalendarRange, CalendarClock, ChevronLeft, ChevronRight, Ban, Phone, Cake, CreditCard, Pencil, AlertTriangle, Printer, PhoneCall, Wallet, ChevronDown, CalendarOff, Users } from 'lucide-react';
|
||||
import { Clock, CheckCircle, XCircle, CheckCheck, UserCheck, UserX, Plus, PackageCheck, Sparkles, RotateCcw, LayoutGrid, List, Rows3, CalendarRange, CalendarClock, ChevronLeft, ChevronRight, Ban, Phone, Cake, CreditCard, Pencil, AlertTriangle, Printer, PhoneCall, Wallet, ChevronDown, CalendarOff, Users, Play, Check } from 'lucide-react';
|
||||
import Layout from '../components/Layout';
|
||||
import { usePatientDrawer } from '../components/PatientDrawer';
|
||||
import AgendaPrint from '../components/AgendaPrint';
|
||||
@@ -9,7 +9,7 @@ import RecargarSaldoModal from '../components/RecargarSaldoModal';
|
||||
import ReciboPrint from '../components/ReciboPrint';
|
||||
import BloqueosModal, { quickCloseOptions, type BloqueoQuickClose } from '../components/agenda/BloqueosModal';
|
||||
import { ListaEsperaPanel, ListaEsperaModal, whatsappLink } from '../components/agenda/ListaEspera';
|
||||
import { EstadoChecklist } from '../components/agenda/EstadoChecklist';
|
||||
import { EstadoChecklist, CHECKLIST_STEPS, stepIndex } from '../components/agenda/EstadoChecklist';
|
||||
import {
|
||||
Card,
|
||||
Button,
|
||||
@@ -625,12 +625,13 @@ const Agenda: FC = () => {
|
||||
// Colores por estado siguiendo la paleta de la marca (coral/mint/sky/lilac).
|
||||
// El color se lleva en el fondo; la barra lateral izquierda indica la categoría del servicio.
|
||||
const gridStateClass: Record<string, string> = {
|
||||
pending: 'bg-theme-accent-bg text-theme-heading',
|
||||
confirmed: 'bg-brand-mint text-theme-heading',
|
||||
arrived: 'bg-brand-sky text-theme-heading',
|
||||
in_progress: 'bg-brand-lilac text-theme-heading',
|
||||
done: 'bg-theme-bg text-theme-muted',
|
||||
no_show: 'bg-rose-50 text-rose-700',
|
||||
pending: 'bg-amber-100 text-amber-900',
|
||||
confirmed: 'bg-emerald-200 text-emerald-900',
|
||||
arrived: 'bg-sky-200 text-sky-900',
|
||||
in_progress: 'bg-violet-300 text-violet-950',
|
||||
done: 'bg-slate-200 text-slate-600',
|
||||
no_show: 'bg-rose-200 text-rose-900',
|
||||
cancelled: 'bg-rose-100 text-rose-700 line-through',
|
||||
};
|
||||
|
||||
// Barra lateral por categoría del servicio (service_category ya viene del API)
|
||||
@@ -667,11 +668,11 @@ const Agenda: FC = () => {
|
||||
};
|
||||
|
||||
const gridStateLegend: { state: string; label: string; dot: string }[] = [
|
||||
{ state: 'pending', label: 'Pendiente', dot: 'bg-brand-coral' },
|
||||
{ state: 'confirmed', label: 'Confirmada', dot: 'bg-brand-mint border border-emerald-700' },
|
||||
{ state: 'arrived', label: 'Llegó', dot: 'bg-brand-sky border border-sky-700' },
|
||||
{ state: 'in_progress', label: 'En curso', dot: 'bg-brand-lilac border border-violet-600' },
|
||||
{ state: 'done', label: 'Completada', dot: 'bg-theme-border-strong' },
|
||||
{ state: 'pending', label: 'Pendiente', dot: 'bg-amber-300' },
|
||||
{ state: 'confirmed', label: 'Confirmada', dot: 'bg-emerald-400' },
|
||||
{ state: 'arrived', label: 'Llegó', dot: 'bg-sky-400' },
|
||||
{ state: 'in_progress', label: 'En curso', dot: 'bg-violet-500' },
|
||||
{ state: 'done', label: 'Completada', dot: 'bg-slate-400' },
|
||||
];
|
||||
|
||||
const firstName = (full: string) => (full || '').trim().split(' ')[0] || '-';
|
||||
@@ -1607,6 +1608,17 @@ const Agenda: FC = () => {
|
||||
<UserCheck size={16} className="text-blue-600" />
|
||||
</Button>
|
||||
)}
|
||||
{apt.state === 'arrived' && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => handleStatus(apt.id, 'start')}
|
||||
disabled={updating === apt.id}
|
||||
title="Marcar en curso"
|
||||
>
|
||||
<Play size={16} className="text-violet-600" />
|
||||
</Button>
|
||||
)}
|
||||
{apt.state !== 'done' && apt.state !== 'cancelled' && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -2552,6 +2564,29 @@ const Agenda: FC = () => {
|
||||
<p className="text-theme-muted">Tel: {tooltip.apt.phone || '—'}</p>
|
||||
<p className="text-theme-muted">Médico: {tooltip.apt.doctor || 'Sin asignar'}</p>
|
||||
<p className="mt-1 text-theme-muted">{confirmTitle(tooltip.apt)}</p>
|
||||
{/* Mini-checklist del flujo (visual; el click abre el detalle con el checklist completo) */}
|
||||
<div className="mt-2 pt-2 border-t border-theme-border flex items-center gap-1">
|
||||
{CHECKLIST_STEPS.map((step) => {
|
||||
const i = stepIndex(tooltip.apt.state);
|
||||
const hecho = i >= 0 && stepIndex(step.state) <= i;
|
||||
return (
|
||||
<span
|
||||
key={step.state}
|
||||
title={step.label}
|
||||
className={`inline-flex items-center gap-0.5 rounded-full px-1.5 py-0.5 text-[9px] font-medium ${
|
||||
stepIndex(tooltip.apt.state) === stepIndex(step.state)
|
||||
? 'bg-violet-300 text-violet-950'
|
||||
: hecho
|
||||
? 'bg-emerald-200 text-emerald-900'
|
||||
: 'bg-theme-bg text-theme-muted'
|
||||
}`}
|
||||
>
|
||||
{hecho && <Check size={9} />}
|
||||
{step.label}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user