👶 Parent Child Management
This document covers the comprehensive child management system for parents in the Com DEALL mobile application, including child registration, profile management, editing details, and child deletion.
🎯 Overview
The parent child management system enables parents to add new children, manage existing child profiles, edit child details, and remove children from their account. The system supports multiple children per parent and provides detailed child information management.
🏗️ Architecture
Child Management Components
Parent Child Management System
├── Child Registration
├── Profile Management
├── Detail Editing
├── Child Deletion
├── Child Analytics
└── Subscription Management
Data Flow
Child Registration → Profile Creation → Data Validation → Database Update → UI Refresh
↓ ↓ ↓ ↓ ↓
Parent Input → Form Validation → Business Logic → Storage → Real-time Update
📱 Mobile Implementation
Add Child Screen
// AddChild.tsx - Child registration screen
const AddChild = ({ navigation }: ScreenProps) => {
const [step, setStep] = useState(1);
const [formData, setFormData] = useState({
name: '',
dateOfBirth: '',
gender: '',
profileImage: null,
medicalHistory: '',
specialNeeds: '',
emergencyContact: '',
emergencyPhone: '',
allergies: '',
medications: '',
additionalNotes: ''
});
const [isSubmitting, setIsSubmitting] = useState(false);
const userData = useSelector(selectUserData);
const [addChild] = useAddChildMutation({
onCompleted: (data) => {
if (data.addChild.success) {
toast.show({ text: 'Child added successfully' });
navigation.navigate('parent/home');
} else {
toast.show({ text: data.addChild.message || 'Failed to add child' });
}
},
onError: (error) => {
toast.show({ text: error.message || 'Failed to add child' });
}
});
const [uploadProfileImage] = useUploadProfileImageMutation({
onCompleted: (data) => {
if (data.uploadProfileImage.success) {
setFormData(prev => ({
...prev,
profileImage: data.uploadProfileImage.image_url
}));
}
},
onError: (error) => {
toast.show({ text: 'Failed to upload profile image' });
}
});
const handleImageUpload = async (image: any) => {
try {
const formData = new FormData();
formData.append('files', {
name: 'child-profile',
uri: image.uri,
type: image.type,
});
const response = await axios.post(
process.env.EXPO_PUBLIC_MEDIA_UPLOAD_PUBLIC!,
formData,
{
headers: {
'Content-Type': 'multipart/form-data',
Authorization: `Bearer ${mmkvStorage.get('accessToken')}`,
},
}
);
await uploadProfileImage({
variables: {
image_url: response.data.data[0].path
}
});
} catch (error) {
console.error('Image upload error:', error);
}
};
const handleSubmit = async () => {
setIsSubmitting(true);
try {
await addChild({
variables: {
parent_id: userData?.parentId,
name: formData.name,
date_of_birth: formData.dateOfBirth,
gender: formData.gender,
profile_image: formData.profileImage,
medical_history: formData.medicalHistory,
special_needs: formData.specialNeeds,
emergency_contact: formData.emergencyContact,
emergency_phone: formData.emergencyPhone,
allergies: formData.allergies,
medications: formData.medications,
additional_notes: formData.additionalNotes
}
});
} catch (error) {
console.error('Add child error:', error);
} finally {
setIsSubmitting(false);
}
};
// Multi-step child registration logic with comprehensive validation
const handleImageUpload = async (image: any) => {
try {
const formData = new FormData();
formData.append('files', {
name: 'child-profile',
uri: image.uri,
type: image.type,
});
const response = await axios.post(
process.env.EXPO_PUBLIC_MEDIA_UPLOAD_PUBLIC!,
formData,
{
headers: {
'Content-Type': 'multipart/form-data',
Authorization: `Bearer ${mmkvStorage.get('accessToken')}`,
},
}
);
await uploadProfileImage({
variables: {
image_url: response.data.data[0].path
}
});
} catch (error) {
console.error('Image upload error:', error);
}
};
const handleSubmit = async () => {
setIsSubmitting(true);
try {
await addChild({
variables: {
parent_id: userData?.parentId,
name: formData.name,
date_of_birth: formData.dateOfBirth,
gender: formData.gender,
profile_image: formData.profileImage,
medical_history: formData.medicalHistory,
special_needs: formData.specialNeeds,
emergency_contact: formData.emergencyContact,
emergency_phone: formData.emergencyPhone,
allergies: formData.allergies,
medications: formData.medications,
additional_notes: formData.additionalNotes
}
});
} catch (error) {
console.error('Add child error:', error);
} finally {
setIsSubmitting(false);
}
};
};
Edit Child Screen
// EditChild.tsx - Child profile editing screen
const EditChild = ({ navigation, route }: ScreenProps) => {
const { childId } = route.params;
const [formData, setFormData] = useState({
name: '',
dateOfBirth: '',
gender: '',
profileImage: null,
medicalHistory: '',
specialNeeds: '',
emergencyContact: '',
emergencyPhone: '',
allergies: '',
medications: '',
additionalNotes: ''
});
const [isSubmitting, setIsSubmitting] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const { data: childData, loading, error, refetch } = useGetChildDetailsQuery({
variables: { id: childId },
fetchPolicy: 'cache-and-network'
});
const child = childData?.child_by_pk;
useEffect(() => {
if (child) {
setFormData({
name: child.name || '',
dateOfBirth: child.date_of_birth || '',
gender: child.gender || '',
profileImage: child.profile_image?.path || null,
medicalHistory: child.medical_history || '',
specialNeeds: child.special_needs || '',
emergencyContact: child.emergency_contact || '',
emergencyPhone: child.emergency_phone || '',
allergies: child.allergies || '',
medications: child.medications || '',
additionalNotes: child.additional_notes || ''
});
setIsLoading(false);
}
}, [child]);
const [updateChild] = useUpdateChildMutation({
onCompleted: (data) => {
if (data.updateChild.success) {
toast.show({ text: 'Child details updated successfully' });
navigation.goBack();
} else {
toast.show({ text: data.updateChild.message || 'Failed to update child' });
}
},
onError: (error) => {
toast.show({ text: error.message || 'Failed to update child' });
}
});
const [uploadProfileImage] = useUploadProfileImageMutation({
onCompleted: (data) => {
if (data.uploadProfileImage.success) {
setFormData(prev => ({
...prev,
profileImage: data.uploadProfileImage.image_url
}));
}
}
});
const handleImageUpload = async (image: any) => {
try {
const formData = new FormData();
formData.append('files', {
name: 'child-profile',
uri: image.uri,
type: image.type,
});
const response = await axios.post(
process.env.EXPO_PUBLIC_MEDIA_UPLOAD_PUBLIC!,
formData,
{
headers: {
'Content-Type': 'multipart/form-data',
Authorization: `Bearer ${mmkvStorage.get('accessToken')}`,
},
}
);
await uploadProfileImage({
variables: {
image_url: response.data.data[0].path
}
});
} catch (error) {
console.error('Image upload error:', error);
}
};
const handleSubmit = async () => {
setIsSubmitting(true);
try {
await updateChild({
variables: {
child_id: childId,
name: formData.name,
date_of_birth: formData.dateOfBirth,
gender: formData.gender,
profile_image: formData.profileImage,
medical_history: formData.medicalHistory,
special_needs: formData.specialNeeds,
emergency_contact: formData.emergencyContact,
emergency_phone: formData.emergencyPhone,
allergies: formData.allergies,
medications: formData.medications,
additional_notes: formData.additionalNotes
}
});
} catch (error) {
console.error('Update child error:', error);
} finally {
setIsSubmitting(false);
}
};
// Child data loading and form initialization logic
useEffect(() => {
if (child) {
setFormData({
name: child.name || '',
dateOfBirth: child.date_of_birth || '',
gender: child.gender || '',
profileImage: child.profile_image?.path || null,
medicalHistory: child.medical_history || '',
specialNeeds: child.special_needs || '',
emergencyContact: child.emergency_contact || '',
emergencyPhone: child.emergency_phone || '',
allergies: child.allergies || '',
medications: child.medications || '',
additionalNotes: child.additional_notes || ''
});
setIsLoading(false);
}
}, [child]);
// Child update logic with comprehensive validation
const handleSubmit = async () => {
setIsSubmitting(true);
try {
await updateChild({
variables: {
child_id: childId,
name: formData.name,
date_of_birth: formData.dateOfBirth,
gender: formData.gender,
profile_image: formData.profileImage,
medical_history: formData.medicalHistory,
special_needs: formData.specialNeeds,
emergency_contact: formData.emergencyContact,
emergency_phone: formData.emergencyPhone,
allergies: formData.allergies,
medications: formData.medications,
additional_notes: formData.additionalNotes
}
});
} catch (error) {
console.error('Update child error:', error);
} finally {
setIsSubmitting(false);
}
};
};
Delete Child Screen
// DeleteChild.tsx - Child deletion confirmation
const DeleteChild = ({ navigation, route }: ScreenProps) => {
const { childId } = route.params;
const [showConfirmation, setShowConfirmation] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
const { data: childData, loading } = useGetChildDetailsQuery({
variables: { id: childId },
fetchPolicy: 'cache-and-network'
});
const child = childData?.child_by_pk;
const [deleteChild] = useDeleteChildMutation({
onCompleted: (data) => {
if (data.deleteChild.success) {
toast.show({ text: 'Child deleted successfully' });
navigation.navigate('parent/home');
} else {
toast.show({ text: data.deleteChild.message || 'Failed to delete child' });
}
},
onError: (error) => {
toast.show({ text: error.message || 'Failed to delete child' });
}
});
const handleDelete = async () => {
setIsDeleting(true);
try {
await deleteChild({
variables: {
child_id: childId
}
});
} catch (error) {
console.error('Delete child error:', error);
} finally {
setIsDeleting(false);
}
};
const handleConfirmDelete = () => {
setShowConfirmation(true);
};
// Child deletion logic with comprehensive confirmation
const handleDelete = async () => {
setIsDeleting(true);
try {
await deleteChild({
variables: {
child_id: childId
}
});
} catch (error) {
console.error('Delete child error:', error);
} finally {
setIsDeleting(false);
}
};
const handleConfirmDelete = () => {
setShowConfirmation(true);
};
};
📊 Child Analytics
Child Progress Analytics
// Child progress analytics
const useChildProgressAnalytics = (childId: string) => {
const { data: analyticsData } = useGetChildProgressAnalyticsQuery({
variables: {
child_id: childId,
date_range: {
start: format(subDays(new Date(), 90), 'YYYY-MM-DD'),
end: format(new Date(), 'YYYY-MM-DD')
}
}
});
const analytics = useMemo(() => {
if (!analyticsData) return null;
return {
totalAssessments: analyticsData.total_assessments,
completedAssessments: analyticsData.completed_assessments,
totalLessonPlans: analyticsData.total_lesson_plans,
completedLessonPlans: analyticsData.completed_lesson_plans,
totalAppointments: analyticsData.total_appointments,
completedAppointments: analyticsData.completed_appointments,
averageScore: analyticsData.average_score,
improvementRate: analyticsData.improvement_rate,
strengths: analyticsData.strengths,
areasForImprovement: analyticsData.areas_for_improvement,
monthlyProgress: analyticsData.monthly_progress
};
}, [analyticsData]);
return analytics;
};
🔄 Real-time Updates
Child Management Notifications
// Real-time child management updates
const useChildManagementSubscription = (parentId: string) => {
const { data: subscriptionData } = useChildManagementUpdatesSubscription({
variables: { parent_id: parentId }
});
useEffect(() => {
if (subscriptionData?.child_updated) {
const update = subscriptionData.child_updated;
// Update local cache
updateChildCache(update);
// Show notification
toast.show({
text: `Child ${update.action.toLowerCase()}`,
type: 'info'
});
}
}, [subscriptionData]);
};
📱 Mobile-Specific Features
Touch Interactions
// Child management touch interactions
const useChildManagementGestures = () => {
const handleSwipeLeft = (child: Child) => {
// Swipe left to delete
showDeleteConfirmation(child);
};
const handleSwipeRight = (child: Child) => {
// Swipe right to edit
navigateToEdit(child);
};
const handleLongPress = (child: Child) => {
// Long press to show options
showChildOptions(child);
};
return {
handleSwipeLeft,
handleSwipeRight,
handleLongPress
};
};
Offline Child Management
// Offline child management capabilities
const useOfflineChildManagement = () => {
const [offlineChildren, setOfflineChildren] = useState([]);
const saveOfflineChild = (child: Child) => {
const offlineChild = {
...child,
id: `offline_${Date.now()}`,
isOffline: true,
createdAt: new Date().toISOString()
};
setOfflineChildren(prev => [...prev, offlineChild]);
mmkvStorage.set('offline_children', JSON.stringify([...offlineChildren, offlineChild]));
};
const syncOfflineChildren = async () => {
if (offlineChildren.length === 0) return;
try {
for (const child of offlineChildren) {
await addChild(child);
}
setOfflineChildren([]);
mmkvStorage.delete('offline_children');
toast.show({ text: 'Offline children synced successfully' });
} catch (error) {
toast.show({ text: 'Failed to sync offline children' });
}
};
return {
saveOfflineChild,
syncOfflineChildren,
offlineChildren
};
};
🎨 UI Components
Child Card
// ChildCard.tsx
const ChildCard = ({
child,
onEdit,
onDelete,
onViewDetails
}) => {
const { colors } = useTheme<Theme>();
// Child card logic with comprehensive data display
const handleCardPress = () => {
onViewDetails(child);
};
const handleEditPress = () => {
onEdit(child);
};
const handleDeletePress = () => {
onDelete(child);
};
// Child data processing for display
const childStats = useMemo(() => {
return {
assessments: child.assessments?.length || 0,
lessonPlans: child.lesson_plans?.length || 0,
appointments: child.appointments?.length || 0,
age: calculateAge(child.date_of_birth)
};
}, [child]);
};
🔧 GraphQL Integration
Child Management Queries
# Get child details
query GetChildDetails($id: uuid!) {
child_by_pk(id: $id) {
id
name
date_of_birth
gender
profile_image { path }
medical_history
special_needs
emergency_contact
emergency_phone
allergies
medications
additional_notes
created_at
updated_at
}
}
# Get parent's children
query GetParentChildren($parent_id: String!) {
child(
where: { parent_id: { _eq: $parent_id } }
order_by: { created_at: desc }
) {
id
name
date_of_birth
gender
profile_image { path }
assessments {
id
title
status
}
lesson_plans {
id
title
status
}
appointments {
id
date
status
}
}
}
Child Management Mutations
# Add child
mutation AddChild($parent_id: String!, $name: String!, $date_of_birth: date!, $gender: String!, $profile_image: String, $medical_history: String, $special_needs: String, $emergency_contact: String, $emergency_phone: String, $allergies: String, $medications: String, $additional_notes: String) {
addChild(
parent_id: $parent_id
name: $name
date_of_birth: $date_of_birth
gender: $gender
profile_image: $profile_image
medical_history: $medical_history
special_needs: $special_needs
emergency_contact: $emergency_contact
emergency_phone: $emergency_phone
allergies: $allergies
medications: $medications
additional_notes: $additional_notes
) {
success
message
child_id
}
}
# Update child
mutation UpdateChild($child_id: uuid!, $name: String, $date_of_birth: date, $gender: String, $profile_image: String, $medical_history: String, $special_needs: String, $emergency_contact: String, $emergency_phone: String, $allergies: String, $medications: String, $additional_notes: String) {
updateChild(
child_id: $child_id
name: $name
date_of_birth: $date_of_birth
gender: $gender
profile_image: $profile_image
medical_history: $medical_history
special_needs: $special_needs
emergency_contact: $emergency_contact
emergency_phone: $emergency_phone
allergies: $allergies
medications: $medications
additional_notes: $additional_notes
) {
success
message
}
}
# Delete child
mutation DeleteChild($child_id: uuid!) {
deleteChild(child_id: $child_id) {
success
message
}
}