JavaScript 56.7%
TypeScript 42.6%
1"use client";23import Link from "next/link";4import { usePathname, useRouter } from "next/navigation";5import { Menu, Search, Upload, Sparkles, ChevronRight, Home } from "lucide-react";6import ThemeToggle from "../theme/ThemeToggle";7import NotificationBell from "./NotificationBell";8import Kbd, { modKey } from "../ui/Kbd";9import { useShell, toggleAiPanel } from "./ShellContext";10import { useEffect, useState } from "react";1112const routeTitles: Record<string, string> = {13 "/dashboard": "Accueil",14 "/dashboard/files": "Fichiers",15 "/dashboard/favorites": "Favoris",16 "/dashboard/recent": "Récents",17 "/dashboard/graph": "Visualisation",18 "/dashboard/shared-with-me": "Partagé avec moi",19 "/dashboard/passwords": "Mots de passe",20 "/dashboard/trash": "Corbeille",21 "/dashboard/security": "Sécurité",22 "/admin": "Administration",23 "/admin/users": "Utilisateurs",24 "/admin/shared-spaces": "Espaces partagés",25 "/admin/shares": "Liens partagés",26 "/admin/activity": "Activité",27 "/admin/snapshots": "Snapshots",28 "/admin/backup": "Backups",29 "/admin/api-keys": "Clés API",30 "/admin/settings": "Paramètres",31};3233export default function TopBar() {34 const pathname = usePathname();35 const router = useRouter();36 const { header, openCommand, setMobileNavOpen } = useShell();37 const [mod, setMod] = useState("⌘");38 useEffect(() => setMod(modKey()), []);3940 const isFiles = pathname.startsWith("/dashboard/files") || pathname.startsWith("/dashboard/folder");41 const title = header.title || routeTitles[pathname] || (pathname.startsWith("/dashboard/shared-space") ? "Espace partagé" : isFiles ? "Fichiers" : "SPB Cloud");42 const crumbs = header.crumbs;4344 return (45 <header className="h-14 lg:h-16 shrink-0 flex items-center gap-2 px-3 sm:px-4 lg:px-6 border-b border-line dark:border-white/[0.07] bg-surface/70 dark:bg-canvas/70 backdrop-blur-xl safe-top z-30">46 {/* Mobile : menu */}47 <button48 onClick={() => setMobileNavOpen(true)}49 className="lg:hidden w-10 h-10 -ml-1 flex items-center justify-center rounded-xl text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-white/10 transition-colors"50 aria-label="Ouvrir le menu"51 >52 <Menu className="w-5 h-5" />53 </button>5455 {/* Titre / fil d'Ariane */}56 <div className="flex-1 min-w-0 flex items-center">57 {crumbs ? (58 <nav className="flex items-center gap-1 text-sm overflow-x-auto scrollbar-none whitespace-nowrap" aria-label="Fil d'Ariane">59 <Link href="/dashboard/files" className="flex items-center gap-1.5 text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white transition-colors px-1.5 py-1 rounded-lg hover:bg-gray-100 dark:hover:bg-white/5" data-crumb-root>60 <Home className="w-4 h-4" />61 <span className={crumbs.length ? "hidden sm:inline" : "font-semibold text-gray-900 dark:text-white"}>Fichiers</span>62 </Link>63 {crumbs.map((c, i) => {64 const last = i === crumbs.length - 1;65 return (66 <span key={c.id} className="flex items-center gap-1">67 <ChevronRight className="w-3.5 h-3.5 text-gray-300 dark:text-gray-600 shrink-0" />68 <Link69 href={`/dashboard/folder/${c.id}`}70 data-crumb-id={c.id}71 className={`px-1.5 py-1 rounded-lg transition-colors truncate max-w-[10rem] sm:max-w-[16rem] ${last ? "font-semibold text-gray-900 dark:text-white" : "text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-white/5"}`}72 aria-current={last ? "page" : undefined}73 >74 {c.name}75 </Link>76 </span>77 );78 })}79 </nav>80 ) : (81 <h1 className="text-[15px] lg:text-base font-semibold tracking-tight text-gray-900 dark:text-white truncate">{title}</h1>82 )}83 </div>8485 {/* Recherche / palette */}86 <button87 onClick={() => openCommand("")}88 className="hidden md:flex items-center gap-2 h-10 w-64 lg:w-80 px-3 rounded-xl bg-gray-100/80 dark:bg-white/[0.06] border border-transparent hover:border-line dark:hover:border-white/10 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors text-sm text-left"89 aria-label="Rechercher ou exécuter une commande"90 >91 <Search className="w-4 h-4 shrink-0" />92 <span className="flex-1 truncate">Rechercher, naviguer, agir…</span>93 <span className="flex items-center gap-0.5"><Kbd>{mod}</Kbd><Kbd>K</Kbd></span>94 </button>95 <button96 onClick={() => openCommand("")}97 className="md:hidden w-10 h-10 flex items-center justify-center rounded-xl text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-white/10 transition-colors"98 aria-label="Rechercher"99 >100 <Search className="w-5 h-5" />101 </button>102103 {/* Actions */}104 <div className="flex items-center gap-0.5 sm:gap-1">105 <button106 onClick={() => router.push(isFiles ? `${pathname}?upload=1` : "/dashboard/files?upload=1")}107 className="hidden sm:flex items-center gap-1.5 h-10 px-3 rounded-xl text-white bg-accent-gradient text-sm font-medium shadow-[inset_0_1px_0_rgb(255_255_255/.18)] hover:brightness-110 hover:shadow-glow transition-all"108 title="Envoyer des fichiers"109 >110 <Upload className="w-4 h-4" />111 <span className="hidden lg:inline">Envoyer</span>112 </button>113 <button114 onClick={toggleAiPanel}115 className="hidden sm:flex w-10 h-10 items-center justify-center rounded-xl text-gray-500 hover:text-blue-600 hover:bg-blue-500/10 dark:text-gray-400 dark:hover:text-blue-300 transition-colors"116 title="Assistant IA"117 aria-label="Assistant IA"118 >119 <Sparkles className="w-[18px] h-[18px]" />120 </button>121 <NotificationBell />122 <ThemeToggle />123 </div>124 </header>125 );126}127