Implemented comprehensive internationalization (i18n) for both frontend and backend: Frontend: - Added react-i18next configuration with Spanish (default) and English - Created translation files (locales/es.json, locales/en.json) - Refactored core components to use i18n: LoginPage, DashboardHeader, DataUploader - Created LanguageSelector component with toggle between ES/EN - Updated API client to send Accept-Language header Backend: - Created i18n module with translation dictionary for error messages - Updated security.py to return localized authentication errors - Updated api/analysis.py to return localized validation errors - Implemented language detection from Accept-Language header Spanish remains the default language ensuring backward compatibility. Users can switch between languages using the language selector in the dashboard header. https://claude.ai/code/session_1N9VX
101 lines
3.6 KiB
TypeScript
101 lines
3.6 KiB
TypeScript
import { motion } from 'framer-motion';
|
|
import { LayoutDashboard, Layers, Bot, Map, ShieldCheck, Info, Scale } from 'lucide-react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { LanguageSelector } from './LanguageSelector';
|
|
|
|
export type TabId = 'executive' | 'dimensions' | 'readiness' | 'roadmap' | 'law10';
|
|
|
|
export interface TabConfig {
|
|
id: TabId;
|
|
label: string;
|
|
icon: React.ElementType;
|
|
}
|
|
|
|
interface DashboardHeaderProps {
|
|
title?: string;
|
|
activeTab: TabId;
|
|
onTabChange: (id: TabId) => void;
|
|
onMetodologiaClick?: () => void;
|
|
}
|
|
|
|
export function DashboardHeader({
|
|
title = 'CLIENTE DEMO - Beyond CX Analytics',
|
|
activeTab,
|
|
onTabChange,
|
|
onMetodologiaClick
|
|
}: DashboardHeaderProps) {
|
|
const { t } = useTranslation();
|
|
|
|
const TABS: TabConfig[] = [
|
|
{ id: 'executive', label: t('tabs.executive'), icon: LayoutDashboard },
|
|
{ id: 'dimensions', label: t('tabs.dimensions'), icon: Layers },
|
|
{ id: 'readiness', label: t('tabs.agenticReadiness'), icon: Bot },
|
|
{ id: 'roadmap', label: t('tabs.roadmap'), icon: Map },
|
|
{ id: 'law10', label: t('tabs.law10'), icon: Scale },
|
|
];
|
|
return (
|
|
<header className="sticky top-0 z-50 bg-white border-b border-slate-200 shadow-sm">
|
|
{/* Top row: Title and Badges */}
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 py-3 sm:py-4">
|
|
<div className="flex items-center justify-between gap-2">
|
|
<h1 className="text-base sm:text-xl font-bold text-slate-800 truncate">{title}</h1>
|
|
<div className="flex items-center gap-2">
|
|
<LanguageSelector />
|
|
{onMetodologiaClick && (
|
|
<button
|
|
onClick={onMetodologiaClick}
|
|
className="inline-flex items-center gap-1 sm:gap-1.5 px-2 sm:px-3 py-1 sm:py-1.5 bg-green-100 text-green-800 rounded-full text-[10px] sm:text-xs font-medium hover:bg-green-200 transition-colors cursor-pointer flex-shrink-0"
|
|
>
|
|
<ShieldCheck className="w-3 h-3 sm:w-3.5 sm:h-3.5" />
|
|
<span className="hidden md:inline">{t('methodology.appliedBadge')}</span>
|
|
<span className="md:hidden">{t('methodology.appliedBadgeShort')}</span>
|
|
<Info className="w-2.5 h-2.5 sm:w-3 sm:h-3 opacity-60" />
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Tab Navigation */}
|
|
<nav className="max-w-7xl mx-auto px-2 sm:px-6 overflow-x-auto">
|
|
<div className="flex space-x-1">
|
|
{TABS.map((tab) => {
|
|
const Icon = tab.icon;
|
|
const isActive = activeTab === tab.id;
|
|
|
|
return (
|
|
<button
|
|
key={tab.id}
|
|
onClick={() => onTabChange(tab.id)}
|
|
className={`
|
|
relative flex items-center gap-2 px-4 py-3 text-sm font-medium
|
|
transition-colors duration-200
|
|
${isActive
|
|
? 'text-[#6D84E3]'
|
|
: 'text-slate-500 hover:text-slate-700'
|
|
}
|
|
`}
|
|
>
|
|
<Icon className="w-4 h-4" />
|
|
<span className="hidden sm:inline">{tab.label}</span>
|
|
|
|
{/* Active indicator */}
|
|
{isActive && (
|
|
<motion.div
|
|
layoutId="activeTab"
|
|
className="absolute bottom-0 left-0 right-0 h-0.5 bg-[#6D84E3]"
|
|
initial={false}
|
|
transition={{ type: 'spring', stiffness: 500, damping: 30 }}
|
|
/>
|
|
)}
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
</nav>
|
|
</header>
|
|
);
|
|
}
|
|
|
|
export default DashboardHeader;
|