Files
gemini-nexus/src/layouts/MainLayout.tsx
T
Gemini Nexus Bot 7d2977b665 feat(ui): replace sidebar with compact top header navigation
- Rewrite MainLayout from vertical sidebar to horizontal header bar
- Brand left, nav tabs center, status indicator right
- Simplify StatusBar to single inline layout (remove collapsed variant)
- Widen content area from max-w-4xl to max-w-6xl
- Remove sidebar-collapsed localStorage persistence
2026-05-02 00:27:36 -03:00

118 lines
3.9 KiB
TypeScript

import React, { useRef } from 'react';
import { Link, Outlet, useLocation } from '@tanstack/react-router';
import { cn } from '@/lib/utils';
import { StatusBar } from '@/components/StatusBar';
import { LayoutDashboard, Settings, Network, RefreshCw, BarChart3 } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { Button } from '@/components/ui/button';
import { ErrorBoundary } from 'react-error-boundary';
import { useToast } from '@/components/ui/use-toast';
import { getLocalizedErrorMessage } from '@/utils/errorMessages';
import iconPng from '@/assets/icon.png';
export const MainLayout: React.FC = () => {
const location = useLocation();
const { t } = useTranslation();
const { toast } = useToast();
const hasShownRouteErrorToastRef = useRef(false);
const navItems = [
{
to: '/',
icon: LayoutDashboard,
label: t('nav.accounts'),
},
{
to: '/usage',
icon: BarChart3,
label: t('nav.usage'),
},
{
to: '/proxy',
icon: Network,
label: t('nav.proxy'),
},
{
to: '/settings',
icon: Settings,
label: t('nav.settings'),
},
];
return (
<div className="bg-background text-foreground flex h-screen flex-col overflow-hidden">
<header className="flex h-12 items-center border-b border-white/[0.06] px-4">
<div className="flex shrink-0 items-center gap-2.5">
<div className="flex h-7 w-7 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-transparent">
<img src={iconPng} alt="Gemini Nexus Logo" className="h-full w-full object-contain" />
</div>
<h1 className="text-[15px] font-semibold tracking-tight whitespace-nowrap">
Gemini Nexus
</h1>
</div>
<nav className="mx-6 flex flex-1 items-center justify-center gap-1 overflow-x-auto">
{navItems.map((item) => {
const isActive = location.pathname === item.to;
return (
<Link
key={item.to}
to={item.to}
className={cn(
'flex items-center gap-2 rounded-md px-3 py-2 text-[13px] font-medium whitespace-nowrap transition-colors',
isActive
? 'text-foreground bg-white/[0.08]'
: 'text-muted-foreground hover:text-foreground hover:bg-white/[0.04]',
)}
>
<item.icon className="h-[16px] w-[16px]" />
{item.label}
</Link>
);
})}
</nav>
<div className="shrink-0">
<StatusBar />
</div>
</header>
<main className="flex-1 overflow-auto">
<ErrorBoundary
resetKeys={[location.pathname]}
onReset={() => {
hasShownRouteErrorToastRef.current = false;
}}
onError={(error) => {
if (hasShownRouteErrorToastRef.current) {
return;
}
toast({
title: t('error.generic'),
description: getLocalizedErrorMessage(error, t),
variant: 'destructive',
});
hasShownRouteErrorToastRef.current = true;
}}
fallbackRender={({ resetErrorBoundary }) => (
<div className="mx-auto max-w-3xl p-6">
<div className="rounded-xl border border-dashed border-white/[0.06] p-8 text-center">
<div className="text-sm font-semibold">{t('error.generic')}</div>
<div className="text-muted-foreground mt-2 text-xs">{t('action.retry')}</div>
<Button className="mt-4" variant="outline" onClick={resetErrorBoundary}>
<RefreshCw className="mr-2 h-4 w-4" />
{t('action.retry')}
</Button>
</div>
</div>
)}
>
<Outlet />
</ErrorBoundary>
</main>
</div>
);
};