From 57efc3d9d331e52d17c89c4b60ec68ef939caa86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Consultor=C3=ADa=20Alcaraz=20Salazar?= Date: Mon, 31 Aug 2026 03:56:48 +0000 Subject: [PATCH] Rol medico: pagina Mis ventas con KPIs y comision estimada, menu restringido y bloqueo de endpoints de dinero global --- frontend/src/App.tsx | 2 + frontend/src/components/ClasicoLayout.tsx | 2 + frontend/src/components/HomeNestLayout.tsx | 2 + frontend/src/components/SkeenLayout.tsx | 2 + frontend/src/lib/auth.tsx | 1 + frontend/src/pages/MisVentas.tsx | 179 ++++++++++++++++++ frontend/src/services/odoo.ts | 31 +++ .../skeen_whatsapp/controllers/auth.py | 15 ++ .../skeen_whatsapp/controllers/frontend.py | 136 ++++++++++++- odoo-addons/skeen_whatsapp/roles.py | 1 + 10 files changed, 366 insertions(+), 5 deletions(-) create mode 100644 frontend/src/pages/MisVentas.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index bd4c6e4..b202089 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -23,6 +23,7 @@ import Inventario from './pages/Inventario'; import WacrmMessages from './pages/WacrmMessages'; import WacrmLeads from './pages/WacrmLeads'; import Chat from './pages/Chat'; +import MisVentas from './pages/MisVentas'; import Login from './pages/Login'; import './App.css'; @@ -64,6 +65,7 @@ const App: FC = () => { } /> } /> } /> + } /> } /> } /> } /> diff --git a/frontend/src/components/ClasicoLayout.tsx b/frontend/src/components/ClasicoLayout.tsx index 503bc2f..a6ac59a 100644 --- a/frontend/src/components/ClasicoLayout.tsx +++ b/frontend/src/components/ClasicoLayout.tsx @@ -28,6 +28,7 @@ import { ChevronUp, ChevronDown, DollarSign, + CircleDollarSign, } from 'lucide-react'; import { Button, Modal, Input } from './ui'; import QuickLinksDock, { QuickLinksModePicker } from './QuickLinksDock'; @@ -133,6 +134,7 @@ const menuTree: MenuEntry[] = [ { label: 'Ventas del día', icon: ShoppingCart, href: '/ventas' }, { label: 'Punto de Venta', icon: Store, href: '/pos' }, { label: 'Clientes', icon: Users, href: '/pacientes' }, + { label: 'Mis ventas', icon: CircleDollarSign, href: '/mis-ventas' }, ]; // Hojas planas (para accesos rápidos y dock) diff --git a/frontend/src/components/HomeNestLayout.tsx b/frontend/src/components/HomeNestLayout.tsx index fccd1e0..ff57e2c 100644 --- a/frontend/src/components/HomeNestLayout.tsx +++ b/frontend/src/components/HomeNestLayout.tsx @@ -32,6 +32,7 @@ import { Pencil, ChevronUp, MessageCircle, + CircleDollarSign, } from 'lucide-react'; import { Button, Modal, Input } from './ui'; import QuickLinksDock, { QuickLinksModePicker } from './QuickLinksDock'; @@ -66,6 +67,7 @@ const menuSections = [ { label: 'Visitas', icon: ClipboardList, href: '/visitas' }, { label: 'Consultas', icon: ClipboardPlus, href: '/consultas' }, { label: 'Expedientes', icon: FolderClock, href: '/expedientes' }, + { label: 'Mis ventas', icon: CircleDollarSign, href: '/mis-ventas' }, { label: 'Médicos', icon: Stethoscope, href: '/medicos' }, { label: 'Servicios', icon: Briefcase, href: '/servicios' }, ], diff --git a/frontend/src/components/SkeenLayout.tsx b/frontend/src/components/SkeenLayout.tsx index 013081c..90c56bf 100644 --- a/frontend/src/components/SkeenLayout.tsx +++ b/frontend/src/components/SkeenLayout.tsx @@ -32,6 +32,7 @@ import { ChevronUp, ChevronDown, MessageCircle, + CircleDollarSign, } from 'lucide-react'; import { Button, Modal, Input } from './ui'; import QuickLinksDock, { QuickLinksModePicker } from './QuickLinksDock'; @@ -66,6 +67,7 @@ const menuGroups = [ { label: 'Visitas', icon: ClipboardList, href: '/visitas' }, { label: 'Consultas', icon: ClipboardPlus, href: '/consultas' }, { label: 'Expedientes', icon: FolderClock, href: '/expedientes' }, + { label: 'Mis ventas', icon: CircleDollarSign, href: '/mis-ventas' }, { label: 'Médicos', icon: Stethoscope, href: '/medicos' }, { label: 'Servicios', icon: Briefcase, href: '/servicios' }, ], diff --git a/frontend/src/lib/auth.tsx b/frontend/src/lib/auth.tsx index 1df2e3f..c6951a3 100644 --- a/frontend/src/lib/auth.tsx +++ b/frontend/src/lib/auth.tsx @@ -23,6 +23,7 @@ const ROLE_RANK: Record = { const MENU_ROLES: Record = { '/': ['admin', 'recepcion', 'medico', 'lectura'], '/agenda': ['admin', 'recepcion', 'medico'], + '/mis-ventas': ['medico'], '/pacientes': ['admin', 'recepcion', 'medico', 'lectura'], '/pos': ['admin', 'recepcion'], '/visitas': ['admin', 'recepcion', 'medico'], diff --git a/frontend/src/pages/MisVentas.tsx b/frontend/src/pages/MisVentas.tsx new file mode 100644 index 0000000..39f9998 --- /dev/null +++ b/frontend/src/pages/MisVentas.tsx @@ -0,0 +1,179 @@ +import type { FC } from 'react'; +import { useEffect, useState, useCallback } from 'react'; +import { CircleDollarSign, CreditCard, ShoppingCart, Percent, DollarSign } from 'lucide-react'; +import Layout from '../components/Layout'; +import { + Card, + Select, + EmptyState, + PageHeader, + Skeleton, + toast, +} from '../components/ui'; +import { odooApi, type MySalesResponse } from '../services/odoo'; + +const rangeOptions = [ + { value: '30d', label: 'Últimos 30 días' }, + { value: 'month', label: 'Este mes' }, + { value: 'last_month', label: 'Mes pasado' }, +]; + +const mxn = (n: number) => + `$${n.toLocaleString('es-MX', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; + +const MisVentas: FC = () => { + const [range, setRange] = useState<'30d' | 'month' | 'last_month'>('30d'); + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const load = useCallback(async () => { + try { + setLoading(true); + setError(null); + const res = await odooApi.getMySales(range); + if (res.status === 'success') setData(res); + } catch (err) { + setError('Error al cargar tus ventas'); + toast.error('Error al cargar tus ventas'); + console.error(err); + } finally { + setLoading(false); + } + }, [range]); + + useEffect(() => { + load(); + }, [load]); + + const kpis = data?.kpis; + + return ( + + +