Fix IA (max_tokens del modelo de razonamiento + fallback al gateway), sidebar con toggle arriba, busqueda de un solo campo (paciente/global), editar expediente, marketing en visitas, responsive de agenda; doc de diagnostico WhatsApp

This commit is contained in:
2026-09-24 10:01:55 +00:00
parent d75411f0d9
commit e32a79084a
14 changed files with 897 additions and 238 deletions

View File

@@ -3,6 +3,7 @@ import { Fragment, useEffect, useState, useCallback } from 'react';
import { Plus, Trash2, CreditCard, ShoppingCart, Search, Undo2, Download, Printer, ChevronDown, ChevronRight, Ban } from 'lucide-react';
import Layout from '../components/Layout';
import { usePatientDrawer } from '../components/PatientDrawer';
import PatientCombobox from '../components/PatientCombobox';
import TicketPrint from '../components/TicketPrint';
import {
Card,
@@ -19,7 +20,7 @@ import {
toast,
badgeForSaleState,
} from '../components/ui';
import { odooApi, type Sale, type SaleLine, type Service, type Patient, type Doctor } from '../services/odoo';
import { odooApi, type Sale, type SaleLine, type Service, type Doctor } from '../services/odoo';
import { exportToExcel } from '../lib/exporter';
import { useAuth } from '../lib/auth';
import { DATE_PRESETS, rangeForPreset, readDateFilterPrefs, writeDateFilterPrefs, type DatePreset } from '../lib/dateRange';
@@ -50,7 +51,6 @@ const Ventas: FC = () => {
const puedeCancelar = ROLES_CANCELAN.includes(user?.role || '');
const [sales, setSales] = useState<Sale[]>([]);
const [services, setServices] = useState<Service[]>([]);
const [patients, setPatients] = useState<Patient[]>([]);
const [doctors, setDoctors] = useState<Doctor[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
@@ -81,7 +81,6 @@ const Ventas: FC = () => {
const [pageSize, setPageSize] = useState(50);
const [total, setTotal] = useState(0);
const [totalPages, setTotalPages] = useState(0);
const [patientSearch, setPatientSearch] = useState('');
// ---- Rango de fechas recordado (presets) ----
const saved = readDateFilterPrefs(FILTERS_KEY);
@@ -154,20 +153,6 @@ const Ventas: FC = () => {
setPage(1);
}, [search]);
// Cargar pacientes para el selector (búsqueda server-side)
useEffect(() => {
if (!createOpen) return;
const t = setTimeout(async () => {
try {
const res = await odooApi.getPatients({ search: patientSearch, page_size: 50 });
if (res.status === 'success') setPatients(res.patients);
} catch (err) {
console.error(err);
}
}, 250);
return () => clearTimeout(t);
}, [createOpen, patientSearch]);
const addLine = () =>
setLines([...lines, emptyLine()]);
@@ -362,11 +347,6 @@ const Ventas: FC = () => {
}
};
const patientOptions = [
{ value: '', label: 'Seleccionar paciente...' },
...patients.map((p) => ({ value: String(p.id), label: `${p.name} (${p.phone})` })),
];
const serviceOptions = [
{ value: '', label: 'Servicio...' },
...services.map((s) => ({ value: String(s.id), label: `${s.name} — $${s.price}` })),
@@ -694,17 +674,12 @@ const Ventas: FC = () => {
}
>
<div className="space-y-4">
<Input
label="Buscar paciente"
placeholder="Escribe nombre o teléfono..."
value={patientSearch}
onChange={(e) => setPatientSearch(e.target.value)}
/>
<Select
<PatientCombobox
label="Paciente *"
options={patientOptions}
value={patientId}
onChange={(e) => setPatientId(e.target.value)}
placeholder="Escribe nombre, apellidos o teléfono..."
emptyHint="Sin resultados. Da de alta al paciente en Pacientes y vuelve a intentar."
onChange={(id) => setPatientId(id)}
/>
<div>