import React, { useState, useEffect } from 'react'; import { useConfirm } from '../contexts/ConfirmContext.tsx'; import { Users, Calendar as CalendarIcon, LayoutDashboard, DollarSign, RefreshCw, Activity, BookOpen, Award, Megaphone, LogOut, ClipboardList, Bell, Building2, User, BarChart3, Settings, FileText, Puzzle, ScanLine, Monitor, UsersRound, GraduationCap, UserSearch, Briefcase, TrendingUp, Eye, FlaskConical, DoorOpen, Sparkles, FileSignature, Percent, ShieldAlert, ChevronDown, MessageCircle, Bot, Smartphone, Power } from 'lucide-react'; import { ToothIcon } from './ToothIcon.tsx'; import { HybridBackend } from '../services/backend.ts'; import { getActivePlugins, isPluginActive } from '../views/plugins/pluginRegistry.ts'; interface SidebarProps { activeTab: string; setActiveTab: (t: string) => void; } export const Sidebar: React.FC = ({ activeTab, setActiveTab }) => { const currentRole = HybridBackend.getCurrentRole(); const userName = HybridBackend.getCurrentUserName(); const userEmail = HybridBackend.getCurrentUser(); const activeWorkspace = HybridBackend.getActiveWorkspace(); const clinColor = activeWorkspace?.cor || (currentRole === 'superadmin' ? '#dc2626' : '#2563eb'); const activePluginIds = getActivePlugins(); const isTutor = (() => { try { return JSON.parse(localStorage.getItem('SCOREODONTO_USER_DATA') || '{}').is_tutor === true; } catch { return false; } })(); // Acesso do usuário às áreas do WhatsApp (Inbox/Secretária). null = ainda carregando // (fail-open: mostra até saber). Dono e autorizados veem; os demais não. const [waAccess, setWaAccess] = useState<{ isOwner: boolean; inbox: boolean; secretaria: boolean; delete_msg: boolean } | null>(null); useEffect(() => { let alive = true; if (isPluginActive('newwhats')) { HybridBackend.getWaAccess().then(a => { if (alive) setWaAccess(a); }).catch(() => {}); } return () => { alive = false; }; }, [activeWorkspace?.id]); const canSeeInbox = !waAccess || waAccess.isOwner || waAccess.inbox; const canSeeSecretaria = !waAccess || waAccess.isOwner || waAccess.secretaria; const pluginMenuItems: Array<{ id: string; label: string; icon: any; color: string }> = []; if (activePluginIds.includes('rx-scoreodonto')) { // Radiografias são ODONTOLÓGICAS: só papéis odonto (dentista + donos/funcionário). // Biomédico e protético NÃO veem RX. if (['dentista', 'funcionario', 'admin', 'donoclinica', 'donoconsultorio'].includes(currentRole)) { pluginMenuItems.push({ id: 'rx-radiografias', label: 'RX / RADIOGRAFIAS', icon: ScanLine, color: '#7c3aed' }); } // DISPOSITIVOS RX (config + PCs): admin/donos da clínica e superadmin (gestão global). if (['admin', 'donoclinica', 'donoconsultorio', 'superadmin'].includes(currentRole)) { pluginMenuItems.push({ id: 'rx-config', label: 'DISPOSITIVOS RX', icon: Monitor, color: '#7c3aed' }); } } // NewWhats: Inbox + Secretária IA — para TODOS os usuários quando o plugin está // ativo (a CONFIG do plugin fica no catálogo PLUGINS, exclusivo do superadmin). if (isPluginActive('newwhats')) { if (canSeeInbox) pluginMenuItems.push({ id: 'wa-inbox', label: 'WHATSAPP', icon: MessageCircle, color: '#16a34a' }); // INSTÂNCIAS (gestão/QR) só para o dono; os demais operam via Inbox. if (!waAccess || waAccess.isOwner) pluginMenuItems.push({ id: 'wa-sessions', label: 'INSTÂNCIAS', icon: Smartphone, color: '#0ea5e9' }); if (canSeeSecretaria) pluginMenuItems.push({ id: 'wa-secretaria', label: 'SECRETÁRIA IA', icon: Bot, color: '#2563eb' }); } // Locação de Salas: menus de LOCADOR (administra as próprias salas) e LOCATÁRIO (aluga de // terceiros). São decisões de NEGÓCIO do titular — não cabem a funcionário (subordinado) nem // a paciente. Funcionário, quando muito, opera a AGENDA da sala (workspace tipo='sala'), não // a locação. Por isso restringimos a papéis que podem ser titulares de sala. const PAPEIS_LOCACAO = ['donosala', 'donoclinica', 'donoconsultorio', 'admin', 'dentista', 'biomedico', 'protetico']; if (PAPEIS_LOCACAO.includes(currentRole)) { pluginMenuItems.push({ id: 'minhas-salas', label: 'MINHAS SALAS', icon: DoorOpen, color: '#0d9488' }); pluginMenuItems.push({ id: 'alugar-salas', label: 'ALUGAR SALAS', icon: Building2, color: '#0d9488' }); } // Marketplace de Profissionais: clínicas contratam, profissionais divulgam perfil/recebem propostas. // O protético DEPENDE desta tela para publicar o perfil no diretório e ser encontrado pelas clínicas // — por isso aparece sempre para ele, mesmo sem o plugin ativado. if ((activePluginIds.includes('profissionais-marketplace') || currentRole === 'protetico' || currentRole === 'biomedico' || currentRole === 'dentista') && currentRole !== 'superadmin' && currentRole !== 'paciente') { pluginMenuItems.push({ id: 'marketplace-profissionais', label: 'PROFISSIONAIS', icon: UserSearch, color: '#0d9488' }); } // OBS: o catálogo de PLUGINS é EXCLUSIVO do superadmin (gerência/ativação). // Não exibir para nenhum outro papel. const menuItems = [ { id: 'superadmin-overview', label: 'VISÃO GERAL', icon: TrendingUp }, { id: 'superadmin-users', label: 'USUÁRIOS', icon: Users }, { id: 'superadmin-clinicas', label: 'CLÍNICAS', icon: Building2 }, { id: 'superadmin-logs', label: 'ACESSOS', icon: Activity }, { id: 'superadmin-financeiro', label: 'FINANCEIRO', icon: DollarSign }, { id: 'superadmin-notificacoes', label: 'NOTIFICAÇÕES', icon: Bell }, { id: 'superadmin-secretaria', label: 'SECRETÁRIA IA', icon: Power }, { id: 'sala-home', label: 'PAINEL DA SALA', icon: DoorOpen }, { id: 'lab-home', label: 'PAINEL DO LAB', icon: LayoutDashboard }, { id: 'lab-bancada', label: 'BANCADA', icon: FlaskConical }, { id: 'lab-agenda', label: 'AGENDA', icon: CalendarIcon }, { id: 'lab-clientes', label: 'CLIENTES', icon: Building2 }, { id: 'lab-cotacoes', label: 'COTAÇÕES', icon: FileText }, { id: 'lab-financeiro', label: 'FINANCEIRO', icon: DollarSign }, { id: 'lab-indicadores', label: 'INDICADORES', icon: BarChart3 }, { id: 'lab-equipe', label: 'EQUIPE', icon: UsersRound }, { id: 'dashboard', label: 'VISÃO GERAL', icon: LayoutDashboard }, { id: 'agenda', label: 'AGENDA', icon: CalendarIcon }, { id: 'pacientes', label: 'PACIENTES', icon: Users }, { id: 'lancar-gto', label: 'LANÇAR GTO', icon: ClipboardList }, { id: 'proteses', label: 'PRÓTESES', icon: FlaskConical }, { id: 'ortodontia', label: 'ORTODONTIA', icon: Activity }, { id: 'financeiro', label: 'FINANCEIRO', icon: DollarSign }, { id: 'relatorios', label: 'RELATÓRIOS', icon: BarChart3 }, { id: 'comissoes', label: 'COMISSÕES', icon: Percent }, { id: 'glosas', label: 'GLOSAS', icon: ShieldAlert }, { id: 'orcamentos', label: 'ORÇAMENTOS', icon: FileText }, { id: 'contratos', label: 'CONTRATOS', icon: FileSignature }, { id: 'leads', label: 'GESTÃO DE LEADS', icon: Megaphone }, { id: 'tratamentos', label: 'MEUS TRATAMENTOS', icon: ClipboardList }, { id: 'clinicas', label: 'MINHAS CLÍNICAS', icon: Building2 }, { id: 'dentistas', label: 'DENTISTAS', icon: ToothIcon }, { id: 'especialidades', label: 'ESPECIALIDADES', icon: Award }, { id: 'procedimentos', label: 'PROCEDIMENTOS', icon: ClipboardList }, { id: 'planos', label: 'PLANOS / CONVÊNIOS', icon: BookOpen }, { id: 'gestao-equipe', label: 'EQUIPE / USUÁRIOS', icon: UsersRound }, { id: 'diretorio-profissionais', label: 'DIRETÓRIO PROF.', icon: UserSearch }, { id: 'admin-gestao-tutores', label: 'GESTÃO TUTORES', icon: GraduationCap }, { id: 'candidatura-tutor', label: 'SER TUTOR ORTO', icon: GraduationCap }, { id: 'tutoria-painel', label: 'PAINEL TUTOR', icon: GraduationCap }, { id: 'notificacoes', label: 'NOTIFICAÇÕES', icon: Bell }, { id: 'plugins', label: 'PLUGINS', icon: Puzzle }, { id: 'configuracoes', label: 'CONFIGURAÇÕES', icon: Settings }, ].filter(item => { // superadmin: itens específicos de administração global if (currentRole === 'superadmin') return ['superadmin-overview', 'superadmin-users', 'superadmin-clinicas', 'superadmin-logs', 'superadmin-financeiro', 'superadmin-notificacoes', 'plugins', 'configuracoes'].includes(item.id); // telas de superadmin são exclusivas do superadmin (não vazam p/ admin/donos) if (item.id.startsWith('superadmin')) return false; // PAINEL DA SALA só existe quando o workspace ativo é uma sala. if (item.id === 'sala-home') return (activeWorkspace as any)?.tipo === 'sala'; // PAINEL DO LAB / BANCADA só existem no workspace de laboratório. if (['lab-home', 'lab-bancada', 'lab-agenda', 'lab-clientes', 'lab-cotacoes', 'lab-financeiro', 'lab-equipe', 'lab-indicadores'].includes(item.id)) return (activeWorkspace as any)?.tipo === 'laboratorio'; // Contexto de SALA (workspace tipo='sala'): isola da clínica — menu base só conta/notificações. // (MINHAS SALAS / ALUGAR SALAS são itens de plugin, mostrados à parte.) if ((activeWorkspace as any)?.tipo === 'sala') return ['sala-home', 'notificacoes', 'configuracoes'].includes(item.id); // Contexto de LABORATÓRIO (workspace tipo='laboratorio'): área própria do protético — // painel do lab + bancada de produção, sem as telas clínicas da clínica. if ((activeWorkspace as any)?.tipo === 'laboratorio') { // Técnico (vínculo tecnico_protese) só opera a produção; gestão é do dono. const ehTecnico = (activeWorkspace as any)?.role === 'tecnico_protese'; return ehTecnico ? ['lab-home', 'lab-bancada', 'notificacoes', 'configuracoes'].includes(item.id) : ['lab-home', 'lab-bancada', 'lab-agenda', 'lab-clientes', 'lab-cotacoes', 'lab-financeiro', 'lab-indicadores', 'lab-equipe', 'notificacoes', 'configuracoes'].includes(item.id); } // plugins e gestão de tutores: exclusivo superadmin if (item.id === 'plugins' || item.id === 'admin-gestao-tutores') return false; // diretório antigo é absorvido pelo plugin Profissionais quando ele está ativo if (item.id === 'diretorio-profissionais' && activePluginIds.includes('profissionais-marketplace')) return false; // painel tutor: só para tutores aprovados if (item.id === 'tutoria-painel') return isTutor; // RBAC por CARGO: membro não-dono com permissões definidas no workspace ativo const wsRole = (activeWorkspace as any)?.role; const wsPerms = (activeWorkspace as any)?.permissoes; const isWsOwner = ['admin', 'donoclinica', 'donoconsultorio'].includes(wsRole || ''); if (!isWsOwner && Array.isArray(wsPerms) && wsPerms.length > 0) { // Navegação/conta sempre visível if (['clinicas', 'configuracoes', 'notificacoes'].includes(item.id)) return true; return wsPerms.includes(item.id); } if (['admin', 'donoclinica', 'donoconsultorio'].includes(currentRole)) return true; // Dono de sala (locador não-clínico): só conta/notificações; salas vêm como itens de plugin. if (currentRole === 'donosala') return ['notificacoes', 'configuracoes'].includes(item.id); if (currentRole === 'paciente') return ['tratamentos', 'notificacoes', 'configuracoes'].includes(item.id); // Dentista: clínico odonto completo (ortodontia, tutoria orto). if (currentRole === 'dentista') return ['dashboard', 'tratamentos', 'agenda', 'ortodontia', 'proteses', 'notificacoes', 'clinicas', 'configuracoes', 'candidatura-tutor', 'diretorio-profissionais'].includes(item.id); // Biomédico(a): seu próprio catálogo (especialidades/procedimentos de biomedicina); // SEM itens odontológicos (ortodontia, RX, tutoria orto). if (currentRole === 'biomedico') return ['dashboard', 'agenda', 'tratamentos', 'especialidades', 'procedimentos', 'notificacoes', 'clinicas', 'configuracoes', 'diretorio-profissionais'].includes(item.id); // Protético: laboratório — agenda própria + GTO; sem ortodontia/RX/pacientes clínicos. // Protético: opera dentro do workspace 'laboratorio' (menu governado pelo branch tipo acima). // Este é só o fallback fora desse contexto — sem telas clínicas (agenda/GTO/tratamentos). if (currentRole === 'protetico') return ['notificacoes', 'clinicas', 'configuracoes', 'diretorio-profissionais'].includes(item.id); if (currentRole === 'funcionario') return ['dashboard', 'leads', 'pacientes', 'tratamentos', 'lancar-gto', 'proteses', 'agenda', 'financeiro', 'relatorios', 'glosas', 'notificacoes', 'clinicas', 'configuracoes', 'orcamentos', 'contratos', 'diretorio-profissionais'].includes(item.id); return false; }); const confirm = useConfirm(); const [wsOpen, setWsOpen] = React.useState(false); const handleLogout = async () => { if (await confirm('TEM CERTEZA QUE DESEJA SAIR DO SISTEMA?')) { HybridBackend.logout(); } }; const workspaceName = activeWorkspace?.nome || 'SCOREODONTO'; const activeIsSala = (activeWorkspace as any)?.tipo === 'sala'; const WorkspaceIcon = activeIsSala ? DoorOpen : currentRole === 'donoconsultorio' ? Briefcase : Building2; // Workspaces para o seletor: clínicas (via vínculo) + salas próprias, agrupados. const allWorkspaces: any[] = HybridBackend.getWorkspaces() || []; const wsClinicas = allWorkspaces.filter(w => w.tipo !== 'sala'); const wsSalas = allWorkspaces.filter(w => w.tipo === 'sala'); const canSwitch = allWorkspaces.length > 1; return (
{wsOpen && canSwitch && ( <>
setWsOpen(false)} />
{wsClinicas.length > 0 &&

Clínicas

} {wsClinicas.map(w => ( ))} {wsSalas.length > 0 &&

Salas

} {wsSalas.map(w => ( ))}
)}
); };