Files
clube67_newwhats.local/base-scoreodonto/scoreodonto/components/Sidebar.tsx
T
VPS 4 Deploy Agent 5ec6bd6354
continuous-integration/webhook Falha no deploy de clube67_newwhats.local (VPS 4)
chore(ops): migrate clube67 to newwhats.clube67.com directory
2026-05-18 03:26:41 +02:00

152 lines
6.6 KiB
TypeScript

import React from 'react';
import {
Users, Calendar as CalendarIcon, LayoutDashboard, Stethoscope,
DollarSign, RefreshCw, Database, Activity, BookOpen, Award, Megaphone, LogOut, ClipboardList, Bell, Building2, User, BarChart3
} from 'lucide-react';
import { HybridBackend } from '../services/backend.ts';
import { GoogleConnectButton } from './GoogleConnectButton.tsx';
import { useState, useEffect } from 'react';
interface SidebarProps {
activeTab: string;
setActiveTab: (t: string) => void;
}
export const Sidebar: React.FC<SidebarProps> = ({ activeTab, setActiveTab }) => {
const [connectedAccounts, setConnectedAccounts] = useState<any[]>([]);
const currentRole = HybridBackend.getCurrentRole();
const userName = HybridBackend.getCurrentUserName();
const userEmail = HybridBackend.getCurrentUser();
const activeWorkspace = HybridBackend.getActiveWorkspace();
const clinColor = activeWorkspace?.cor || '#2563eb';
const fetchGoogleStatus = async () => {
try {
const response = await fetch('http://localhost:3005/api/auth/google/status');
const data = await response.json();
setConnectedAccounts(data);
} catch (error) {
console.error("Erro ao buscar status do Google:", error);
}
};
useEffect(() => {
fetchGoogleStatus();
}, []);
const menuItems = [
{ id: 'dashboard', label: 'VISÃO GERAL', icon: LayoutDashboard },
{ id: 'clinicas', label: 'MINHAS CLÍNICAS', icon: Building2 },
{ id: 'leads', label: 'GESTÃO DE LEADS', icon: Megaphone },
{ id: 'pacientes', label: 'PACIENTES', icon: Users },
{ id: 'tratamentos', label: 'MEUS TRATAMENTOS', icon: ClipboardList },
{ id: 'lancar-gto', label: 'LANÇAR GTO', icon: ClipboardList },
{ id: 'agenda', label: 'AGENDA', icon: CalendarIcon },
{ id: 'ortodontia', label: 'ORTODONTIA', icon: Activity },
{ id: 'financeiro', label: 'FINANCEIRO', icon: DollarSign },
{ id: 'relatorios', label: 'RELATÓRIOS', icon: BarChart3 },
{ id: 'dentistas', label: 'DENTISTAS', icon: Stethoscope },
{ id: 'especialidades', label: 'ESPECIALIDADES', icon: Award },
{ id: 'procedimentos', label: 'PROCEDIMENTOS', icon: ClipboardList },
{ id: 'planos', label: 'PLANOS / CONVÊNIOS', icon: BookOpen },
{ id: 'notificacoes', label: 'NOTIFICAÇÕES', icon: Bell },
{ id: 'sync', label: 'SINCRONIZAÇÃO', icon: RefreshCw },
].filter(item => {
if (['admin', 'donoclinica'].includes(currentRole)) return true;
if (currentRole === 'paciente') return ['tratamentos', 'notificacoes'].includes(item.id);
if (currentRole === 'dentista') return ['dashboard', 'pacientes', 'tratamentos', 'agenda', 'ortodontia', 'notificacoes', 'clinicas'].includes(item.id);
if (currentRole === 'funcionario') return ['dashboard', 'leads', 'pacientes', 'tratamentos', 'lancar-gto', 'agenda', 'financeiro', 'relatorios', 'notificacoes', 'clinicas'].includes(item.id);
return false;
});
const handleLogout = () => {
if (window.confirm('TEM CERTEZA QUE DESEJA SAIR DO SISTEMA?')) {
HybridBackend.logout();
}
};
return (
<div className="w-64 bg-white border-r border-gray-200 h-screen flex flex-col shadow-xl md:shadow-none">
<div className="p-6 flex items-center gap-3">
<div
className="w-8 h-8 rounded-lg flex items-center justify-center text-white font-bold shadow-lg transition-colors duration-500"
style={{ backgroundColor: clinColor }}
>
<Database size={18} />
</div>
<div>
<span className="font-bold text-lg text-gray-800 block leading-tight tracking-tight">SCOREODONTO</span>
<span className="text-[10px] text-gray-400 font-bold uppercase tracking-wider">MYSQL + GOOGLE</span>
</div>
</div>
<nav className="flex-1 px-4 py-2 space-y-1 overflow-y-auto custom-scrollbar">
{menuItems.map((item) => {
const Icon = item.icon;
const isActive = activeTab === item.id;
return (
<button
key={item.id}
onClick={() => setActiveTab(item.id)}
className={`w-full flex items-center gap-3 px-4 py-3 rounded-xl text-[11px] font-bold uppercase transition-all duration-300 group ${isActive
? 'text-white'
: 'text-gray-500 hover:bg-gray-50'
}`}
style={{
backgroundColor: isActive ? clinColor : 'transparent',
boxShadow: isActive ? `0 10px 15px -5px ${clinColor}44` : 'none'
}}
>
<Icon size={18} className={`${isActive ? 'text-white' : 'text-gray-400 group-hover:text-gray-600'} transition-colors`}
style={{ color: isActive ? '#fff' : undefined }} />
{item.label}
</button>
);
})}
</nav>
<div className="p-4 border-t border-gray-100 bg-gray-50/50 space-y-4">
{/* User Profile Section */}
<div className="bg-white p-3 rounded-2xl border border-gray-100 shadow-sm">
<div className="flex items-center gap-3 mb-3">
<div className="w-10 h-10 bg-gray-100 rounded-full flex items-center justify-center text-gray-500 border border-gray-200">
<User size={20} />
</div>
<div className="min-w-0">
<p className="text-xs font-black text-gray-800 uppercase truncate leading-none mb-1">{userName}</p>
<p className="text-[9px] font-bold text-gray-400 truncate">{userEmail}</p>
</div>
</div>
<div
className="flex items-center gap-2 p-2 rounded-lg border transition-all duration-500"
style={{
backgroundColor: `${clinColor}08`,
borderColor: `${clinColor}22`
}}
>
<div className="w-2 h-2 rounded-full shadow-sm" style={{ backgroundColor: clinColor }}></div>
<span className="text-[9px] font-black uppercase truncate" style={{ color: clinColor }}>
{activeWorkspace?.nome || 'Sem Unidade'}
</span>
</div>
</div>
{currentRole === 'admin' && (
<GoogleConnectButton
ownerId="admin"
isConnected={connectedAccounts.some(a => a.owner_id === 'admin' || a.owner_id === userEmail)}
onStatusChange={fetchGoogleStatus}
/>
)}
<button
onClick={handleLogout}
className="w-full flex items-center gap-3 px-4 py-2 text-red-500 hover:bg-red-50 rounded-xl text-[10px] font-black uppercase transition-all"
>
<LogOut size={16} />
SAIR DO SISTEMA
</button>
</div>
</div>
);
};