Files
maternal-app/maternal-web/app/history/page.tsx
Andrei b56f9546c2 feat: Complete high-priority i18n localization with date/time support
This commit implements comprehensive localization for high-priority components:

## Tracking Pages (4 files)
- Localized feeding, sleep, diaper, and medicine tracking pages
- Replaced hardcoded strings with translation keys from tracking namespace
- Added useTranslation hook integration
- All form labels, buttons, and messages now support multiple languages

## Child Dialog Components (2 files)
- Localized ChildDialog (add/edit child form)
- Localized DeleteConfirmDialog
- Added new translation keys to children.json for dialog content
- Includes validation messages and action buttons

## Date/Time Localization (14 files + new hook)
- Created useLocalizedDate hook wrapping date-fns with locale support
- Supports 5 languages: English, Spanish, French, Portuguese, Chinese
- Updated all date formatting across:
  * Tracking pages (feeding, sleep, diaper, medicine)
  * Activity pages (activities, history, track activity)
  * Settings components (sessions, biometric, device trust)
  * Analytics components (insights, growth, sleep chart, feeding graph)
- Date displays automatically adapt to user's language (e.g., "2 hours ago" → "hace 2 horas")

## Translation Updates
- Enhanced children.json with dialog section containing:
  * Form field labels (name, birthDate, gender, photoUrl)
  * Action buttons (add, update, delete, cancel, saving, deleting)
  * Delete confirmation messages
  * Validation error messages

Files changed: 17 files (+164, -113)
Languages supported: en, es, fr, pt-BR, zh-CN

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-03 11:49:48 +00:00

222 lines
7.2 KiB
TypeScript

'use client';
import { useState } from 'react';
import {
Box,
Typography,
Paper,
List,
ListItem,
ListItemAvatar,
ListItemText,
Avatar,
Chip,
IconButton,
Tabs,
Tab,
Button,
} from '@mui/material';
import {
Restaurant,
Hotel,
BabyChangingStation,
Delete,
Edit,
FilterList,
} from '@mui/icons-material';
import { AppShell } from '@/components/layouts/AppShell/AppShell';
import { ProtectedRoute } from '@/components/common/ProtectedRoute';
import { motion } from 'framer-motion';
import { useLocalizedDate } from '@/hooks/useLocalizedDate';
// Mock data - will be replaced with API calls
const mockActivities = [
{
id: '1',
type: 'feeding',
timestamp: new Date(Date.now() - 2 * 60 * 60 * 1000),
details: 'Breast feeding - Left, 15 minutes',
icon: <Restaurant />,
color: '#FFB6C1',
},
{
id: '2',
type: 'diaper',
timestamp: new Date(Date.now() - 3 * 60 * 60 * 1000),
details: 'Diaper change - Wet',
icon: <BabyChangingStation />,
color: '#FFE4B5',
},
{
id: '3',
type: 'sleep',
timestamp: new Date(Date.now() - 5 * 60 * 60 * 1000),
details: 'Sleep - 2h 30m, Good quality',
icon: <Hotel />,
color: '#B6D7FF',
},
{
id: '4',
type: 'feeding',
timestamp: new Date(Date.now() - 6 * 60 * 60 * 1000),
details: 'Bottle - 120ml',
icon: <Restaurant />,
color: '#FFB6C1',
},
{
id: '5',
type: 'diaper',
timestamp: new Date(Date.now() - 7 * 60 * 60 * 1000),
details: 'Diaper change - Both',
icon: <BabyChangingStation />,
color: '#FFE4B5',
},
];
export default function HistoryPage() {
const { formatDistanceToNow } = useLocalizedDate();
const [filter, setFilter] = useState<string>('all');
const [activities, setActivities] = useState(mockActivities);
const filteredActivities =
filter === 'all'
? activities
: activities.filter((activity) => activity.type === filter);
const handleDelete = (id: string) => {
// TODO: Call API to delete activity
setActivities(activities.filter((activity) => activity.id !== id));
};
return (
<ProtectedRoute>
<AppShell>
<Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
<Typography variant="h4" fontWeight="600">
Activity History
</Typography>
<IconButton>
<FilterList />
</IconButton>
</Box>
{/* Filter Tabs */}
<Paper sx={{ mb: 3 }}>
<Tabs
value={filter}
onChange={(_, newValue) => setFilter(newValue)}
variant="scrollable"
scrollButtons="auto"
>
<Tab label="All" value="all" />
<Tab label="Feeding" value="feeding" icon={<Restaurant />} iconPosition="start" />
<Tab label="Sleep" value="sleep" icon={<Hotel />} iconPosition="start" />
<Tab label="Diaper" value="diaper" icon={<BabyChangingStation />} iconPosition="start" />
</Tabs>
</Paper>
{/* Activity Timeline */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.3 }}
>
<Paper>
<List>
{filteredActivities.length === 0 ? (
<Box sx={{ p: 4, textAlign: 'center' }}>
<Typography variant="body1" color="text.secondary">
No activities found
</Typography>
</Box>
) : (
filteredActivities.map((activity, index) => (
<motion.div
key={activity.id}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.3, delay: index * 0.05 }}
>
<ListItem
sx={{
borderBottom: index < filteredActivities.length - 1 ? '1px solid' : 'none',
borderColor: 'divider',
py: 2,
}}
secondaryAction={
<Box>
<IconButton edge="end" aria-label="edit" sx={{ mr: 1 }}>
<Edit />
</IconButton>
<IconButton
edge="end"
aria-label="delete"
onClick={() => handleDelete(activity.id)}
>
<Delete />
</IconButton>
</Box>
}
>
<ListItemAvatar>
<Avatar sx={{ bgcolor: activity.color }}>
{activity.icon}
</Avatar>
</ListItemAvatar>
<ListItemText
primary={activity.details}
secondary={
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mt: 0.5 }}>
<Typography variant="caption" color="text.secondary">
{formatDistanceToNow(activity.timestamp, { addSuffix: true })}
</Typography>
<Chip
label={activity.type}
size="small"
sx={{
height: 20,
fontSize: '0.7rem',
textTransform: 'capitalize',
}}
/>
</Box>
}
/>
</ListItem>
</motion.div>
))
)}
</List>
</Paper>
</motion.div>
{/* Daily Summary */}
<Paper sx={{ p: 3, mt: 3 }}>
<Typography variant="h6" fontWeight="600" gutterBottom>
Today's Summary
</Typography>
<Box sx={{ display: 'flex', gap: 2, flexWrap: 'wrap', mt: 2 }}>
<Chip
icon={<Restaurant />}
label={`${activities.filter((a) => a.type === 'feeding').length} Feedings`}
sx={{ bgcolor: '#FFB6C1', color: 'white' }}
/>
<Chip
icon={<Hotel />}
label={`${activities.filter((a) => a.type === 'sleep').length} Sleep Sessions`}
sx={{ bgcolor: '#B6D7FF', color: 'white' }}
/>
<Chip
icon={<BabyChangingStation />}
label={`${activities.filter((a) => a.type === 'diaper').length} Diaper Changes`}
sx={{ bgcolor: '#FFE4B5', color: 'white' }}
/>
</Box>
</Paper>
</Box>
</AppShell>
</ProtectedRoute>
);
}