Files
SRCmail/components/layout/mobile-header.tsx
T
Matthieu MALVACHEandMatthieu MALVACHE 0d68851b63 feat(i18n): Complete internationalization with timezone fixes and language switching
Completes full internationalization coverage for the webmail application with enhanced language support:

- Replace all remaining hardcoded English strings with translation keys
- Add timezone auto-detection to prevent hydration mismatches
- Enable instant client-side language switching without page reload
- Add 60+ new translation keys in English and French locales
- Improve language switcher component with proper state management
- Add health check endpoint for container orchestration

All user-facing text now supports English and French with no hardcoded fallbacks, providing a fully localized experience.
2026-01-08 04:29:19 +01:00

143 lines
3.3 KiB
TypeScript

"use client";
import { Menu, ArrowLeft, Plus, Search, X } from "lucide-react";
import { Button } from "@/components/ui/button";
import { useUIStore } from "@/stores/ui-store";
import { cn } from "@/lib/utils";
interface MobileHeaderProps {
title: string;
showBack?: boolean;
onBack?: () => void;
onCompose?: () => void;
onSearch?: () => void;
className?: string;
}
export function MobileHeader({
title,
showBack = false,
onBack,
onCompose,
onSearch,
className,
}: MobileHeaderProps) {
const { toggleSidebar, goBack, sidebarOpen } = useUIStore();
const handleLeftAction = () => {
if (showBack && onBack) {
onBack();
} else if (showBack) {
goBack();
} else {
toggleSidebar();
}
};
return (
<header
className={cn(
"flex items-center justify-between px-4 h-14 border-b border-border bg-background shrink-0",
"lg:hidden", // Only visible on mobile/tablet
className
)}
>
{/* Left action: Menu or Back button */}
<div className="flex items-center gap-2">
<Button
variant="ghost"
size="icon"
onClick={handleLeftAction}
className="h-10 w-10"
aria-label={showBack ? "Go back" : "Toggle menu"}
>
{showBack ? (
<ArrowLeft className="h-5 w-5" />
) : sidebarOpen ? (
<X className="h-5 w-5" />
) : (
<Menu className="h-5 w-5" />
)}
</Button>
{/* Title */}
<h1 className="font-semibold text-lg truncate">{title}</h1>
</div>
{/* Right actions */}
<div className="flex items-center gap-1">
{onSearch && (
<Button
variant="ghost"
size="icon"
onClick={onSearch}
className="h-10 w-10"
aria-label="Search"
>
<Search className="h-5 w-5" />
</Button>
)}
{onCompose && (
<Button
variant="ghost"
size="icon"
onClick={onCompose}
className="h-10 w-10 text-primary"
aria-label="Compose"
>
<Plus className="h-5 w-5" />
</Button>
)}
</div>
</header>
);
}
/**
* Viewer header for mobile - shows when viewing an email
*/
interface MobileViewerHeaderProps {
subject?: string;
onBack: () => void;
onDelete?: () => void;
onArchive?: () => void;
className?: string;
}
export function MobileViewerHeader({
subject,
onBack,
onDelete: _onDelete,
onArchive: _onArchive,
className,
}: MobileViewerHeaderProps) {
return (
<header
className={cn(
"flex items-center justify-between px-2 h-14 border-b border-border bg-background shrink-0",
"lg:hidden", // Only visible on mobile/tablet
className
)}
>
<Button
variant="ghost"
size="icon"
onClick={onBack}
className="h-10 w-10"
aria-label="Go back"
>
<ArrowLeft className="h-5 w-5" />
</Button>
<h1 className="flex-1 font-medium text-sm truncate px-2 text-center">
{subject || "(No Subject)"}
</h1>
<div className="flex items-center">
{/* Placeholder for additional actions - kept minimal */}
<div className="w-10" />
</div>
</header>
);
}