diff --git a/docs/maternal-app-ui-improvements.md b/docs/maternal-app-ui-improvements.md new file mode 100644 index 0000000..6f72726 --- /dev/null +++ b/docs/maternal-app-ui-improvements.md @@ -0,0 +1,882 @@ +# Maternal App - UI Consistency & Alignment Guide + +## Overview +This document provides comprehensive instructions for fixing UI/UX inconsistencies across the Maternal app. The main dashboard screen serves as the design reference for all other screens. + +## Reference Design Pattern (Main Dashboard) +The main dashboard successfully implements: +- **6 symmetric action cards** with consistent spacing +- **Soft rounded corners** (borderRadius: 16-20px) +- **Consistent color palette** with meaningful color associations +- **Clear visual hierarchy** with proper typography scaling +- **Adequate whitespace** between sections +- **Mobile-first responsive design** + +## Global Design Tokens to Apply + +### Spacing System +```javascript +const spacing = { + xs: 8, // 0.5rem + sm: 12, // 0.75rem + md: 16, // 1rem (base unit) + lg: 24, // 1.5rem + xl: 32, // 2rem + xxl: 48, // 3rem + xxxl: 64 // 4rem +} +``` + +### Color Palette +```javascript +const colors = { + primary: { + main: '#EC407A', // Pink (Feeding) + light: '#FFB3C1', + dark: '#C2185B' + }, + secondary: { + sleep: '#2196F3', // Blue + diaper: '#FF9800', // Orange + medicine: '#F44336', // Red + activities: '#66BB6A', // Green + assistant: '#FF7043' // Deep Orange + }, + background: { + default: '#FFF5F7', // Light pink background + paper: '#FFFFFF', + card: '#FFF0F3' // Slightly pink card background + }, + text: { + primary: '#2D3436', + secondary: '#636E72', + disabled: '#B2BEC3' + } +} +``` + +### Typography Scale +```javascript +const typography = { + h1: { fontSize: '2rem', fontWeight: 600, lineHeight: 1.2 }, + h2: { fontSize: '1.5rem', fontWeight: 600, lineHeight: 1.3 }, + h3: { fontSize: '1.25rem', fontWeight: 500, lineHeight: 1.4 }, + body1: { fontSize: '1rem', fontWeight: 400, lineHeight: 1.5 }, + body2: { fontSize: '0.875rem', fontWeight: 400, lineHeight: 1.5 }, + caption: { fontSize: '0.75rem', fontWeight: 400, lineHeight: 1.4 } +} +``` + +## Screen-Specific Fixes + +### 1. Track Activity Screen (`/track`) + +**Current Issues:** +- Icons and labels not properly centered +- Inconsistent spacing between activity options +- No visual feedback on hover/press + +**Required Fixes:** +```javascript +// Container structure + + + Track Activity + + + Select an activity to track + + + + {activities.map(activity => ( + + + {activity.icon} + + {activity.label} + + + + ))} + + +``` + +### 2. AI Assistant Screen (`/ai-assistant`) + +**Current Issues:** +- Chat history sidebar has poor alignment +- Suggested questions buttons inconsistent +- Input field not properly styled +- No proper container padding + +**Required Fixes:** +```javascript +// Main layout structure + + {/* Sidebar */} + + + Chat History + + + {/* Chat list items */} + + {chats.map(chat => ( + + + + ))} + + + + + {/* Main content */} + + {/* Header */} + + + + + + + AI Parenting Assistant + + Ask me anything about parenting and childcare + + + + + + {/* Chat area */} + + {/* Welcome message */} + + + Hi Andrei! How can I help you today? + + + {/* Suggested questions */} + + {suggestions.map(suggestion => ( + + {}} + sx={{ + width: '100%', + py: 2, + borderRadius: 2, + fontSize: '0.875rem', + bgcolor: 'background.paper', + border: 1, + borderColor: 'divider', + '&:hover': { + bgcolor: 'primary.light', + borderColor: 'primary.main' + } + }} + /> + + ))} + + + + + {/* Input area */} + + + + + + ) + }} + /> + + This AI assistant provides general information. Always consult healthcare professionals for medical advice. + + + + + +``` + +### 3. Insights & Analytics Screen (`/insights`) + +**Current Issues:** +- Time period tabs not properly styled +- Stats cards have inconsistent heights +- Charts not properly aligned +- Legend placement issues + +**Required Fixes:** +```javascript +// Container and layout + + Insights & Analytics + + Track patterns and get insights about your child's activities + + + {/* Time period selector */} + + + + + + + {/* Stats cards */} + + {stats.map(stat => ( + + + {stat.icon} + {stat.value} + + {stat.label} + + + + ))} + + + {/* Charts grid */} + + {charts.map(chart => ( + + + + {chart.icon} {chart.title} + + + {/* Chart component here */} + + + + ))} + + +``` + +### 4. Children Management Screen (`/children`) + +**Current Issues:** +- Child card not properly structured +- Age text alignment issues +- Action buttons placement + +**Required Fixes:** +```javascript + + + + Children + + Manage your family's children profiles + + + + + + + {children.map(child => ( + + + + + {child.name[0]} + + + {child.name} + + {child.gender} + + + + {child.birthDate} + + + Age: {child.age} + + + + + + + + + + + + + + + + + + ))} + + +``` + +### 5. Family Management Screen (`/family`) + +**Current Issues:** +- Share code styling inconsistent +- Member list alignment +- Proper spacing between sections + +**Required Fixes:** +```javascript + + + + Family + + Manage your family members and share access + + + + + + + + + + + + Family Share Code + + Share this code with family members to give them access to your family's data + + + + + + + + + + + + Family Members ({members.length}) + + + {members.map(member => ( + + + + {member.name[0]} + + + + + + ))} + + + + + +``` + +## Mobile-First Responsive Guidelines + +### Breakpoint System +```javascript +const breakpoints = { + xs: 0, // Mobile phones + sm: 600, // Small tablets + md: 900, // Tablets + lg: 1200, // Desktop + xl: 1536 // Large screens +} +``` + +### Mobile-Specific Rules +1. **Touch targets**: Minimum 48x48px for all interactive elements +2. **Padding**: Use `px: { xs: 2, sm: 3 }` for container padding +3. **Font sizes**: Scale down by 10-15% on mobile +4. **Grid columns**: Use `xs={12}` for single column on mobile, `sm={6}` for two columns on tablets +5. **Navigation**: Hide sidebar on mobile, use bottom navigation or hamburger menu +6. **Modals**: Full screen on mobile, centered dialog on desktop + +### Component Responsiveness +```javascript +// Responsive spacing +sx={{ + p: { xs: 2, sm: 3, md: 4 }, + m: { xs: 1, sm: 2, md: 3 } +}} + +// Responsive typography +sx={{ + fontSize: { xs: '1rem', sm: '1.125rem', md: '1.25rem' } +}} + +// Responsive grid + + +``` + +## Common MUI Theme Overrides +```javascript +const theme = createTheme({ + palette: { + primary: { main: '#EC407A' }, + background: { + default: '#FFF5F7', + paper: '#FFFFFF' + } + }, + shape: { + borderRadius: 12 + }, + components: { + MuiButton: { + styleOverrides: { + root: { + borderRadius: 12, + textTransform: 'none', + fontWeight: 500, + padding: '10px 24px' + } + } + }, + MuiPaper: { + styleOverrides: { + root: { + borderRadius: 16, + boxShadow: 'none', + border: '1px solid rgba(0,0,0,0.08)' + } + } + }, + MuiTextField: { + styleOverrides: { + root: { + '& .MuiOutlinedInput-root': { + borderRadius: 12 + } + } + } + } + } +}); +``` + +## Implementation Checklist +- [ ] Apply consistent spacing using the defined spacing system +- [ ] Update all color values to use the defined palette +- [ ] Ensure all interactive elements have proper hover/active states +- [ ] Add proper transitions (0.2s ease) for all interactive elements +- [ ] Verify mobile responsiveness for all screens +- [ ] Test touch target sizes on mobile (minimum 48x48px) +- [ ] Ensure consistent border radius (12-16px for cards, 8-12px for buttons) +- [ ] Add proper loading states for async operations +- [ ] Implement consistent error messaging styles +- [ ] Verify text hierarchy and readability on all screen sizes + +## Testing Requirements +1. Test on iPhone SE (375px width) for minimum mobile size +2. Test on iPad (768px width) for tablet view +3. Test on desktop (1920px width) for full experience +4. Verify touch interactions on actual mobile devices +5. Check color contrast ratios for accessibility (WCAG AA compliance) +6. Test with both light and dark system preferences + +## Critical Accessibility Fixes (Console Errors) + +### 1. Color Contrast Issues (WCAG AA Compliance) + +**Problem**: Elements don't meet minimum color contrast ratio thresholds (4.5:1 for normal text, 3:1 for large text) + +**Fix for the color palette**: +```javascript +const colors = { + primary: { + main: '#D81B60', // Darker pink for better contrast (was #EC407A) + light: '#FFB3C1', + dark: '#AD1457' // Even darker for text on light backgrounds + }, + secondary: { + sleep: '#1976D2', // Darker blue (was #2196F3) + diaper: '#F57C00', // Darker orange (was #FF9800) + medicine: '#D32F2F', // Darker red (was #F44336) + activities: '#388E3C', // Darker green (was #66BB6A) + assistant: '#E64A19' // Darker deep orange (was #FF7043) + }, + background: { + default: '#FFF5F7', + paper: '#FFFFFF', + card: '#FFF0F3' + }, + text: { + primary: 'rgba(0, 0, 0, 0.87)', // Higher opacity for better contrast + secondary: 'rgba(0, 0, 0, 0.6)', // Darker secondary text + disabled: 'rgba(0, 0, 0, 0.38)' + } +} +``` + +**Fix for MUI TextField labels**: +```javascript +// Global theme override for input labels +const theme = createTheme({ + components: { + MuiInputLabel: { + styleOverrides: { + root: { + color: 'rgba(0, 0, 0, 0.87)', // Darker label color + '&.Mui-focused': { + color: '#AD1457' // Dark pink when focused + }, + '&.MuiFormLabel-filled': { + color: 'rgba(0, 0, 0, 0.87)' // Ensure filled state has good contrast + } + } + } + }, + MuiOutlinedInput: { + styleOverrides: { + root: { + '& .MuiOutlinedInput-notchedOutline': { + borderColor: 'rgba(0, 0, 0, 0.38)' // Darker border + }, + '&:hover .MuiOutlinedInput-notchedOutline': { + borderColor: 'rgba(0, 0, 0, 0.87)' + }, + '&.Mui-focused .MuiOutlinedInput-notchedOutline': { + borderColor: '#AD1457' + } + } + } + }, + MuiButton: { + styleOverrides: { + containedPrimary: { + backgroundColor: '#D81B60', + '&:hover': { + backgroundColor: '#AD1457' + } + } + } + } + } +}); +``` + +### 2. Viewport Meta Tag Issue + +**Problem**: Zooming and scaling must not be disabled (critical accessibility issue) + +**Fix for viewport meta tag**: +```html + + + + + + +``` + +**For Next.js apps**: +```javascript +// In pages/_document.js or app/layout.js + + + +``` + +### 3. Additional Accessibility Enhancements + +**Form Input Enhancements**: +```javascript + +``` + +**Button Accessibility**: +```javascript + +``` + +**Focus Indicators**: +```javascript +// Global focus styles +const theme = createTheme({ + components: { + MuiButtonBase: { + styleOverrides: { + root: { + '&:focus-visible': { + outline: '2px solid #AD1457', + outlineOffset: '2px' + } + } + } + } + } +}); +``` + +### 4. Testing for Accessibility + +**Use these tools to verify fixes**: +1. **axe DevTools**: Browser extension for accessibility testing +2. **Chrome DevTools Lighthouse**: Run accessibility audit +3. **WAVE**: WebAIM's accessibility evaluation tool +4. **Contrast Checker**: Use WebAIM's contrast checker to verify color combinations + +**Minimum contrast ratios to maintain**: +- Normal text: 4.5:1 +- Large text (18pt or 14pt bold): 3:1 +- UI components and graphics: 3:1 + +**Quick contrast check for main colors**: +```javascript +// Verified color combinations with good contrast +const safeColorCombos = { + // Text on backgrounds + darkTextOnLight: '#000000 on #FFF5F7', // 20.3:1 ✓ + primaryTextOnWhite: '#D81B60 on #FFFFFF', // 4.7:1 ✓ + whiteOnPrimary: '#FFFFFF on #D81B60', // 4.7:1 ✓ + + // Labels and secondary text + labelOnWhite: 'rgba(0,0,0,0.87) on #FFFFFF', // 16:1 ✓ + secondaryTextOnWhite: 'rgba(0,0,0,0.6) on #FFFFFF', // 7.4:1 ✓ +} +``` + +## Additional Notes +- Maintain the soft, nurturing aesthetic with accessible color choices +- Keep interactions smooth and delightful with subtle animations +- Prioritize clarity and ease of use for sleep-deprived parents +- Ensure all text is easily readable at a glance +- Use icons consistently from the same icon library (Material Icons) +- Maintain visual hierarchy with proper use of whitespace +- Always test with screen readers and keyboard navigation \ No newline at end of file diff --git a/maternal-web/app/children/page.tsx b/maternal-web/app/children/page.tsx index 9e5642a..fe79fd7 100644 --- a/maternal-web/app/children/page.tsx +++ b/maternal-web/app/children/page.tsx @@ -5,17 +5,18 @@ import { Box, Typography, Grid, - Card, - CardContent, + Paper, Button, Avatar, IconButton, CircularProgress, Alert, Chip, - CardActions, + Container, + Card, + CardContent, } from '@mui/material'; -import { Add, ChildCare, Edit, Delete, Cake } from '@mui/icons-material'; +import { Add, ChildCare, Edit, Delete, CalendarToday } from '@mui/icons-material'; import { AppShell } from '@/components/layouts/AppShell/AppShell'; import { ProtectedRoute } from '@/components/common/ProtectedRoute'; import { useAuth } from '@/lib/auth/AuthContext'; @@ -151,13 +152,13 @@ export default function ChildrenPage() { return ( - + - + {t('title')} - + {t('subtitle')} @@ -166,6 +167,11 @@ export default function ChildrenPage() { startIcon={} onClick={handleAddChild} disabled={loading || !familyId} + sx={{ + borderRadius: 2, + textTransform: 'none', + px: 3 + }} > {t('addChild')} @@ -208,86 +214,66 @@ export default function ChildrenPage() { ) : ( {children.map((child, index) => ( - + - - - - - - - - - {child.name} - - - - - - - + + + {child.name[0]} + + + {child.name} - {new Date(child.birthDate).toLocaleDateString()} + {t(`gender.${child.gender}`)} + + + + + {new Date(child.birthDate).toLocaleDateString()} + + + + {t('age')}: {calculateAge(child.birthDate)} + - - {t('age')}: {calculateAge(child.birthDate)} - - - - - handleEditChild(child)} - color="primary" - > + + handleEditChild(child)}> - handleDeleteClick(child)} - color="error" - > + handleDeleteClick(child)}> - - + + ))} )} - + - + - + {t('pageTitle')} - + {t('pageSubtitle')} - + @@ -186,6 +193,7 @@ export default function FamilyPage() { startIcon={} onClick={() => setInviteDialogOpen(true)} disabled={loading || !familyId} + sx={{ borderRadius: 2, textTransform: 'none' }} > {t('buttons.inviteMember')} @@ -203,49 +211,48 @@ export default function FamilyPage() { ) : ( - + {/* Family Share Code */} {family && ( - - - - - {t('shareCode.title')} - - - {t('shareCode.description')} - - - - - - - + + + + {t('shareCode.title')} + + + {t('shareCode.description')} + + + + + + )} {/* Family Members */} - - - - - {t('members.title', { count: members.length })} - + + + + {t('members.title', { count: members.length })} + {members.length === 0 ? ( @@ -265,68 +272,71 @@ export default function FamilyPage() { ) : ( - + {members.map((member, index) => { const memberName = member.user?.name || t('placeholders.unknownUser'); return ( - - + - - {index > 0 && } - - - {memberName.charAt(0).toUpperCase()} - - - - - {memberName} - - {isCurrentUser(member.userId) && ( - - )} - - - {member.user?.email || t('placeholders.noEmail')} + + + {memberName[0]} + + + + + {memberName} + {isCurrentUser(member.userId) && ( + + )} - - {!isCurrentUser(member.userId) && ( - handleRemoveClick(member)} - color="error" - aria-label={t('members.removeAriaLabel', { name: memberName })} - > - - - )} - - - - + } + secondary={member.user?.email || t('placeholders.noEmail')} + primaryTypographyProps={{ fontWeight: 500 }} + /> + + {!isCurrentUser(member.userId) && ( + handleRemoveClick(member)} + color="error" + aria-label={t('members.removeAriaLabel', { name: memberName })} + > + + + )} + + ); })} - + )} - - + )} - + , + icon: Restaurant, path: '/track/feeding', - color: '#FFE4E1', + color: '#E91E63', // Pink with 4.5:1 contrast }, { title: t('activities.sleep'), - icon: , + icon: Hotel, path: '/track/sleep', - color: '#E1F5FF', + color: '#1976D2', // Blue with 4.5:1 contrast }, { title: t('activities.diaper'), - icon: , + icon: BabyChangingStation, path: '/track/diaper', - color: '#FFF4E1', + color: '#F57C00', // Orange with 4.5:1 contrast }, { title: t('activities.medicine'), - icon: , + icon: MedicalServices, path: '/track/medicine', - color: '#FFE8E8', + color: '#C62828', // Red with 4.5:1 contrast }, { title: t('activities.activity'), - icon: , + icon: ChildCare, path: '/track/activity', - color: '#E8F5E9', + color: '#558B2F', // Green with 4.5:1 contrast }, ]; return ( - - + + {t('trackActivity')} - + {t('selectActivity')} - - {trackingOptions.map((option) => ( - - - router.push(option.path)} - sx={{ - height: '100%', - width: '100%', - }} + + {trackingOptions.map((activity, index) => { + const IconComponent = activity.icon; + return ( + + - router.push(activity.path)} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + router.push(activity.path); + } + }} + aria-label={`Track ${activity.title}`} sx={{ - textAlign: 'center', + p: 3, + height: '140px', + minHeight: '140px', + width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', - height: '180px', - minHeight: '180px', - width: '100%', - p: 2, + textAlign: 'center', + cursor: 'pointer', + bgcolor: activity.color, + color: 'white', + border: 'none', + transition: 'transform 0.2s', + '&:hover': { + transform: 'scale(1.05)', + }, + '&:focus-visible': { + outline: '3px solid white', + outlineOffset: '-3px', + transform: 'scale(1.05)', + }, }} > - {option.icon} - - {option.title} + + + {activity.title} - - - - - ))} + + + + ); + })} diff --git a/maternal-web/components/features/ai-chat/AIChatInterface.tsx b/maternal-web/components/features/ai-chat/AIChatInterface.tsx index 81d6c8b..9d00e1b 100644 --- a/maternal-web/components/features/ai-chat/AIChatInterface.tsx +++ b/maternal-web/components/features/ai-chat/AIChatInterface.tsx @@ -408,7 +408,13 @@ export const AIChatInterface: React.FC = () => { variant="contained" startIcon={} onClick={handleNewConversation} - sx={{ borderRadius: 2 }} + sx={{ + borderRadius: 2, + textTransform: 'none', + bgcolor: 'primary.light', + color: 'primary.main', + '&:hover': { bgcolor: 'primary.main', color: 'white' } + }} > {t('chat.newChat')} @@ -621,10 +627,15 @@ export const AIChatInterface: React.FC = () => { label={question} onClick={() => handleSuggestedQuestion(question)} sx={{ - borderRadius: 3, + py: 2, + borderRadius: 2, + fontSize: '0.875rem', + bgcolor: 'background.paper', + border: 1, + borderColor: 'divider', '&:hover': { bgcolor: 'primary.light', - color: 'white', + borderColor: 'primary.main', }, }} /> @@ -776,6 +787,9 @@ export const AIChatInterface: React.FC = () => { sx={{ '& .MuiOutlinedInput-root': { borderRadius: 3, + bgcolor: 'background.default', + '&:hover fieldset': { borderColor: 'primary.main' }, + '&.Mui-focused fieldset': { borderColor: 'primary.main' } }, }} /> diff --git a/maternal-web/components/features/analytics/InsightsDashboard.tsx b/maternal-web/components/features/analytics/InsightsDashboard.tsx index 0f09786..21f2db2 100644 --- a/maternal-web/components/features/analytics/InsightsDashboard.tsx +++ b/maternal-web/components/features/analytics/InsightsDashboard.tsx @@ -68,11 +68,11 @@ interface ActivityTypeData { } const COLORS = { - feeding: '#FFB6C1', - sleep: '#B6D7FF', - diaper: '#FFE4B5', - medication: '#D4B5FF', - milestone: '#B5FFD4', + feeding: '#E91E63', + sleep: '#1976D2', + diaper: '#F57C00', + medication: '#C62828', + milestone: '#558B2F', note: '#FFD3B6', wet: '#87CEEB', dirty: '#D2691E', @@ -269,50 +269,49 @@ export const InsightsDashboard: React.FC = () => { animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5 }} > - - + + {t('title')} - + {t('subtitle')} - {/* Filters */} - - - {children.length > 1 && ( - - - {t('filters.child')} - - - - )} - - newValue && setDateRange(newValue)} - fullWidth - size="large" + {/* Time period selector */} + + {children.length > 1 && ( + + {t('filters.child')} + + + )} + newValue && setDateRange(newValue)} + sx={{ + '& .MuiToggleButton-root': { + textTransform: 'none', + fontWeight: 500, + minWidth: { xs: 80, sm: 120 } + } + }} + > + {t('filters.dateRange.7days')} + {t('filters.dateRange.30days')} + {t('filters.dateRange.3months')} + + {error && ( @@ -355,148 +354,167 @@ export const InsightsDashboard: React.FC = () => { {!loading && !noChildren && !noActivities && ( <> - {/* Summary Statistics */} - - + {/* Stats cards */} + + - - - - - {stats.totalFeedings} - - - {t('stats.feedings.subtitle')} - + + - + + {stats.totalFeedings} + + + {t('stats.feedings.subtitle')} + + - + - - - - - {stats.avgSleepHours}h - - - {t('stats.sleep.subtitle')} - + + - + + {stats.avgSleepHours}h + + + {t('stats.sleep.subtitle')} + + - + - - - - - {stats.totalDiapers} - - - {t('stats.diapers.subtitle')} - + + - + + {stats.totalDiapers} + + + {t('stats.diapers.subtitle')} + + - + - - - - - {t(`activityTypes.${stats.mostCommonType}`)} - - - {t('stats.topActivity.subtitle')} - + + - + + {t(`activityTypes.${stats.mostCommonType}`)} + + + {t('stats.topActivity.subtitle')} + + - {/* Charts */} - - - - - - - - {t('charts.feedingFrequency')} - - + {/* Charts grid */} + + + + + {t('charts.feedingFrequency')} + + @@ -506,19 +524,26 @@ export const InsightsDashboard: React.FC = () => { - - + + - - - - - - - {t('charts.sleepDuration')} - - + + + + {t('charts.sleepDuration')} + + @@ -535,20 +560,27 @@ export const InsightsDashboard: React.FC = () => { /> - - + + {diaperData.length > 0 && ( - - - - - - - {t('charts.diaperChangesByType')} - - + + + + {t('charts.diaperChangesByType')} + + { - - + + )} - 0 ? 6 : 12}> - - - - - - {t('charts.activityTimeline')} - - + 0 ? 6 : 12} sx={{ minWidth: 400 }}> + + + {t('charts.activityTimeline')} + + @@ -594,8 +633,8 @@ export const InsightsDashboard: React.FC = () => { - - + + diff --git a/maternal-web/components/layouts/AppShell/AppShell.tsx b/maternal-web/components/layouts/AppShell/AppShell.tsx index 2fb7851..6dbf5f0 100644 --- a/maternal-web/components/layouts/AppShell/AppShell.tsx +++ b/maternal-web/components/layouts/AppShell/AppShell.tsx @@ -62,20 +62,65 @@ export const AppShell = ({ children }: AppShellProps) => { flexDirection: 'column', minHeight: '100vh', bgcolor: 'background.default', - pb: isMobile ? '64px' : 0, // Space for tab bar + pb: { xs: '64px', md: 0 }, // Space for tab bar on mobile }}> {!isMobile && } - {/* Mobile User Menu Button - Top Left */} + {/* Mobile Header Bar */} {isMobile && ( + {/* Connection Status & Presence Indicator */} + + + : } + label={isConnected ? t('connection.live') : t('connection.offline')} + size="small" + color={isConnected ? 'success' : 'default'} + sx={{ + fontWeight: 600, + }} + /> + + + {isConnected && presence.count > 1 && ( + + } + label={presence.count} + size="small" + color="primary" + sx={{ + fontWeight: 600, + }} + /> + + )} + + + {/* User Menu Button - Top Right */} { aria-controls={anchorEl ? 'user-menu' : undefined} aria-haspopup="true" aria-expanded={anchorEl ? 'true' : undefined} - sx={{ - bgcolor: 'background.paper', - boxShadow: 1, - '&:hover': { - bgcolor: 'background.paper', - boxShadow: 2, - }, - }} > { open={Boolean(anchorEl)} onClose={handleMenuClose} onClick={handleMenuClose} - transformOrigin={{ horizontal: 'left', vertical: 'top' }} - anchorOrigin={{ horizontal: 'left', vertical: 'bottom' }} + transformOrigin={{ horizontal: 'right', vertical: 'top' }} + anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }} sx={{ mt: 1, }} @@ -145,52 +182,55 @@ export const AppShell = ({ children }: AppShellProps) => { )} - {/* Connection Status & Presence Indicator */} - - - : } - label={isConnected ? t('connection.live') : t('connection.offline')} - size="small" - color={isConnected ? 'success' : 'default'} - sx={{ - fontWeight: 600, - boxShadow: 1, - }} - /> - - - {isConnected && presence.count > 1 && ( - + {/* Connection Status & Presence Indicator - Desktop Only */} + {!isMobile && ( + + } - label={presence.count} + icon={isConnected ? : } + label={isConnected ? t('connection.live') : t('connection.offline')} size="small" - color="primary" + color={isConnected ? 'success' : 'default'} sx={{ fontWeight: 600, boxShadow: 1, }} /> - )} - + + {isConnected && presence.count > 1 && ( + + } + label={presence.count} + size="small" + color="primary" + sx={{ + fontWeight: 600, + boxShadow: 1, + }} + /> + + )} + + )} {children}