budget-view-finance/src/components/layout/ModernLayout.tsx
gpt-engineer-app[bot] 6f6292f857 Fix: User profile dropdown and icon display
The user profile dropdown was not working correctly, and the icon was cut off. This commit fixes the dropdown functionality and ensures the icon is fully visible.
2025-06-23 01:26:54 +00:00

338 lines
10 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { useIsMobile } from '@/hooks/use-mobile';
import { ModernSidebar, SidebarBody } from '@/components/ui/modern-sidebar';
import { VerticalLimelightNav } from '@/components/ui/limelight-nav';
import OnboardingTour from '@/components/onboarding/OnboardingTour';
import HelpIcon from '@/components/help/HelpIcon';
import { useOnboardingTour } from '@/hooks/useOnboardingTour';
import {
Home,
Receipt,
CreditCard,
ListFilter,
Target,
Calendar,
Crown,
MessageSquareText,
Users,
Settings,
LogOut,
User
} from 'lucide-react';
import { motion } from 'framer-motion';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Button } from '@/components/ui/button';
import { supabase } from '@/integrations/supabase/client';
import { useToast } from '@/hooks/use-toast';
import { useNavigate } from 'react-router-dom';
interface ModernLayoutProps {
children: React.ReactNode;
}
const Logo = () => {
return (
<Link
to="/"
className="relative z-20 flex items-center space-x-2 py-1 text-sm font-normal text-black"
>
<img
src="/lovable-uploads/7149adf3-440a-491e-83c2-d964a3348cc9.png"
alt="Finance Home Logo"
className="h-8 w-8 shrink-0"
/>
<motion.span
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="font-medium whitespace-pre text-blue-700 dark:text-white"
>
Finance Home
</motion.span>
</Link>
);
};
const LogoIcon = () => {
return (
<Link
to="/"
className="relative z-20 flex items-center space-x-2 py-1 text-sm font-normal text-black"
>
<img
src="/lovable-uploads/7149adf3-440a-491e-83c2-d964a3348cc9.png"
alt="Finance Home Logo"
className="h-8 w-8 shrink-0"
/>
</Link>
);
};
export default function ModernLayout({ children }: ModernLayoutProps) {
const [open, setOpen] = useState(false);
const location = useLocation();
const isMobile = useIsMobile();
const { toast } = useToast();
const navigate = useNavigate();
const [userEmail, setUserEmail] = useState<string>('');
const [dropdownOpen, setDropdownOpen] = useState(false);
const {
isOpen: tourOpen,
currentStep,
nextStep,
skipTour,
closeTour
} = useOnboardingTour();
useEffect(() => {
// Buscar dados do usuário logado
const getUserData = async () => {
const { data: { user } } = await supabase.auth.getUser();
if (user?.email) {
setUserEmail(user.email);
}
};
getUserData();
}, []);
const handleLogout = async () => {
try {
const { error } = await supabase.auth.signOut();
if (error) throw error;
// Limpar localStorage
localStorage.removeItem('userEmail');
toast({
title: "Logout realizado",
description: "Você foi desconectado com sucesso"
});
navigate('/auth');
} catch (error) {
console.error('Erro no logout:', error);
toast({
title: "Erro no logout",
description: "Ocorreu um erro ao sair da conta",
variant: "destructive"
});
}
};
const mainLinks = [
{
id: "dashboard",
label: "Dashboard",
href: "/",
icon: <Home />,
},
{
id: "transacoes",
label: "Transações",
href: "/transacoes",
icon: <Receipt />,
},
{
id: "cartoes",
label: "Cartões de Crédito",
href: "/cartoes",
icon: <CreditCard />,
},
{
id: "categorias",
label: "Categorias",
href: "/categorias",
icon: <ListFilter />,
},
{
id: "metas",
label: "Metas",
href: "/metas",
icon: <Target />,
},
{
id: "calendario",
label: "Calendário",
href: "/calendario",
icon: <Calendar />,
},
{
id: "assinatura",
label: "Assinatura",
href: "/assinatura",
icon: <Crown />,
}
];
const whatsappLinks = [
{
id: "whatsapp",
label: "Conectar WhatsApp",
href: "/whatsapp",
icon: <MessageSquareText data-tour="whatsapp-menu" />,
},
{
id: "grupos",
label: "Grupos",
href: "/grupos-whatsapp",
icon: <Users data-tour="grupos-menu" />,
}
];
const configLinks = [
{
id: "configuracoes",
label: "Configurações",
href: "/configuracoes",
icon: <Settings />,
}
];
// Determine active index based on current route
const getActiveIndex = () => {
const allLinks = [...mainLinks, ...whatsappLinks, ...configLinks];
const activeIndex = allLinks.findIndex(link => link.href === location.pathname);
return activeIndex >= 0 ? activeIndex : 0;
};
return (
<div className="min-h-screen bg-background flex w-full">
<ModernSidebar open={open} setOpen={setOpen}>
<SidebarBody className="justify-start gap-4 h-full">
<div className="flex flex-1 flex-col overflow-x-hidden overflow-y-auto h-full">
{open ? <Logo /> : <LogoIcon />}
<div className="mt-8 flex flex-col gap-6 flex-1 pb-4">
{/* Main Navigation */}
<div>
<VerticalLimelightNav
items={mainLinks}
defaultActiveIndex={getActiveIndex() < mainLinks.length ? getActiveIndex() : 0}
showLabels={open}
className="space-y-1"
iconContainerClassName={!open ? "justify-center" : ""}
/>
</div>
{/* WhatsApp Section */}
<div className="flex-1">
{open && (
<motion.h2
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="mb-3 px-3 text-xs font-semibold tracking-tight text-gray-500 uppercase"
>
WhatsApp
</motion.h2>
)}
<VerticalLimelightNav
items={whatsappLinks}
defaultActiveIndex={getActiveIndex() >= mainLinks.length && getActiveIndex() < mainLinks.length + whatsappLinks.length ? getActiveIndex() - mainLinks.length : -1}
showLabels={open}
className="space-y-1"
iconContainerClassName={!open ? "justify-center" : ""}
/>
</div>
{/* Config Section */}
<div className="mt-auto">
{open && (
<motion.h2
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="mb-3 px-3 text-xs font-semibold tracking-tight text-gray-500 uppercase"
>
Configurações
</motion.h2>
)}
<VerticalLimelightNav
items={configLinks}
defaultActiveIndex={getActiveIndex() >= mainLinks.length + whatsappLinks.length ? getActiveIndex() - mainLinks.length - whatsappLinks.length : -1}
showLabels={open}
className="space-y-1"
iconContainerClassName={!open ? "justify-center" : ""}
/>
{/* User Profile */}
<div className="mt-4 px-3">
<DropdownMenu open={dropdownOpen} onOpenChange={setDropdownOpen}>
<DropdownMenuTrigger asChild>
<Button
className={`w-full rounded-full py-0 ps-0 h-10 ${open ? 'justify-start' : 'justify-center px-0'}`}
variant="outline"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setDropdownOpen(!dropdownOpen);
}}
>
<div className={`${open ? 'me-2' : ''} flex aspect-square h-full p-1.5`}>
<div className="h-full w-full rounded-full bg-blue-600 flex items-center justify-center">
<User className="h-4 w-4 text-white" />
</div>
</div>
{open && (
<span className="text-sm truncate">
{userEmail || 'Usuário'}
</span>
)}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align={open ? "end" : "center"}
side={open ? "top" : "right"}
className="w-56 z-50"
>
<DropdownMenuItem
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
setDropdownOpen(false);
handleLogout();
}}
className="flex items-center gap-2 text-red-600 hover:text-red-700 hover:bg-red-50 cursor-pointer"
>
<LogOut className="h-4 w-4" />
Sair
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
</div>
</div>
</SidebarBody>
</ModernSidebar>
{/* Main content */}
<div className="flex-1 flex flex-col overflow-hidden">
<main className="flex-1 overflow-y-auto">
<div className="container mx-auto px-4 py-6">
{children}
</div>
</main>
</div>
{/* Help Icon */}
<HelpIcon />
{/* Tour de Onboarding */}
<OnboardingTour
isOpen={tourOpen}
currentStep={currentStep}
onNext={nextStep}
onSkip={skipTour}
onClose={closeTour}
/>
</div>
);
}