Reorganize navigation structure and improve UX

- Reorganized main navigation with cleaner structure
- Updated navigation icons: Feed (RssFeed), Network (Hub), Groups, Post (PostAdd), Chat
- Simplified left menu: removed hierarchical sub-items, added Groups back as main item
- Enhanced Contacts page with Import button and cleaner layout
- Removed unnecessary descriptive text from navigation
- Fixed horizontal overflow in left navigation column
- Updated mobile navigation to match new structure
- Improved navigation item styling with proper text wrapping

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

Co-Authored-By: Claude <noreply@anthropic.com>
main
Oliver Sylvester-Bradley 2 months ago
parent 4ec193928c
commit 77d0da34a6
  1. 109
      src/components/layout/DashboardLayout.tsx
  2. 29
      src/components/navigation/BottomNavigation.tsx
  3. 24
      src/pages/ContactListPage.tsx

@ -20,6 +20,7 @@ import {
Menu, Menu,
MenuItem, MenuItem,
Badge, Badge,
Collapse,
} from '@mui/material'; } from '@mui/material';
import { import {
Menu as MenuIcon, Menu as MenuIcon,
@ -31,10 +32,16 @@ import {
Logout, Logout,
NotificationsNone, NotificationsNone,
SearchRounded, SearchRounded,
Group, Groups,
Home, RssFeed,
PostAdd, PostAdd,
Message, Chat,
ExpandLess,
ExpandMore,
Hub,
List as ListIcon,
AccountTree,
Map,
} from '@mui/icons-material'; } from '@mui/icons-material';
import BottomNavigation from '../navigation/BottomNavigation'; import BottomNavigation from '../navigation/BottomNavigation';
@ -45,6 +52,7 @@ interface NavItem {
icon: ReactNode; icon: ReactNode;
path: string; path: string;
badge?: number; badge?: number;
children?: NavItem[];
} }
interface DashboardLayoutProps { interface DashboardLayoutProps {
@ -56,18 +64,16 @@ const DashboardLayout = ({ children }: DashboardLayoutProps) => {
const isMobile = useMediaQuery(theme.breakpoints.down('md')); const isMobile = useMediaQuery(theme.breakpoints.down('md'));
const [mobileOpen, setMobileOpen] = useState(false); const [mobileOpen, setMobileOpen] = useState(false);
const [profileMenuAnchor, setProfileMenuAnchor] = useState<null | HTMLElement>(null); const [profileMenuAnchor, setProfileMenuAnchor] = useState<null | HTMLElement>(null);
const [expandedItems, setExpandedItems] = useState<Set<string>>(new Set(['Network']));
const location = useLocation(); const location = useLocation();
const navigate = useNavigate(); const navigate = useNavigate();
const navItems: NavItem[] = [ const navItems: NavItem[] = [
{ text: 'Feed', icon: <Home />, path: '/feed' }, { text: 'Feed', icon: <RssFeed />, path: '/feed' },
{ text: 'Contacts', icon: <People />, path: '/contacts' }, { text: 'Network', icon: <Hub />, path: '/contacts' },
{ text: 'Groups', icon: <Group />, path: '/groups' }, { text: 'Groups', icon: <Groups />, path: '/groups' },
{ text: 'Posts & Offers', icon: <PostAdd />, path: '/posts' }, { text: 'Post', icon: <PostAdd />, path: '/posts' },
{ text: 'Messages', icon: <Message />, path: '/messages' }, { text: 'Chat', icon: <Chat />, path: '/messages' },
{ text: 'Import', icon: <CloudDownload />, path: '/import' },
{ text: 'Invite', icon: <QrCode />, path: '/invite' },
{ text: 'Join Network', icon: <PersonAdd />, path: '/onboarding' },
]; ];
const handleDrawerToggle = () => { const handleDrawerToggle = () => {
@ -89,37 +95,58 @@ const DashboardLayout = ({ children }: DashboardLayoutProps) => {
} }
}; };
const toggleExpanded = (itemText: string) => {
setExpandedItems(prev => {
const newSet = new Set(prev);
if (newSet.has(itemText)) {
newSet.delete(itemText);
} else {
newSet.add(itemText);
}
return newSet;
});
};
const isActiveRoute = (path: string) => { const isActiveRoute = (path: string) => {
if (path === '/feed' && (location.pathname === '/' || location.pathname === '/feed')) return true; if (path === '/feed' && (location.pathname === '/' || location.pathname === '/feed')) return true;
if (path !== '/feed' && location.pathname.startsWith(path)) return true; if (path !== '/feed' && location.pathname.startsWith(path)) return true;
return false; return false;
}; };
const drawerContent = ( const isParentActive = (item: NavItem) => {
<Box sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}> if (item.children) {
<Box sx={{ p: 3, borderBottom: 1, borderColor: 'divider' }}> return item.children.some(child => isActiveRoute(child.path));
<Typography variant="h6" sx={{ fontWeight: 700, color: 'primary.main' }}> }
NAO return false;
</Typography> };
<Typography variant="body2" color="text.secondary">
Personal Network Hub
</Typography>
</Box>
<List sx={{ flex: 1, py: 2 }}> const renderNavItem = (item: NavItem, level: number = 0) => {
{navItems.map((item) => ( const hasChildren = item.children && item.children.length > 0;
<ListItem key={item.text} disablePadding> const isExpanded = expandedItems.has(item.text);
const isActive = isActiveRoute(item.path);
const isParentOfActive = isParentActive(item);
return (
<div key={item.text}>
<ListItem disablePadding>
<ListItemButton <ListItemButton
onClick={() => handleNavigation(item.path)} onClick={() => {
selected={isActiveRoute(item.path)} if (hasChildren) {
toggleExpanded(item.text);
} else {
handleNavigation(item.path);
}
}}
selected={isActive || isParentOfActive}
sx={{ sx={{
mx: 1, mx: 1,
ml: level > 0 ? 3 : 1,
borderRadius: 2, borderRadius: 2,
minHeight: 48, minHeight: 48,
'&.Mui-selected': { '&.Mui-selected': {
backgroundColor: 'primary.main', backgroundColor: 'primary.main',
color: 'primary.contrastText', color: 'primary.contrastText',
ml: 1, ml: level > 0 ? 3 : 1,
mr: 2, mr: 2,
'&:hover': { '&:hover': {
backgroundColor: 'primary.dark', backgroundColor: 'primary.dark',
@ -143,12 +170,36 @@ const DashboardLayout = ({ children }: DashboardLayoutProps) => {
primary={item.text} primary={item.text}
primaryTypographyProps={{ primaryTypographyProps={{
fontSize: '0.875rem', fontSize: '0.875rem',
fontWeight: isActiveRoute(item.path) ? 600 : 500, fontWeight: isActive || isParentOfActive ? 600 : 500,
noWrap: true,
}} }}
/> />
{hasChildren && (
isExpanded ? <ExpandLess /> : <ExpandMore />
)}
</ListItemButton> </ListItemButton>
</ListItem> </ListItem>
))} {hasChildren && (
<Collapse in={isExpanded} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
{item.children?.map((child) => renderNavItem(child, level + 1))}
</List>
</Collapse>
)}
</div>
);
};
const drawerContent = (
<Box sx={{ height: '100%', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
<Box sx={{ p: 3, borderBottom: 1, borderColor: 'divider', flexShrink: 0 }}>
<Typography variant="h6" sx={{ fontWeight: 700, color: 'primary.main' }}>
NAO
</Typography>
</Box>
<List sx={{ flex: 1, py: 2, overflow: 'hidden' }}>
{navItems.map((item) => renderNavItem(item))}
</List> </List>
<Divider /> <Divider />

@ -5,11 +5,11 @@ import {
Paper Paper
} from '@mui/material'; } from '@mui/material';
import { import {
Home, RssFeed,
People, Hub,
PostAdd, PostAdd,
Message, Chat,
AccountCircle, Groups,
} from '@mui/icons-material'; } from '@mui/icons-material';
const BottomNavigation = () => { const BottomNavigation = () => {
@ -17,16 +17,27 @@ const BottomNavigation = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const navigationItems = [ const navigationItems = [
{ label: 'Feed', icon: <Home />, path: '/feed' }, { label: 'Feed', icon: <RssFeed />, path: '/feed' },
{ label: 'Contacts', icon: <People />, path: '/contacts' }, { label: 'Network', icon: <Hub />, path: '/contacts' },
{ label: 'Posts', icon: <PostAdd />, path: '/posts' }, { label: 'Groups', icon: <Groups />, path: '/groups' },
{ label: 'Messages', icon: <Message />, path: '/messages' }, { label: 'Post', icon: <PostAdd />, path: '/posts' },
{ label: 'Account', icon: <AccountCircle />, path: '/account' }, { label: 'Chat', icon: <Chat />, path: '/messages' },
]; ];
const getCurrentValue = () => { const getCurrentValue = () => {
const currentPath = location.pathname; const currentPath = location.pathname;
if (currentPath === '/' || currentPath === '/feed') return '/feed'; if (currentPath === '/' || currentPath === '/feed') return '/feed';
// Handle network path - should highlight Network tab
if (currentPath.startsWith('/contacts')) {
return '/contacts';
}
// Groups has its own tab now
if (currentPath.startsWith('/groups')) {
return '/groups';
}
const activeItem = navigationItems.find(item => const activeItem = navigationItems.find(item =>
item.path === currentPath || (item.path !== '/feed' && currentPath.startsWith(item.path)) item.path === currentPath || (item.path !== '/feed' && currentPath.startsWith(item.path))
); );

@ -19,15 +19,16 @@ import {
} from '@mui/material'; } from '@mui/material';
import { import {
List as ListIcon, List as ListIcon,
AccountTree, Hub,
ScatterPlot, Map,
Search, Search,
Add, Add,
LinkedIn, LinkedIn,
Phone, Phone,
Email, Email,
QrCode, QrCode,
Group Group,
CloudDownload
} from '@mui/icons-material'; } from '@mui/icons-material';
import { dataService } from '../services/dataService'; import { dataService } from '../services/dataService';
import type { Contact } from '../types/contact'; import type { Contact } from '../types/contact';
@ -109,11 +110,16 @@ const ContactListPage = () => {
<Typography variant="h4" component="h1" sx={{ fontWeight: 700, mb: 1 }}> <Typography variant="h4" component="h1" sx={{ fontWeight: 700, mb: 1 }}>
Contacts Contacts
</Typography> </Typography>
<Typography variant="body1" color="text.secondary">
Manage your network of {filteredContacts.length} contacts
</Typography>
</Box> </Box>
<Box sx={{ display: 'flex', gap: 1 }}> <Box sx={{ display: 'flex', gap: 1 }}>
<Button
variant="outlined"
startIcon={<CloudDownload />}
onClick={() => navigate('/import')}
sx={{ borderRadius: 2 }}
>
Import
</Button>
<Button <Button
variant="outlined" variant="outlined"
startIcon={<QrCode />} startIcon={<QrCode />}
@ -148,15 +154,15 @@ const ContactListPage = () => {
} }
}} }}
> >
<Tab icon={<ListIcon />} label="List View" /> <Tab icon={<ListIcon />} label="List" />
<Tooltip title="Coming soon"> <Tooltip title="Coming soon">
<span> <span>
<Tab icon={<AccountTree />} label="Network View" disabled /> <Tab icon={<Hub />} label="Network" disabled />
</span> </span>
</Tooltip> </Tooltip>
<Tooltip title="Coming soon"> <Tooltip title="Coming soon">
<span> <span>
<Tab icon={<ScatterPlot />} label="Graph View" disabled /> <Tab icon={<Map />} label="Map" disabled />
</span> </span>
</Tooltip> </Tooltip>
</Tabs> </Tabs>

Loading…
Cancel
Save