Permisos: marketing (export completo, agenda/citas, cumpleanos, visitas, WACRM), gerencia gestiona usuarios y roles, recepcion edita expedientes, cosmetologas ven agenda, fix del sidebar que no dejaba seleccionar Leads

This commit is contained in:
2026-09-18 03:23:44 +00:00
parent c38783eb31
commit cebf8d663d
13 changed files with 354 additions and 47 deletions

View File

@@ -1,5 +1,5 @@
import type { FC } from 'react';
import { useEffect, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { Phone, MapPin, Mail, Globe, Save, Building2, ArrowRightLeft, Percent, Check, Calculator } from 'lucide-react';
import Layout from '../components/Layout';
@@ -11,6 +11,7 @@ import AuditoriaPanel from '../components/AuditoriaPanel';
import ImpactoModal from '../components/ImpactoModal';
import { Card, Button, Input, PageHeader, toast } from '../components/ui';
import { odooApi, type Doctor } from '../services/odoo';
import { useAuth } from '../lib/auth';
const TABS = [
{ key: 'clinica', label: 'Clínica' },
@@ -45,8 +46,17 @@ const defaultSettings: ClinicSettings = {
const STORAGE_KEY = 'skeen_clinic_settings';
const Configuracion: FC = () => {
const { user } = useAuth();
// Gerencia gestiona usuarios y roles; la sección "Clínica" (tipo de cambio y
// comisiones) queda para administración del sistema.
const esAdmin = user?.role === 'admin';
const tabsVisibles = useMemo(
() => (esAdmin ? TABS : TABS.filter((t) => t.key === 'usuarios' || t.key === 'roles')),
[esAdmin],
);
const [searchParams, setSearchParams] = useSearchParams();
const tab = searchParams.get('tab') || 'clinica';
const tabPedida = searchParams.get('tab') || 'clinica';
const tab = tabsVisibles.some((t) => t.key === tabPedida) ? tabPedida : tabsVisibles[0].key;
const setTab = (t: string) => setSearchParams({ tab: t });
const [subcatalogo, setSubcatalogo] = useState<string>(() => {
const sub = searchParams.get('sub');
@@ -85,6 +95,7 @@ const Configuracion: FC = () => {
const [tcSaving, setTcSaving] = useState(false);
useEffect(() => {
if (!esAdmin) return;
odooApi.getExchangeRate()
.then((res) => {
if (res.current) {
@@ -123,6 +134,7 @@ const Configuracion: FC = () => {
const [impactoOpen, setImpactoOpen] = useState(false);
useEffect(() => {
if (!esAdmin) return;
odooApi.getCommissionDefault()
.then((res) => setCommissionDefault(String(res.commission_pct ?? 0)))
.catch(() => {/* odoo no disponible, ignorar */});
@@ -184,7 +196,7 @@ const Configuracion: FC = () => {
{/* Tabs de secciones */}
<div className="flex flex-wrap gap-1.5 mb-6">
{TABS.map((t) => (
{tabsVisibles.map((t) => (
<button
key={t.key}
type="button"