first commit
This commit is contained in:
commit
c9fb67e751
423 files changed
+53287
No files matched your search
@@ -0,0 +1,319 @@
|
||||
import React, { useState, Fragment, useEffect } from "react";
|
||||
import { Outlet, Link, useLocation } from "react-router-dom";
|
||||
import { useAuth } from "../../contexts/AuthContext";
|
||||
import {
|
||||
BellIcon,
|
||||
ChevronDownIcon,
|
||||
MoonIcon,
|
||||
SunIcon,
|
||||
HomeIcon,
|
||||
ScaleIcon,
|
||||
UserGroupIcon,
|
||||
UserIcon,
|
||||
ClipboardDocumentCheckIcon,
|
||||
DocumentTextIcon,
|
||||
DocumentCheckIcon,
|
||||
ArrowUpTrayIcon,
|
||||
ShieldCheckIcon,
|
||||
CurrencyDollarIcon,
|
||||
CalculatorIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { Menu, Transition } from "@headlessui/react";
|
||||
import api from "../../services/api";
|
||||
import { useTheme } from "../../contexts/ThemeContext";
|
||||
|
||||
// Importando o ícone para cálculos judiciais
|
||||
import CalculateIcon from '@mui/icons-material/Calculate';
|
||||
// Importar ícones específicos para subcategorias de cálculos
|
||||
import MoneyIcon from '@mui/icons-material/Money';
|
||||
import ChildCareIcon from '@mui/icons-material/ChildCare';
|
||||
|
||||
export const MainLayout: React.FC = () => {
|
||||
const [isSidebarOpen, setSidebarOpen] = useState(true);
|
||||
const location = useLocation();
|
||||
const { user, signOut } = useAuth();
|
||||
const [userProfile, setUserProfile] = useState<any>(null);
|
||||
const { isDarkMode, toggleDarkMode } = useTheme();
|
||||
|
||||
// Adicionar efeito para buscar perfil do usuário
|
||||
useEffect(() => {
|
||||
const getUserProfile = async () => {
|
||||
try {
|
||||
const response = await api.get("/auth/me"); // Removido o /api pois já está no baseURL
|
||||
console.log("Response data:", response.data); // Debug log
|
||||
setUserProfile(response.data);
|
||||
} catch (error) {
|
||||
console.error("Erro ao buscar perfil:", error);
|
||||
}
|
||||
};
|
||||
getUserProfile();
|
||||
}, []);
|
||||
|
||||
// Console log para debug
|
||||
useEffect(() => {
|
||||
console.log("Perfil do usuário:", userProfile);
|
||||
}, [userProfile]);
|
||||
|
||||
const menuItems = [
|
||||
{ path: "/dashboard", label: "Dashboard", icon: HomeIcon },
|
||||
{ path: "/processos", label: "Processos", icon: ScaleIcon },
|
||||
{ path: "/processos-administrativos", label: "Proc.Adm", icon: ScaleIcon },
|
||||
{
|
||||
path: null,
|
||||
label: "Provas",
|
||||
icon: DocumentCheckIcon,
|
||||
subitems: [
|
||||
{
|
||||
path: '/provas/upload',
|
||||
label: 'Upload',
|
||||
roles: [],
|
||||
icon: ArrowUpTrayIcon
|
||||
},
|
||||
{
|
||||
path: '/provas/admin',
|
||||
label: 'Admin',
|
||||
roles: ['ADVOGADO_ADMIN'],
|
||||
icon: ShieldCheckIcon
|
||||
}
|
||||
]
|
||||
},
|
||||
{ path: "/clientes", label: "Clientes", icon: UserGroupIcon },
|
||||
{ path: "/atendimentos", label: "Atendimentos", icon: UserIcon },
|
||||
{ path: "/tarefas", label: "Tarefas", icon: ClipboardDocumentCheckIcon },
|
||||
{ path: "/documentos", label: "Documentos", icon: DocumentTextIcon },
|
||||
{
|
||||
path: null,
|
||||
label: "Cálculos",
|
||||
icon: CalculateIcon,
|
||||
roles: ['ADVOGADO_ADMIN', 'ADVOGADO', 'ASSISTENTE'],
|
||||
subitems: [
|
||||
{
|
||||
path: '/calculos',
|
||||
label: 'Dashboard',
|
||||
roles: ['ADVOGADO_ADMIN', 'ADVOGADO', 'ASSISTENTE'],
|
||||
icon: HomeIcon
|
||||
},
|
||||
{
|
||||
path: '/calculos/pensao-alimenticia',
|
||||
label: 'Pensão Alimentícia',
|
||||
roles: ['ADVOGADO_ADMIN', 'ADVOGADO', 'ASSISTENTE'],
|
||||
icon: ChildCareIcon
|
||||
},
|
||||
{
|
||||
path: '/calculos/trabalhistas',
|
||||
label: 'Trabalhistas',
|
||||
roles: ['ADVOGADO_ADMIN', 'ADVOGADO', 'ASSISTENTE'],
|
||||
icon: MoneyIcon
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// Função para verificar se o caminho atual corresponde ao item do menu
|
||||
const isCurrentPath = (path: string) => {
|
||||
return location.pathname.startsWith(path);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-screen bg-gray-100 dark:bg-gray-900">
|
||||
{/* Sidebar com barra de rolagem */}
|
||||
<div className="w-20 flex flex-col bg-gradient-to-b from-blue-900 via-blue-800 to-blue-900 dark:from-blue-950 dark:via-blue-900 dark:to-blue-950 text-white">
|
||||
{/* Logo - fixo no topo */}
|
||||
<div className="h-16 flex-shrink-0 flex items-center justify-center border-b border-white/10">
|
||||
<span className="text-xl font-bold">A</span>
|
||||
</div>
|
||||
|
||||
{/* Menu de navegação vertical com scroll */}
|
||||
<nav className="flex-1 overflow-y-auto">
|
||||
<div className="py-4">
|
||||
{menuItems.map((item) => (
|
||||
<div key={item.path || item.label} className="px-2 mb-2">
|
||||
{!item.subitems ? (
|
||||
<Link
|
||||
to={item.path}
|
||||
className={`flex flex-col items-center justify-center p-3 rounded-lg transition-colors ${
|
||||
isCurrentPath(item.path)
|
||||
? "bg-white/20"
|
||||
: "hover:bg-white/10"
|
||||
}`}
|
||||
title={item.label}
|
||||
>
|
||||
<item.icon className="h-6 w-6 text-white opacity-80" />
|
||||
<span className="text-xs mt-1 text-white/80">{item.label}</span>
|
||||
</Link>
|
||||
) : (
|
||||
<div className="relative group">
|
||||
<div
|
||||
className="flex flex-col items-center justify-center p-3 rounded-lg transition-colors hover:bg-white/10 cursor-pointer"
|
||||
title={item.label}
|
||||
>
|
||||
<item.icon className="h-6 w-6 text-white opacity-80" />
|
||||
<span className="text-xs mt-1 text-white/80">{item.label}</span>
|
||||
</div>
|
||||
|
||||
{/* Submenu reposicionado */}
|
||||
<div
|
||||
className="fixed z-50"
|
||||
style={{
|
||||
left: '5rem', // w-20 = 80px
|
||||
top: '50%',
|
||||
transform: 'translateY(-50%)'
|
||||
}}
|
||||
>
|
||||
<div className="opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200">
|
||||
<div className="bg-blue-800/95 backdrop-blur-sm rounded-lg py-2 px-1 shadow-xl w-[100px] ml-2">
|
||||
{item.subitems.filter(subitem =>
|
||||
!subitem.roles.length ||
|
||||
(userProfile?.perfil && subitem.roles.includes(userProfile.perfil))
|
||||
).map((subitem) => (
|
||||
<Link
|
||||
key={subitem.path}
|
||||
to={subitem.path}
|
||||
className="block px-3 py-2 hover:bg-white/10 rounded-lg transition-colors"
|
||||
>
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<subitem.icon className="h-6 w-6 text-white opacity-80" />
|
||||
<span className="text-xs mt-1 text-white/80">{subitem.label}</span>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="flex-1 flex flex-col">
|
||||
{/* Header */}
|
||||
<header className="h-16 bg-white dark:bg-gray-800 shadow-sm">
|
||||
<div className="h-full px-4 flex items-center justify-between">
|
||||
<h1 className="text-xl font-semibold text-gray-800 dark:text-gray-200">
|
||||
{menuItems.find((item) => item.path === location.pathname)
|
||||
?.label || "Dashboard"}
|
||||
</h1>
|
||||
<div className="flex items-center space-x-4">
|
||||
{/* Adicionar botão de toggle do tema antes do menu do usuário */}
|
||||
<button
|
||||
onClick={toggleDarkMode}
|
||||
className="p-2 rounded-lg text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-gray-400"
|
||||
title={isDarkMode ? "Modo claro" : "Modo escuro"}
|
||||
>
|
||||
{isDarkMode ? (
|
||||
<SunIcon className="h-5 w-5" />
|
||||
) : (
|
||||
<MoonIcon className="h-5 w-5" />
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* Notificações */}
|
||||
<button className="text-gray-600 hover:text-gray-800 relative">
|
||||
<BellIcon className="h-6 w-6" />
|
||||
<span className="absolute -top-1 -right-1 bg-red-500 text-white text-xs rounded-full w-4 h-4 flex items-center justify-center">
|
||||
3
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{/* Menu do usuário */}
|
||||
<Menu as="div" className="relative">
|
||||
<Menu.Button className="flex items-center space-x-3 hover:bg-gray-50 dark:hover:bg-gray-700 rounded-lg p-2 transition-colors">
|
||||
<img
|
||||
src={`https://ui-avatars.com/api/?name=${user?.nome || "User"}&background=0D8ABC&color=fff`}
|
||||
alt={user?.nome}
|
||||
className="w-8 h-8 rounded-full"
|
||||
/>
|
||||
<div className="text-left hidden sm:block">
|
||||
<div className="text-sm font-medium text-gray-700 dark:text-gray-200">
|
||||
{user?.nome}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400">{user?.cargo}</div>
|
||||
</div>
|
||||
<ChevronDownIcon className="w-4 h-4 text-gray-500 dark:text-gray-400" />
|
||||
</Menu.Button>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className="absolute right-0 mt-2 w-48 rounded-md bg-white dark:bg-gray-800 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
|
||||
<div className="py-1">
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<Link
|
||||
to="/perfil"
|
||||
className={`${
|
||||
active ? "bg-gray-100 dark:bg-gray-700" : ""
|
||||
} block px-4 py-2 text-sm text-gray-700 dark:text-gray-200`}
|
||||
>
|
||||
Meu Perfil
|
||||
</Link>
|
||||
)}
|
||||
</Menu.Item>
|
||||
|
||||
{/* Novo item - visível apenas para ADMIN */}
|
||||
{userProfile?.perfil === "ADVOGADO_ADMIN" && (
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<Link
|
||||
to="/usuarios"
|
||||
className={`${
|
||||
active ? "bg-gray-100 dark:bg-gray-700" : ""
|
||||
} block px-4 py-2 text-sm text-gray-700 dark:text-gray-200`}
|
||||
>
|
||||
Gerenciar Usuários
|
||||
</Link>
|
||||
)}
|
||||
</Menu.Item>
|
||||
)}
|
||||
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<a
|
||||
href="#configuracoes"
|
||||
className={`${
|
||||
active ? "bg-gray-100 dark:bg-gray-700" : ""
|
||||
} block px-4 py-2 text-sm text-gray-700 dark:text-gray-200`}
|
||||
>
|
||||
Configurações
|
||||
</a>
|
||||
)}
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<button
|
||||
onClick={signOut}
|
||||
className={`${
|
||||
active ? "bg-gray-100 dark:bg-gray-700" : ""
|
||||
} block w-full text-left px-4 py-2 text-sm text-red-600 dark:text-red-400`}
|
||||
>
|
||||
Sair
|
||||
</button>
|
||||
)}
|
||||
</Menu.Item>
|
||||
</div>
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Content */}
|
||||
<main className="flex-1 overflow-auto p-6 bg-gray-100 dark:bg-gray-900">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in new issue
Block a user