Files
Momento/memento-note/components/admin-header.tsx
sepehr 153c921960
Some checks failed
Deploy to Production / Build and Deploy (push) Failing after 1m7s
fix: comprehensive i18n — replace hardcoded French/English strings with t() calls
Replaced ~100+ hardcoded French and English text strings across 30+ components
with proper i18n t() calls. Added 57 new translation keys to all 15 locale files
(ar, de, en, es, fa, fr, hi, it, ja, ko, nl, pl, pt, ru, zh).

Key changes:
- contextual-ai-chat.tsx: 30 French strings → t() (actions, toasts, labels, placeholders)
- ai-chat.tsx: 15 French/English strings → t() (header, tabs, welcome, insights, history)
- note-inline-editor.tsx: 20 French fallbacks removed (toolbar, save status, checklist)
- lab-skeleton.tsx: French loading text → t()
- admin-header.tsx, header.tsx, editor-connections-section.tsx: French fallbacks removed
- New AI chat component, agent cards, sidebar, settings panel i18n cleanup

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 21:14:45 +02:00

137 lines
6.0 KiB
TypeScript

'use client'
import { signOut, useSession } from 'next-auth/react'
import { Shield, Search, Settings, LogOut, User, StickyNote, FlaskConical, Bot } from 'lucide-react'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { NotificationPanel } from './notification-panel'
import { useLanguage } from '@/lib/i18n'
/**
* Admin header — visuellement identique au Header principal.
* Utilise exclusivement des <a> (rechargement complet) au lieu de <Link>
* pour éviter React Error #310 (bug React #33580 / Next.js #63388).
*/
export function AdminHeader() {
const { data: session } = useSession()
const { t } = useLanguage()
const user = session?.user
const initial = user?.name
? user.name.charAt(0).toUpperCase()
: user?.email?.[0]?.toUpperCase() ?? '?'
return (
<header className="flex-none flex items-center justify-between whitespace-nowrap border-b border-solid border-slate-200 dark:border-slate-800 bg-white dark:bg-[#1e2128] px-6 py-3 z-20">
{/* ── Logo + Search ── */}
<div className="flex items-center gap-8">
<a href="/" className="flex items-center gap-3 text-slate-900 dark:text-white group no-underline">
<div className="size-8 bg-primary rounded-lg flex items-center justify-center text-primary-foreground shadow-sm group-hover:shadow-md transition-all">
<StickyNote className="w-5 h-5" />
</div>
<h2 className="text-xl font-bold leading-tight tracking-tight">MEMENTO</h2>
</a>
{/* Badge Admin */}
<span className="hidden sm:flex items-center gap-1.5 px-2.5 py-0.5 rounded-full bg-primary/10 text-primary text-xs font-semibold">
<Shield className="h-3 w-3" />
Admin
</span>
{/* Search (décoratif en mode admin) — même taille que l'entête principale */}
<label className="hidden md:flex flex-col min-w-40 w-96 !h-10">
<div className="flex w-full flex-1 items-stretch rounded-full h-full bg-slate-100 dark:bg-slate-800 border border-transparent">
<div className="text-slate-400 dark:text-slate-400 flex items-center justify-center pl-4">
<Search className="w-4 h-4" />
</div>
<input
className="form-input flex w-full min-w-0 flex-1 resize-none overflow-hidden bg-transparent border-none text-slate-900 dark:text-white placeholder:text-slate-400 dark:placeholder:text-slate-500 px-3 text-sm focus:ring-0 focus:outline-none"
placeholder={t('search.placeholder') }
type="text"
disabled
aria-label={t('search.disabledAdmin')}
/>
</div>
</label>
</div>
{/* ── Droite : nav + notifs + settings + avatar ── */}
<div className="flex flex-1 justify-end gap-2 items-center">
{/* Nav pills — toutes en <a> pour éviter la RSC race condition */}
<div className="hidden md:flex items-center gap-1 bg-slate-100 dark:bg-slate-800/60 rounded-full px-1.5 py-1">
<a
href="/agents"
className="flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-medium text-muted-foreground hover:text-foreground transition-colors"
>
<Bot className="h-3.5 w-3.5" />
<span>{t('nav.agents') || 'Agents'}</span>
</a>
<a
href="/lab"
className="flex items-center justify-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-medium text-muted-foreground hover:text-foreground transition-colors"
>
<FlaskConical className="h-3.5 w-3.5" />
<span>{t('nav.lab') || 'The Lab'}</span>
</a>
</div>
{/* Notifications */}
<NotificationPanel />
{/* Settings */}
<a
href="/settings"
className="flex items-center justify-center size-10 rounded-full hover:bg-slate-100 dark:hover:bg-slate-800 text-slate-600 dark:text-slate-300 transition-colors"
aria-label={t('settings.title')}
>
<Settings className="w-5 h-5" />
</a>
{/* Avatar + menu */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
<div
className="flex items-center justify-center bg-center bg-no-repeat bg-cover rounded-full size-10 ring-2 ring-white dark:ring-slate-700 cursor-pointer shadow-sm hover:shadow-md transition-shadow bg-primary/10 dark:bg-primary/20 text-primary dark:text-primary-foreground"
style={user?.image ? { backgroundImage: `url(${(user as any).image})` } : undefined}
>
{!user?.image && (
<span className="text-sm font-semibold">{initial}</span>
)}
</div>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56">
<div className="flex items-center justify-start gap-2 p-2">
<div className="flex flex-col space-y-1 leading-none">
{user?.name && <p className="font-medium">{user.name}</p>}
{user?.email && <p className="w-[200px] truncate text-sm text-muted-foreground">{user.email}</p>}
</div>
</div>
<DropdownMenuSeparator />
<DropdownMenuItem asChild className="cursor-pointer">
<a href="/settings/profile">
<User className="mr-2 h-4 w-4" />
<span>{t('settings.profile') }</span>
</a>
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={() => signOut({ callbackUrl: '/' })}
className="cursor-pointer text-red-600 focus:text-red-600"
>
<LogOut className="mr-2 h-4 w-4" />
<span>{t('auth.signOut') }</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</header>
)
}