Files
maternal-app/maternal-web/components/layouts/TabBar/TabBar.tsx
Andrei fb9f9d25c7
Some checks failed
CI/CD Pipeline / Lint and Test (push) Has been cancelled
CI/CD Pipeline / E2E Tests (push) Has been cancelled
CI/CD Pipeline / Build Application (push) Has been cancelled
feat: Unify navigation with consistent header and centered bottom menu
- Removed duplicate MobileNav AppBar from desktop view
- Added unified header bar across all screen sizes (mobile + desktop)
- Positioned connection status on left, user menu on right in header
- Centered bottom navigation menu on desktop (30% width with 20px margin)
- Hidden floating voice button on desktop (using center button instead)
- Updated voice command button to pink color (#FF69B4) matching design
- Added rounded corners to desktop bottom navigation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-03 20:55:42 +00:00

144 lines
4.2 KiB
TypeScript

'use client';
import { usePathname, useRouter } from 'next/navigation';
import { useState } from 'react';
import { BottomNavigation, BottomNavigationAction, Paper, Fab, Box, IconButton } from '@mui/material';
import {
Home,
Timeline,
Insights,
Chat,
Mic,
} from '@mui/icons-material';
import { useTranslation } from '@/hooks/useTranslation';
export const TabBar = () => {
const { t } = useTranslation('common');
const router = useRouter();
const pathname = usePathname();
const [voiceOpen, setVoiceOpen] = useState(false);
const tabs = [
{ label: t('navigation.home'), icon: <Home />, value: '/' },
{ label: t('navigation.track'), icon: <Timeline />, value: '/track' },
{ label: '', icon: null, value: 'voice' }, // Placeholder for center button
{ label: t('navigation.insights'), icon: <Insights />, value: '/insights' },
{ label: t('navigation.aiChat'), icon: <Chat />, value: '/ai-assistant' },
];
return (
<>
<Paper
component="nav"
aria-label="Primary navigation"
sx={{
position: 'fixed',
bottom: { xs: 0, md: 20 },
left: { xs: 0, md: '50%' },
right: { xs: 0, md: 'auto' },
transform: { xs: 'none', md: 'translateX(-50%)' },
width: { xs: '100%', md: '30%' },
zIndex: 1000,
borderRadius: { xs: 0, md: 2 },
}}
elevation={3}
>
<BottomNavigation
value={pathname}
onChange={(event, newValue) => {
if (newValue !== 'voice') {
router.push(newValue);
}
}}
showLabels
sx={{
height: 64,
borderRadius: { xs: 0, md: 2 },
'& .MuiBottomNavigationAction-root': {
minWidth: 60,
'&.Mui-selected': {
color: 'primary.main',
},
},
}}
>
{tabs.map((tab) => {
if (tab.value === 'voice') {
// Center voice button
return (
<Box
key="voice-placeholder"
sx={{
flex: 1,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<IconButton
aria-label="voice command"
onClick={() => {
const voiceButton = document.querySelector('[aria-label="voice input"]') as HTMLButtonElement;
if (voiceButton) {
voiceButton.click();
}
}}
sx={{
bgcolor: '#FF69B4',
color: 'white',
width: 48,
height: 48,
'&:hover': {
bgcolor: '#FF1493',
},
}}
>
<Mic />
</IconButton>
</Box>
);
}
return (
<BottomNavigationAction
key={tab.value}
label={tab.label}
icon={tab.icon}
value={tab.value}
/>
);
})}
</BottomNavigation>
</Paper>
{/* Voice Command Floating Button - Mobile Only */}
<Fab
color="secondary"
aria-label="voice command"
onClick={() => {
// Trigger voice command - will integrate with existing VoiceFloatingButton
const voiceButton = document.querySelector('[aria-label="voice input"]') as HTMLButtonElement;
if (voiceButton) {
voiceButton.click();
}
}}
sx={{
display: { xs: 'flex', md: 'none' },
position: 'fixed',
bottom: 40,
left: '50%',
transform: 'translateX(-50%)',
zIndex: 1100,
width: 56,
height: 56,
bgcolor: '#FF69B4',
'&:hover': {
bgcolor: '#FF1493',
},
}}
>
<Mic />
</Fab>
</>
);
};