Spaces:
Sleeping
Sleeping
| /** | |
| * Composant de navigation pour AfriDataHub | |
| * Created by BlackBenAI Team - AfriDataHub Platform | |
| */ | |
| import { useState } from 'react' | |
| import { motion, AnimatePresence } from 'framer-motion' | |
| import { Button } from '@/components/ui/button' | |
| import { | |
| BarChart3, | |
| Database, | |
| AlertTriangle, | |
| User, | |
| Menu, | |
| X, | |
| Globe, | |
| TrendingUp, | |
| Moon, | |
| Sun | |
| } from 'lucide-react' | |
| import { useTheme } from 'next-themes' | |
| const Navigation = ({ currentPage, onPageChange, user, onLogout }) => { | |
| const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false) | |
| const { theme, setTheme } = useTheme() | |
| const menuItems = [ | |
| { id: 'dashboard', label: 'Dashboard', icon: BarChart3 }, | |
| { id: 'datasets', label: 'Datasets', icon: Database }, | |
| { id: 'analytics', label: 'Analyse', icon: TrendingUp }, | |
| { id: 'alerts', label: 'Alertes', icon: AlertTriangle }, | |
| { id: 'api-docs', label: 'API', icon: Globe }, | |
| { id: 'profile', label: 'Profil', icon: User }, | |
| ] | |
| const handlePageChange = (pageId) => { | |
| onPageChange(pageId) | |
| setIsMobileMenuOpen(false) | |
| } | |
| return ( | |
| <nav className="sticky top-0 z-50 glass border-b"> | |
| <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> | |
| <div className="flex justify-between h-20"> | |
| {/* Logo et titre */} | |
| <div className="flex items-center"> | |
| <div className="flex-shrink-0 flex items-center group cursor-pointer" onClick={() => handlePageChange('dashboard')}> | |
| <div className="p-2 bg-primary rounded-xl shadow-lg group-hover:scale-110 transition-transform duration-300"> | |
| <Globe className="h-6 w-6 text-white animate-pulse" /> | |
| </div> | |
| <span className="ml-3 text-xl sm:text-2xl font-black tracking-tight text-foreground"> | |
| AfriDataHub | |
| </span> | |
| </div> | |
| </div> | |
| {/* Menu desktop */} | |
| <div className="hidden lg:flex items-center space-x-1 lg:space-x-2"> | |
| {menuItems.map((item) => { | |
| const Icon = item.icon | |
| const isActive = currentPage === item.id | |
| return ( | |
| <Button | |
| key={item.id} | |
| variant="ghost" | |
| onClick={() => handlePageChange(item.id)} | |
| className={`relative px-4 py-2 rounded-xl transition-all duration-300 group ${isActive | |
| ? 'text-primary bg-primary/10' | |
| : 'text-muted-foreground hover:text-primary hover:bg-primary/5' | |
| }`} | |
| > | |
| <Icon className={`h-4 w-4 mr-2 ${isActive ? 'scale-110' : 'group-hover:scale-110'} transition-transform`} /> | |
| <span className="font-semibold">{item.label}</span> | |
| {isActive && ( | |
| <motion.div | |
| layoutId="nav-active" | |
| className="absolute bottom-0 left-0 right-0 h-0.5 bg-primary rounded-full" | |
| /> | |
| )} | |
| </Button> | |
| ) | |
| })} | |
| {user && ( | |
| <div className="flex items-center ml-6 pl-6 border-l border-border"> | |
| <div className="flex items-center space-x-3 mr-4"> | |
| <div className="w-8 h-8 rounded-full bg-primary flex items-center justify-center text-white font-bold text-xs shadow-md"> | |
| {user.username.charAt(0).toUpperCase()} | |
| </div> | |
| <span className="text-sm font-medium text-foreground/80"> | |
| {user.username} | |
| </span> | |
| </div> | |
| <Button | |
| variant="outline" | |
| onClick={onLogout} | |
| className="rounded-xl border-border text-muted-foreground hover:text-destructive hover:border-destructive/30 hover:bg-destructive/10 transition-colors" | |
| > | |
| Déconnexion | |
| </Button> | |
| </div> | |
| )} | |
| <Button | |
| variant="ghost" | |
| size="icon" | |
| onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')} | |
| className="ml-2 rounded-xl text-foreground hover:bg-primary/10" | |
| > | |
| <Sun className="h-5 w-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" /> | |
| <Moon className="absolute h-5 w-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" /> | |
| <span className="sr-only">Toggle theme</span> | |
| </Button> | |
| </div> | |
| {/* Bouton menu mobile */} | |
| <div className="lg:hidden flex items-center"> | |
| <Button | |
| variant="ghost" | |
| onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)} | |
| className="rounded-xl text-foreground hover:bg-primary/10" | |
| > | |
| {isMobileMenuOpen ? ( | |
| <X className="h-6 w-6" /> | |
| ) : ( | |
| <Menu className="h-6 w-6" /> | |
| )} | |
| </Button> | |
| <Button | |
| variant="ghost" | |
| size="icon" | |
| onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')} | |
| className="ml-2 rounded-xl text-foreground hover:bg-primary/10" | |
| > | |
| <Sun className="h-5 w-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" /> | |
| <Moon className="absolute h-5 w-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" /> | |
| <span className="sr-only">Toggle theme</span> | |
| </Button> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Menu mobile */} | |
| <AnimatePresence> | |
| {isMobileMenuOpen && ( | |
| <motion.div | |
| initial={{ opacity: 0, height: 0 }} | |
| animate={{ opacity: 1, height: 'auto' }} | |
| exit={{ opacity: 0, height: 0 }} | |
| className="lg:hidden glass border-t overflow-hidden" | |
| > | |
| <div className="px-4 pt-4 pb-6 space-y-2"> | |
| {menuItems.map((item) => { | |
| const Icon = item.icon | |
| const isActive = currentPage === item.id | |
| return ( | |
| <Button | |
| key={item.id} | |
| variant="ghost" | |
| onClick={() => handlePageChange(item.id)} | |
| className={`w-full justify-start flex items-center space-x-3 p-4 rounded-xl transition-all ${isActive | |
| ? 'bg-primary/20 text-primary shadow-inner' | |
| : 'text-muted-foreground hover:bg-primary/10 hover:text-primary' | |
| }`} | |
| > | |
| <Icon className="h-5 w-5" /> | |
| <span className="font-bold">{item.label}</span> | |
| </Button> | |
| ) | |
| })} | |
| {user && ( | |
| <div className="pt-4 mt-4 border-t"> | |
| <Button | |
| variant="ghost" | |
| onClick={onLogout} | |
| className="w-full justify-start flex items-center space-x-3 p-4 rounded-xl text-muted-foreground hover:text-destructive hover:bg-destructive/10" | |
| > | |
| <X className="h-5 w-5" /> | |
| <span className="font-bold">Déconnexion</span> | |
| </Button> | |
| </div> | |
| )} | |
| </div> | |
| </motion.div> | |
| )} | |
| </AnimatePresence> | |
| </nav> | |
| ) | |
| } | |
| export default Navigation | |