feat: Integrate ChildSelector component in feeding tracking form
- Replace local child selector with shared ChildSelector component - Use Redux for children state management - Sync selected child with Redux store - Remove duplicate child loading logic - Maintain single child selection for feeding tracking - Build and test successfully
This commit is contained in:
@@ -56,6 +56,10 @@ import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { UnitInput } from '@/components/forms/UnitInput';
|
||||
import { convertVolume, getUnitSymbol } from '@/lib/utils/unitConversion';
|
||||
import { MeasurementSystem } from '@/hooks/useLocale';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { fetchChildren, selectChild, selectSelectedChild, childrenSelectors } from '@/store/slices/childrenSlice';
|
||||
import { AppDispatch, RootState } from '@/store/store';
|
||||
import ChildSelector from '@/components/common/ChildSelector';
|
||||
|
||||
interface FeedingData {
|
||||
feedingType: 'breast' | 'bottle' | 'solid';
|
||||
@@ -72,8 +76,15 @@ function FeedingTrackPage() {
|
||||
const { user } = useAuth();
|
||||
const { t } = useTranslation('tracking');
|
||||
const { formatDistanceToNow } = useLocalizedDate();
|
||||
const [children, setChildren] = useState<Child[]>([]);
|
||||
const [selectedChild, setSelectedChild] = useState<string>('');
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
|
||||
// Redux state
|
||||
const children = useSelector((state: RootState) => childrenSelectors.selectAll(state));
|
||||
const selectedChild = useSelector(selectSelectedChild);
|
||||
const familyId = useSelector((state: RootState) => state.auth.user?.familyId);
|
||||
|
||||
// Local state
|
||||
const [selectedChildIds, setSelectedChildIds] = useState<string[]>([]);
|
||||
const [feedingType, setFeedingType] = useState<'breast' | 'bottle' | 'solid'>('breast');
|
||||
|
||||
// Breastfeeding state
|
||||
@@ -94,7 +105,6 @@ function FeedingTrackPage() {
|
||||
const [notes, setNotes] = useState<string>('');
|
||||
const [recentFeedings, setRecentFeedings] = useState<Activity[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [childrenLoading, setChildrenLoading] = useState(true);
|
||||
const [feedingsLoading, setFeedingsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [successMessage, setSuccessMessage] = useState<string | null>(null);
|
||||
@@ -103,21 +113,26 @@ function FeedingTrackPage() {
|
||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||
const [activityToDelete, setActivityToDelete] = useState<string | null>(null);
|
||||
|
||||
const familyId = user?.families?.[0]?.familyId;
|
||||
|
||||
// Load children
|
||||
// Load children from Redux
|
||||
useEffect(() => {
|
||||
if (familyId) {
|
||||
loadChildren();
|
||||
if (familyId && children.length === 0) {
|
||||
dispatch(fetchChildren(familyId));
|
||||
}
|
||||
}, [familyId]);
|
||||
}, [familyId, dispatch, children.length]);
|
||||
|
||||
// Sync selectedChildIds with Redux selectedChild
|
||||
useEffect(() => {
|
||||
if (selectedChild?.id) {
|
||||
setSelectedChildIds([selectedChild.id]);
|
||||
}
|
||||
}, [selectedChild]);
|
||||
|
||||
// Load recent feedings when child is selected
|
||||
useEffect(() => {
|
||||
if (selectedChild) {
|
||||
if (selectedChild?.id) {
|
||||
loadRecentFeedings();
|
||||
}
|
||||
}, [selectedChild]);
|
||||
}, [selectedChild?.id]);
|
||||
|
||||
// Timer effect
|
||||
useEffect(() => {
|
||||
@@ -130,30 +145,12 @@ function FeedingTrackPage() {
|
||||
return () => clearInterval(interval);
|
||||
}, [isTimerRunning]);
|
||||
|
||||
const loadChildren = async () => {
|
||||
if (!familyId) return;
|
||||
|
||||
try {
|
||||
setChildrenLoading(true);
|
||||
const childrenData = await childrenApi.getChildren(familyId);
|
||||
setChildren(childrenData);
|
||||
if (childrenData.length > 0) {
|
||||
setSelectedChild(childrenData[0].id);
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('Failed to load children:', err);
|
||||
setError(err.response?.data?.message || t('common.error.loadChildrenFailed'));
|
||||
} finally {
|
||||
setChildrenLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const loadRecentFeedings = async () => {
|
||||
if (!selectedChild) return;
|
||||
if (!selectedChild?.id) return;
|
||||
|
||||
try {
|
||||
setFeedingsLoading(true);
|
||||
const activities = await trackingApi.getActivities(selectedChild, 'feeding');
|
||||
const activities = await trackingApi.getActivities(selectedChild.id, 'feeding');
|
||||
// Sort by timestamp descending and take last 10
|
||||
const sorted = activities.sort((a, b) =>
|
||||
new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
|
||||
@@ -188,7 +185,7 @@ function FeedingTrackPage() {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!selectedChild) {
|
||||
if (!selectedChild?.id) {
|
||||
setError(t('common.selectChild'));
|
||||
return;
|
||||
}
|
||||
@@ -228,7 +225,7 @@ function FeedingTrackPage() {
|
||||
data.amountDescription = amountDescription;
|
||||
}
|
||||
|
||||
await trackingApi.createActivity(selectedChild, {
|
||||
await trackingApi.createActivity(selectedChild.id, {
|
||||
type: 'feeding',
|
||||
timestamp: new Date().toISOString(),
|
||||
data,
|
||||
@@ -316,7 +313,9 @@ function FeedingTrackPage() {
|
||||
return '';
|
||||
};
|
||||
|
||||
if (childrenLoading) {
|
||||
const childrenLoading = useSelector((state: RootState) => state.children.loading);
|
||||
|
||||
if (childrenLoading && children.length === 0) {
|
||||
return (
|
||||
<ProtectedRoute>
|
||||
<AppShell>
|
||||
@@ -411,27 +410,21 @@ function FeedingTrackPage() {
|
||||
transition={{ duration: 0.3 }}
|
||||
>
|
||||
{/* Child Selector */}
|
||||
{children.length > 1 && (
|
||||
{children.length > 0 && (
|
||||
<Paper sx={{ p: 2, mb: 3 }}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel id="child-select-label">{t('common.selectChild')}</InputLabel>
|
||||
<Select
|
||||
labelId="child-select-label"
|
||||
value={selectedChild}
|
||||
onChange={(e) => setSelectedChild(e.target.value)}
|
||||
label={t('common.selectChild')}
|
||||
required
|
||||
inputProps={{
|
||||
'aria-required': 'true',
|
||||
}}
|
||||
>
|
||||
{children.map((child) => (
|
||||
<MenuItem key={child.id} value={child.id}>
|
||||
{child.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<ChildSelector
|
||||
children={children}
|
||||
selectedChildIds={selectedChildIds}
|
||||
onChange={(childIds) => {
|
||||
setSelectedChildIds(childIds);
|
||||
if (childIds.length > 0) {
|
||||
dispatch(selectChild(childIds[0]));
|
||||
}
|
||||
}}
|
||||
mode="single"
|
||||
label={t('common.selectChild')}
|
||||
required
|
||||
/>
|
||||
</Paper>
|
||||
)}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user