WIP: Améliorations UX et corrections de bugs avant création des épiques

This commit is contained in:
2026-01-17 11:10:50 +01:00
parent 772dc77719
commit ef60dafd73
84 changed files with 11846 additions and 230 deletions

View File

@@ -77,6 +77,41 @@ export function Header({
setNotebookId(currentNotebook || null)
}, [currentNotebook, setNotebookId])
// Prevent body scroll when mobile menu is open
useEffect(() => {
if (isSidebarOpen) {
document.body.style.overflow = 'hidden'
document.body.style.position = 'fixed'
document.body.style.width = '100%'
} else {
document.body.style.overflow = ''
document.body.style.position = ''
document.body.style.width = ''
}
return () => {
document.body.style.overflow = ''
document.body.style.position = ''
document.body.style.width = ''
}
}, [isSidebarOpen])
// Close mobile menu on Esc key press
useEffect(() => {
const handleEscapeKey = (e: KeyboardEvent) => {
if (e.key === 'Escape' && isSidebarOpen) {
setIsSidebarOpen(false)
}
}
if (isSidebarOpen) {
document.addEventListener('keydown', handleEscapeKey)
}
return () => {
document.removeEventListener('keydown', handleEscapeKey)
}
}, [isSidebarOpen])
// Simple debounced search with URL update (150ms for more responsiveness)
const debouncedSearchQuery = useDebounce(searchQuery, 150)
@@ -224,6 +259,8 @@ export function Header({
? "bg-[#EFB162] text-amber-900"
: "hover:bg-gray-100 dark:hover:bg-zinc-800 text-gray-700 dark:text-gray-300"
)}
style={{ minHeight: '44px' }}
aria-pressed={active}
>
{content}
</button>
@@ -240,6 +277,8 @@ export function Header({
? "bg-[#EFB162] text-amber-900"
: "hover:bg-gray-100 dark:hover:bg-zinc-800 text-gray-700 dark:text-gray-300"
)}
style={{ minHeight: '44px' }}
aria-current={active ? 'page' : undefined}
>
{content}
</Link>
@@ -250,20 +289,36 @@ export function Header({
return (
<>
<header className="h-20 bg-background-light/90 dark:bg-background-dark/90 backdrop-blur-sm border-b border-transparent flex items-center justify-between px-6 lg:px-12 flex-shrink-0 z-30 sticky top-0">
<header className="h-20 bg-background/90 backdrop-blur-sm border-b border-transparent flex items-center justify-between px-6 lg:px-12 flex-shrink-0 z-30 sticky top-0">
{/* Mobile Menu Button */}
<Sheet open={isSidebarOpen} onOpenChange={setIsSidebarOpen}>
<SheetTrigger asChild>
<Button variant="ghost" size="icon" className="lg:hidden mr-4 text-slate-500 dark:text-slate-400">
<Button
variant="ghost"
size="icon"
className="lg:hidden mr-4 text-muted-foreground"
aria-label="Open menu"
aria-expanded={isSidebarOpen}
>
<Menu className="h-6 w-6" />
</Button>
</SheetTrigger>
<SheetContent side="left" className="w-[280px] sm:w-[320px] p-0 pt-4">
<SheetHeader className="px-4 mb-4">
<SheetHeader className="px-4 mb-4 flex items-center justify-between">
<SheetTitle className="flex items-center gap-2 text-xl font-normal">
<StickyNote className="h-6 w-6 text-amber-500" />
<StickyNote className="h-6 w-6 text-primary" />
{t('nav.workspace')}
</SheetTitle>
<Button
variant="ghost"
size="icon"
onClick={() => setIsSidebarOpen(false)}
className="text-muted-foreground hover:text-foreground"
aria-label="Close menu"
style={{ width: '44px', height: '44px' }}
>
<X className="h-5 w-5" />
</Button>
</SheetHeader>
<div className="flex flex-col gap-1 py-2">
<NavItem
@@ -280,7 +335,7 @@ export function Header({
/>
<div className="my-2 px-4 flex items-center justify-between">
<span className="text-xs font-medium text-gray-500 uppercase tracking-wider">{t('labels.title')}</span>
<span className="text-xs font-medium text-muted-foreground uppercase tracking-wider">{t('labels.title')}</span>
</div>
{labels.map(label => (
@@ -312,10 +367,10 @@ export function Header({
</Sheet>
{/* Search Bar */}
<div className="flex-1 max-w-2xl flex items-center bg-white dark:bg-slate-800/80 rounded-2xl px-4 py-3 shadow-sm border border-transparent focus-within:border-indigo-500/50 focus-within:ring-2 ring-indigo-500/10 transition-all">
<Search className="text-slate-400 dark:text-slate-500 text-xl" />
<div className="flex-1 max-w-2xl flex items-center bg-card rounded-lg px-4 py-3 shadow-sm border border-transparent focus-within:border-primary/50 focus-within:ring-2 focus-within:ring-primary/10 transition-all">
<Search className="text-muted-foreground text-xl" />
<input
className="bg-transparent border-none outline-none focus:ring-0 w-full text-sm text-slate-700 dark:text-slate-200 ml-3 placeholder-slate-400"
className="bg-transparent border-none outline-none focus:ring-0 w-full text-sm text-foreground ml-3 placeholder-muted-foreground"
placeholder={t('search.placeholder') || "Search notes, tags, or notebooks..."}
type="text"
value={searchQuery}
@@ -327,11 +382,11 @@ export function Header({
onClick={handleSemanticSearch}
disabled={!searchQuery.trim() || isSemanticSearching}
className={cn(
"flex items-center gap-1 px-2 py-1.5 rounded-md text-xs font-medium transition-colors",
"hover:bg-indigo-100 dark:hover:bg-indigo-900/30",
"flex items-center gap-1 px-2 py-1.5 rounded-md text-xs font-medium transition-colors min-h-[36px]",
"hover:bg-accent",
searchParams.get('semantic') === 'true'
? "bg-indigo-200 dark:bg-indigo-900/50 text-indigo-900 dark:text-indigo-100"
: "text-gray-500 dark:text-gray-400 hover:text-indigo-700 dark:hover:text-indigo-300",
? "bg-primary/20 text-primary"
: "text-muted-foreground hover:text-primary",
"disabled:opacity-50 disabled:cursor-not-allowed"
)}
title={t('search.semanticTooltip')}
@@ -342,7 +397,7 @@ export function Header({
{searchQuery && (
<button
onClick={() => handleSearch('')}
className="ml-2 text-slate-400 hover:text-slate-600 dark:hover:text-slate-200"
className="ml-2 text-muted-foreground hover:text-foreground"
>
<X className="h-4 w-4" />
</button>
@@ -358,14 +413,14 @@ export function Header({
/>
{/* Grid View Button */}
<button className="p-2.5 text-slate-500 hover:bg-white hover:shadow-sm dark:text-slate-400 dark:hover:bg-slate-700 rounded-xl transition-all duration-200">
<button className="p-2.5 text-muted-foreground hover:bg-accent rounded-lg transition-colors duration-200 min-h-[44px] min-w-[44px]">
<Grid3x3 className="text-xl" />
</button>
{/* Theme Toggle */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button className="p-2.5 text-slate-500 hover:bg-white hover:shadow-sm dark:text-slate-400 dark:hover:bg-slate-700 rounded-xl transition-all duration-200">
<button className="p-2.5 text-muted-foreground hover:bg-accent rounded-lg transition-colors duration-200 min-h-[44px] min-w-[44px]">
{theme === 'light' ? <Sun className="text-xl" /> : <Moon className="text-xl" />}
</button>
</DropdownMenuTrigger>
@@ -384,12 +439,12 @@ export function Header({
{/* Active Filters Bar */}
{hasActiveFilters && (
<div className="px-6 lg:px-12 pb-3 flex items-center gap-2 overflow-x-auto border-t border-gray-100 dark:border-zinc-800 pt-2 bg-white/50 dark:bg-zinc-900/50 backdrop-blur-sm animate-in slide-in-from-top-2">
<div className="px-6 lg:px-12 pb-3 flex items-center gap-2 overflow-x-auto border-t border-border pt-2 bg-background/50 backdrop-blur-sm animate-in slide-in-from-top-2">
{currentColor && (
<Badge variant="secondary" className="flex items-center gap-1 h-7 whitespace-nowrap pl-2 pr-1">
<div className={cn("w-3 h-3 rounded-full border border-black/10", `bg-${currentColor}-500`)} />
{t('notes.color')}: {currentColor}
<button onClick={removeColorFilter} className="ml-1 hover:bg-black/10 dark:hover:bg-white/10 rounded-full p-0.5">
<button onClick={removeColorFilter} className="ml-1 hover:bg-black/10 dark:hover:bg-white/10 rounded-full p-0.5 min-h-[24px] min-w-[24px]">
<X className="h-3 w-3" />
</button>
</Badge>
@@ -409,7 +464,7 @@ export function Header({
variant="ghost"
size="sm"
onClick={clearAllFilters}
className="h-7 text-xs text-indigo-600 hover:text-indigo-700 hover:bg-indigo-50 dark:text-indigo-400 dark:hover:bg-indigo-900/20 whitespace-nowrap ml-auto"
className="h-7 text-xs text-primary hover:text-primary hover:bg-accent whitespace-nowrap ml-auto"
>
{t('labels.clearAll')}
</Button>