edumanagerpro2/manager/components/Sidebar.tsx

174 lines
7.0 KiB
TypeScript

import React, { useState } from 'react';
import {
LayoutDashboard,
Users,
BookOpen,
CircleDollarSign,
Settings,
FileSignature,
Award,
Camera,
ListChecks,
ChevronLeft,
ChevronRight,
Menu,
X,
GraduationCap,
Shield,
FileText,
Cloud,
CloudOff,
Library,
Briefcase,
LogOut,
MessageSquare,
ClipboardList
} from 'lucide-react';
import { isSupabaseConfigured } from '../services/supabase';
import { View, User } from '../types';
interface SidebarProps {
currentView: View;
setView: (view: View) => void;
user: User | null;
logo?: string;
onLogout: () => void;
}
const Sidebar: React.FC<SidebarProps> = ({ currentView, setView, user, logo, onLogout }) => {
const [isCollapsed, setIsCollapsed] = useState(false);
const [isMobileOpen, setIsMobileOpen] = useState(false);
const items = [
{ id: View.Dashboard, icon: LayoutDashboard, label: 'Dashboard' },
{ id: View.Courses, icon: GraduationCap, label: 'Cursos' },
{ id: View.Students, icon: Users, label: 'Alunos' },
{ id: View.Classes, icon: BookOpen, label: 'Turmas' },
{ id: View.Exams, icon: ClipboardList, label: 'Avaliações' },
{ id: View.ReportCard, icon: FileText, label: 'Boletim Escolar' },
{ id: View.Finance, icon: CircleDollarSign, label: 'Financeiro' },
{ id: View.Contracts, icon: FileSignature, label: 'Contratos' },
{ id: View.Certificates, icon: Award, label: 'Certificados' },
{ id: View.Attendance, icon: Camera, label: 'Frequência' },
{ id: View.AttendanceQuery, icon: ListChecks, label: 'Registro de Frequência' },
{ id: View.Handouts, icon: Library, label: 'Apostilas' },
{ id: View.Employees, icon: Briefcase, label: 'Funcionários' },
{ id: View.Users, icon: Shield, label: 'Usuários' },
{ id: View.Messages, icon: MessageSquare, label: 'Mensagens' },
{ id: View.Settings, icon: Settings, label: 'Configurações' },
];
const toggleMobile = () => setIsMobileOpen(!isMobileOpen);
const supabaseConfigured = isSupabaseConfigured();
return (
<>
{/* Mobile Toggle */}
<div className="md:hidden fixed top-0 left-0 right-0 bg-white border-b border-slate-200 p-4 flex justify-between items-center z-40">
<h1 className="text-xl font-bold text-indigo-600 flex items-center gap-2">
{logo ? <img src={logo} alt="Logo" className="h-8 w-auto object-contain" /> : <BookOpen size={24} />}
<span>EduManager</span>
</h1>
<button onClick={toggleMobile} className="p-2 text-slate-600">
{isMobileOpen ? <X size={24} /> : <Menu size={24} />}
</button>
</div>
{/* Sidebar Overlay for Mobile */}
{isMobileOpen && (
<div
className="md:hidden fixed inset-0 bg-slate-900/50 backdrop-blur-sm z-40"
onClick={() => setIsMobileOpen(false)}
/>
)}
{/* Sidebar Container */}
<aside className={`
fixed md:static inset-y-0 left-0 z-50 bg-white border-r border-slate-200 flex flex-col transition-all duration-300
${isMobileOpen ? 'translate-x-0 w-64' : '-translate-x-full md:translate-x-0'}
${isCollapsed ? 'md:w-20' : 'md:w-64'}
`}>
<div className={`p-6 border-b border-slate-200 flex items-center ${isCollapsed ? 'justify-center' : 'justify-between'}`}>
{(!isCollapsed || isMobileOpen) && (
<h1 className="text-xl font-bold text-indigo-600 flex items-center gap-2 overflow-hidden whitespace-nowrap">
{logo ? <img src={logo} alt="Logo" className="h-8 w-auto object-contain flex-shrink-0" /> : <BookOpen size={24} className="flex-shrink-0" />}
<span>EduManager</span>
</h1>
)}
{isCollapsed && !isMobileOpen && (logo ? <img src={logo} alt="Logo" className="h-8 w-auto object-contain" /> : <BookOpen size={24} className="text-indigo-600" />)}
<button
onClick={() => setIsCollapsed(!isCollapsed)}
className="hidden md:flex p-1.5 rounded-lg hover:bg-slate-100 text-slate-400 ml-2"
>
{isCollapsed ? <ChevronRight size={18} /> : <ChevronLeft size={18} />}
</button>
</div>
<nav className="flex-1 p-4 space-y-2 overflow-y-auto">
{items.map((item) => (
<button
key={item.id}
onClick={() => {
setView(item.id);
setIsMobileOpen(false);
}}
title={isCollapsed ? item.label : ''}
className={`w-full flex items-center gap-3 px-4 py-3 rounded-xl text-sm font-semibold transition-all duration-200 ${
currentView === item.id
? 'bg-indigo-600 text-white shadow-lg shadow-indigo-100'
: 'text-slate-600 hover:bg-slate-50 hover:text-slate-900'
} ${isCollapsed && !isMobileOpen ? 'justify-center px-0' : ''}`}
>
<item.icon size={22} className="flex-shrink-0" />
{(!isCollapsed || isMobileOpen) && <span>{item.label}</span>}
</button>
))}
</nav>
<div className="p-4 border-t border-slate-100 space-y-3">
<div className={`flex items-center gap-3 ${isCollapsed ? 'justify-center' : 'px-4 py-2'}`}>
{user?.photoURL ? (
<img
src={user.photoURL}
alt={user.displayName || user.name}
className="w-8 h-8 rounded-full object-cover border border-slate-200"
referrerPolicy="no-referrer"
/>
) : (
<div className="w-8 h-8 rounded-full bg-indigo-100 flex items-center justify-center text-indigo-700 font-bold text-xs">
{user?.name?.substring(0, 2).toUpperCase() || 'AD'}
</div>
)}
{!isCollapsed && (
<div className="overflow-hidden flex-1">
<p className="text-xs font-bold text-slate-900 truncate">{user?.displayName || user?.name || 'Administrador'}</p>
<p className="text-[10px] text-slate-500 truncate uppercase tracking-tighter">{user?.role === 'admin' ? 'Administrador' : 'Usuário'}</p>
</div>
)}
{!isCollapsed && (
<button
onClick={onLogout}
className="p-1.5 text-slate-400 hover:text-red-500 rounded-lg transition-all"
title="Sair"
>
<LogOut size={16} />
</button>
)}
</div>
<div className={`flex items-center gap-2 px-4 py-2 rounded-lg ${supabaseConfigured ? 'bg-emerald-50 text-emerald-600' : 'bg-slate-50 text-slate-400'} ${isCollapsed ? 'justify-center px-0' : ''}`}>
{supabaseConfigured ? <Cloud size={16} /> : <CloudOff size={16} />}
{!isCollapsed && (
<span className="text-[10px] font-black uppercase tracking-widest">
{supabaseConfigured ? 'Nuvem Ativa' : 'Nuvem Inativa'}
</span>
)}
</div>
</div>
</aside>
</>
);
};
export default Sidebar;