Oleada UX 2: Agenda (mensajes de conflicto humanos, imprimir agenda del dia, confirmacion en lote, prefs persistentes) y Pacientes (acciones en ficha, timeline unificada, aviso de telefono duplicado) + fixes de precarga e impresion
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
import Layout from '../components/Layout';
|
||||
import PatientAdjuntos from '../components/PatientAdjuntos';
|
||||
import EstadoCuentaPrint from '../components/EstadoCuentaPrint';
|
||||
import { usePatientDrawer } from '../components/PatientDrawer';
|
||||
import {
|
||||
Card,
|
||||
Button,
|
||||
@@ -142,6 +143,9 @@ const Pacientes: FC = () => {
|
||||
const [editing, setEditing] = useState<Patient | null>(null);
|
||||
const [photo, setPhoto] = useState<string | null>(null);
|
||||
const photoRef = useRef<HTMLInputElement>(null);
|
||||
const { openPatient } = usePatientDrawer();
|
||||
// Aviso de duplicado por teléfono/móvil en el formulario de alta/edición
|
||||
const [phoneDup, setPhoneDup] = useState<{ id: number; name: string } | null>(null);
|
||||
|
||||
const emptyForm: PatientForm = {
|
||||
name: '',
|
||||
@@ -290,6 +294,36 @@ const Pacientes: FC = () => {
|
||||
setForm((prev) => ({ ...prev, ...patch }));
|
||||
};
|
||||
|
||||
// Duplicado de teléfono: se consulta con debounce al escribir (phone o mobile).
|
||||
// Al editar se excluye el propio paciente para no auto-alertarse.
|
||||
useEffect(() => {
|
||||
if (!modalOpen) {
|
||||
setPhoneDup(null);
|
||||
return;
|
||||
}
|
||||
const digitsOf = (s: string) => (s || '').replace(/\D/g, '');
|
||||
const phone = (form.phone || '').trim();
|
||||
const mobile = (form.mobile || '').trim();
|
||||
const checkVal = digitsOf(phone).length >= 7 ? phone : mobile;
|
||||
if (digitsOf(checkVal).length < 7) {
|
||||
setPhoneDup(null);
|
||||
return;
|
||||
}
|
||||
let active = true;
|
||||
const t = setTimeout(async () => {
|
||||
try {
|
||||
const res = await odooApi.checkPatientPhone(checkVal, editing?.id);
|
||||
if (active) setPhoneDup(res.status === 'success' && res.exists && res.patient ? { id: res.patient.id, name: res.patient.name } : null);
|
||||
} catch {
|
||||
if (active) setPhoneDup(null);
|
||||
}
|
||||
}, 400);
|
||||
return () => {
|
||||
active = false;
|
||||
clearTimeout(t);
|
||||
};
|
||||
}, [form.phone, form.mobile, editing?.id, modalOpen]);
|
||||
|
||||
const onPhotoChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
e.target.value = '';
|
||||
@@ -638,6 +672,21 @@ const Pacientes: FC = () => {
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<Input label="Nombre *" value={form.name} onChange={(e) => updateForm({ name: e.target.value })} />
|
||||
<Input label="Teléfono *" value={form.phone} onChange={(e) => updateForm({ phone: e.target.value })} />
|
||||
{phoneDup && (
|
||||
<div className="sm:col-span-2 rounded-xl border border-amber-300 bg-amber-50 px-3 py-2.5 text-sm text-amber-800">
|
||||
<p className="font-medium">
|
||||
Este teléfono ya está registrado: {phoneDup.name}. ¿Es la misma persona?
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2 mt-2">
|
||||
<Button size="sm" variant="outline" onClick={() => openPatient(phoneDup.id)}>
|
||||
Abrir su ficha
|
||||
</Button>
|
||||
<Button size="sm" variant="ghost" onClick={() => setPhoneDup(null)}>
|
||||
Continuar de todos modos
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<Input label="Email" type="email" value={form.email} onChange={(e) => updateForm({ email: e.target.value })} />
|
||||
<Input label="Celular" value={form.mobile} onChange={(e) => updateForm({ mobile: e.target.value })} />
|
||||
<Input label="Teléfono casa" value={form.home_phone} onChange={(e) => updateForm({ home_phone: e.target.value })} />
|
||||
|
||||
Reference in New Issue
Block a user