Skip to content

Commit 94f9cc4

Browse files
committed
feat: enhance MainLayout with mobile navigation and logout functionality
- Introduced a responsive navigation menu for mobile devices, allowing users to access key sections easily. - Added a logout button with both icon and text options for better user experience. - Updated language switcher to include a globe icon for improved visual representation. - Added "logout" translations in English and Chinese locales.
1 parent 6406603 commit 94f9cc4

File tree

4 files changed

+104
-49
lines changed

4 files changed

+104
-49
lines changed

src/components/layouts/MainLayout.tsx

Lines changed: 98 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
BarChart3,
66
CreditCard,
77
History,
8+
LogOut,
89
} from 'lucide-react'
910

1011
import { Button } from '@/components/ui/button'
@@ -23,6 +24,34 @@ export function MainLayout({ children }: MainLayoutProps) {
2324
const { t } = useTranslation('navigation')
2425
const { user, logout } = useAuthStore()
2526

27+
const navLinks = [
28+
{
29+
to: '/',
30+
icon: <Home className="h-4 w-4" />,
31+
text: t('dashboard'),
32+
},
33+
{
34+
to: '/subscriptions',
35+
icon: <CreditCard className="h-4 w-4" />,
36+
text: t('subscriptions'),
37+
},
38+
{
39+
to: '/expense-reports',
40+
icon: <BarChart3 className="h-4 w-4" />,
41+
text: t('reports'),
42+
},
43+
{
44+
to: '/notifications',
45+
icon: <History className="h-4 w-4" />,
46+
text: t('notifications'),
47+
},
48+
{
49+
to: '/settings',
50+
icon: <Settings className="h-4 w-4" />,
51+
text: t('settings'),
52+
},
53+
]
54+
2655
return (
2756
<div className="flex flex-col min-h-screen bg-background">
2857
<header className="sticky top-0 z-40 border-b bg-background/95 backdrop-blur">
@@ -31,64 +60,86 @@ export function MainLayout({ children }: MainLayoutProps) {
3160
<Link to="/" className="flex items-center space-x-2">
3261
<span className="font-bold text-lg sm:text-xl">SubManager</span>
3362
</Link>
63+
<nav className="hidden md:flex items-center gap-1 sm:gap-2">
64+
{navLinks.map((link) => (
65+
<Link to={link.to} key={link.to}>
66+
<Button
67+
variant={
68+
location.pathname === link.to ? 'default' : 'ghost'
69+
}
70+
size="sm"
71+
className="px-2 sm:px-3"
72+
>
73+
{link.icon}
74+
<span className="md:ml-2">{link.text}</span>
75+
</Button>
76+
</Link>
77+
))}
78+
</nav>
3479
</div>
3580

36-
<div className="flex items-center gap-1 sm:gap-2">
37-
<Link to="/">
38-
<Button variant={location.pathname === '/' ? "default" : "ghost"} size="sm" className="px-2 sm:px-3">
39-
<Home className="h-4 w-4 sm:mr-2" />
40-
<span className="hidden sm:inline">{t('dashboard')}</span>
41-
</Button>
42-
</Link>
43-
44-
<Link to="/subscriptions">
45-
<Button variant={location.pathname === '/subscriptions' ? "default" : "ghost"} size="sm" className="px-2 sm:px-3">
46-
<CreditCard className="h-4 w-4 sm:mr-2" />
47-
<span className="hidden sm:inline">{t('subscriptions')}</span>
48-
</Button>
49-
</Link>
50-
51-
<Link to="/expense-reports">
52-
<Button variant={location.pathname === '/expense-reports' ? "default" : "ghost"} size="sm" className="px-2 sm:px-3">
53-
<BarChart3 className="h-4 w-4 sm:mr-2" />
54-
<span className="hidden sm:inline">{t('reports')}</span>
55-
</Button>
56-
</Link>
57-
58-
<Link to="/notifications">
59-
<Button variant={location.pathname === '/notifications' ? "default" : "ghost"} size="sm" className="px-2 sm:px-3">
60-
<History className="h-4 w-4 sm:mr-2" />
61-
<span className="hidden sm:inline">{t('notifications')}</span>
62-
</Button>
63-
</Link>
64-
65-
<Link to="/settings">
66-
<Button variant={location.pathname === '/settings' ? "default" : "ghost"} size="sm" className="px-2 sm:px-3">
67-
<Settings className="h-4 w-4 sm:mr-2" />
68-
<span className="hidden sm:inline">{t('settings')}</span>
69-
</Button>
70-
</Link>
71-
72-
<div className="flex items-center gap-2">
73-
<LanguageSwitcher />
74-
<ModeToggle />
75-
{user && (
76-
<UIButton variant="ghost" size="sm" onClick={logout}>Logout</UIButton>
77-
)}
78-
</div>
81+
<div className="flex items-center gap-2">
82+
<LanguageSwitcher />
83+
<ModeToggle />
84+
{user && (
85+
<>
86+
<UIButton
87+
variant="ghost"
88+
size="sm"
89+
onClick={logout}
90+
className="hidden md:inline-flex"
91+
>
92+
{t('logout')}
93+
</UIButton>
94+
<UIButton
95+
variant="ghost"
96+
size="icon"
97+
onClick={logout}
98+
className="md:hidden"
99+
>
100+
<LogOut className="h-5 w-5" />
101+
</UIButton>
102+
</>
103+
)}
79104
</div>
80105
</div>
81106
</header>
82-
83-
<main className="container py-4 sm:py-6 px-4 sm:px-6 flex-grow">{children}</main>
84-
85-
<footer className="border-t py-4 sm:py-6">
107+
108+
<main className="container py-4 sm:py-6 px-4 sm:px-6 flex-grow pb-20 md:pb-6">
109+
{children}
110+
</main>
111+
112+
<footer className="border-t py-4 sm:py-6 hidden md:block">
86113
<div className="container flex flex-col items-center justify-center gap-4 md:flex-row md:gap-6 px-4 sm:px-6">
87114
<p className="text-center text-sm leading-loose text-muted-foreground">
88115
&copy; {new Date().getFullYear()} SubManager. All rights reserved.
89116
</p>
90117
</div>
91118
</footer>
119+
120+
{/* Bottom Navigation for Mobile */}
121+
<div className="md:hidden fixed bottom-0 left-0 right-0 z-40 border-t bg-background">
122+
<div className="container mx-auto px-4">
123+
<nav className="flex justify-around items-center h-16">
124+
{navLinks.map((link) => (
125+
<Link
126+
to={link.to}
127+
key={link.to}
128+
className="flex flex-col items-center justify-center text-muted-foreground hover:text-primary"
129+
>
130+
<Button
131+
variant={
132+
location.pathname === link.to ? 'secondary' : 'ghost'
133+
}
134+
size="icon"
135+
>
136+
{link.icon}
137+
</Button>
138+
</Link>
139+
))}
140+
</nav>
141+
</div>
142+
</div>
92143
</div>
93144
)
94145
}

src/components/ui/LanguageSwitcher.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useEffect } from 'react';
22
import { useTranslation } from 'react-i18next';
3+
import { Globe } from 'lucide-react';
34
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './select';
45
import { cn } from '@/lib/utils';
56
import { apiClient } from '@/utils/api-client';
@@ -50,6 +51,7 @@ export function LanguageSwitcher() {
5051
<SelectTrigger className={cn(
5152
"w-auto min-w-0 px-3 py-2 h-9 gap-1.5"
5253
)}>
54+
<Globe className="h-4 w-4" />
5355
<SelectValue>
5456
{currentLanguage?.name || t('language')}
5557
</SelectValue>

src/i18n/locales/en/navigation.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
"settings": "Settings",
66
"dashboard": "Dashboard",
77
"expenseReports": "Expense Reports",
8-
"notifications": "Notification History"
8+
"notifications": "Notification History",
9+
"logout": "Logout"
910
}

src/i18n/locales/zh-CN/navigation.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
"settings": "设置",
66
"dashboard": "仪表板",
77
"expenseReports": "支出报表",
8-
"notifications": "通知历史"
8+
"notifications": "通知历史",
9+
"logout": "登出"
910
}

0 commit comments

Comments
 (0)