Skip to main content

👶 Child Management & Assignments

This document covers the comprehensive child management system for therapists in the Com DEALL mobile application, including child assignment, assessment management, lesson plan assignments, and progress tracking.

🎯 Overview

The child management system enables therapists to manage their assigned children, assign assessments and lesson plans, track progress, and maintain detailed child records. The system supports age-based filtering, assignment workflows, and real-time progress updates.

🏗️ Architecture

Child Management Components

Child Management System
├── Child List & Filtering
├── Child Details & Profile
├── Assessment Assignment
├── Lesson Plan Assignment
├── Progress Tracking
└── Child Analytics

Data Flow

Child Assignment → Assessment/Lesson Plan → Progress Tracking → Analytics → Reports
↓ ↓ ↓ ↓ ↓
Therapist Action → Content Assignment → Data Collection → Insights → Documentation

📱 Mobile Implementation

Child List Screen Logic

Child List Management Process:

  • System fetches all children assigned to the current therapist
  • Implements real-time filtering based on age groups and date ranges
  • Provides search functionality for quick child lookup
  • Handles loading states and error scenarios gracefully
  • Supports pull-to-refresh for data updates

Filter Implementation Logic:

  • Age Group Filtering: Filters children by predefined age ranges (0-2, 3-5, 6-8, 9+)
  • Date Range Filtering: Filters based on appointment dates within specified range
  • Combined Filtering: Applies both age and date filters simultaneously
  • Filter State Management: Maintains filter state across component lifecycle
  • Real-time Updates: Filters update immediately when criteria change

Data Processing Logic:

  • Child Assignment: Fetches children assigned to current therapist
  • Age Calculation: Calculates child age for filtering purposes
  • Date Processing: Converts appointment dates for range filtering
  • Filter Application: Applies multiple filter criteria with AND logic
  • Result Optimization: Optimizes filtered results for performance
// ChildList.tsx - Comprehensive child management with advanced filtering
const ChildList = ({ navigation }: Props) => {
const [filter, setFilter] = useState<FilterData>(filterDefaultState);
const [modalVisible, setModalVisible] = useState(false);
const { therapistId } = useSelector(selectUserData);

const { loading, error, data, refetch } = useTherapistChildQuery({
fetchPolicy: 'cache-and-network',
variables: { therapist_id: therapistId },
});

const [childList, setChildList] = useState<
TherapistChildQuery['child_therapist'] | undefined
>(data?.child_therapist);

// Advanced filtering logic with age and date range support
useEffect(() => {
if (
filter.ageGroups.length === 0 &&
(!filter.dateRange.from || !filter.dateRange.upto)
) {
setChildList(data?.child_therapist);
} else {
const filteredData: Child_Therapist[] = [];

data?.child_therapist!.forEach(item => {
const isAgeInRange = filter.ageGroups.some(
range =>
item.child?.age >= range.minimum &&
item.child?.age <= range.maximum,
);

const fromDate = filter.dateRange.from
? new Date(filter.dateRange.from)
: null;
const uptoDate = filter.dateRange.upto
? new Date(filter.dateRange.upto)
: null;
const itemDate = item.child?.appointments[0]?.date
? new Date(item.child?.appointments[0]?.date)
: null;

const isDateInRange =
(!fromDate || itemDate >= fromDate) &&
(!uptoDate || itemDate <= uptoDate);

if (isAgeInRange && isDateInRange) {
filteredData.push(item);
}
});

setChildList(filteredData);
}
}, [filter, setChildList, data]);

const hasChildData = useMemo(() => {
return (childList?.length ?? 0) > 0;
}, [childList]);
};

Child Details Screen Logic

Child Details Management Process:

  • Fetches comprehensive child information including personal details, assessments, and progress
  • Implements tabbed interface for organized information display
  • Provides navigation to edit child details and manage assignments
  • Handles loading states and error scenarios for child data
  • Supports real-time updates when child information changes

Tab Navigation Logic:

  • Overview Tab: Displays child's basic information, age, and key statistics
  • Assessments Tab: Shows assigned and completed assessments with progress tracking
  • Lesson Plans Tab: Displays assigned lesson plans and completion status
  • Progress Tab: Shows detailed progress analytics and development milestones

Data Fetching Strategy:

  • Child Details: Fetches complete child profile information
  • Assessment Data: Loads assessment history and assignments
  • Lesson Plan Data: Retrieves lesson plan assignments and progress
  • Progress Analytics: Calculates and displays development metrics
  • Real-time Updates: Refreshes data when assignments change
// ChildDetails.tsx - Comprehensive child details with tabbed interface
const ChildDetails = ({ route, navigation }: ScreenProps) => {
const { childId } = route.params;
const [activeTab, setActiveTab] = useState(0);

const { data: childData, loading, error, refetch } = useGetChildDetailsQuery({
variables: { id: childId },
fetchPolicy: 'cache-and-network'
});

const child = childData?.child_by_pk;

const tabs = [
{ key: 'overview', title: 'Overview', component: ChildOverview },
{ key: 'assessments', title: 'Assessments', component: ChildAssessments },
{ key: 'lesson-plans', title: 'Lesson Plans', component: ChildLessonPlans },
{ key: 'progress', title: 'Progress', component: ChildProgress }
];
};

📊 Assessment Assignment

Assign Assessments Screen

// AssignAssessments.tsx - Assessment assignment screen
const AssignAssessments = ({ navigation, route }: ScreenProps) => {
const { childId } = route.params;
const [activeTab, setActiveTab] = useState(0);
const [modalVisible, setModalVisible] = useState(false);
const [filterModalVisible, setFilterModalVisible] = useState(false);

const { therapistId } = useSelector(selectUserData);
const toast = useToast();

const { data: childDetails } = useGetChildPersonalDetailsQuery({
variables: { id: childId }
});

const childAge = calculateAgeInMonths(childDetails?.child_by_pk?.dob);

// Get available assessments
const {
data: assessmentsData,
loading: assessmentsLoading,
error,
refetch,
} = useGetTherapistChildAllAssessmentsQuery({
variables: {
child_id: childId,
filter_type: AllAssessmentsFilterType.NotAssigned,
limit: 50,
page: 1,
},
fetchPolicy: 'cache-and-network',
});

// Get assigned assessments
const {
data: assignedData,
loading: assignedLoading,
error: assignedError,
refetch: refetchAssigned,
} = useGetAssignedAssessmentTherapistQuery({
variables: {
child_id: childId,
limit: 50,
offset: 0,
type: {},
},
fetchPolicy: 'cache-and-network',
});

const [assignAssessment, { loading: assignLoading }] = useAssignAssessmentMutation({
onCompleted: (data) => {
if (data.assignAssessment.success) {
toast.show({ text: 'Assessment assigned successfully' });
refetch();
refetchAssigned();
} else {
toast.show({ text: data.assignAssessment.message || 'Failed to assign assessment' });
}
},
onError: (error) => {
toast.show({ text: error.message || 'Failed to assign assessment' });
}
});

const handleAssignAssessment = (assessmentId: string) => {
assignAssessment({
variables: {
assessment_id: assessmentId,
child_id: childId,
therapist_id: therapistId
}
});
};

const handleUnassignAssessment = (assignmentId: string) => {
unassignAssessment({
variables: {
assignment_id: assignmentId
}
});
};

// Assessment assignment logic with comprehensive validation
const handleAssignAssessment = (assessmentId: string) => {
assignAssessment({
variables: {
assessment_id: assessmentId,
child_id: childId,
therapist_id: therapistId
}
});
};

const handleUnassignAssessment = (assignmentId: string) => {
unassignAssessment({
variables: {
assignment_id: assignmentId
}
});
};
};

Assessment Assignment Logic

// Assessment assignment validation
const validateAssessmentAssignment = (assessment: Assessment, childAge: number) => {
const errors = [];

// Check age compatibility
if (childAge < assessment.min_age || childAge > assessment.max_age) {
errors.push(`Assessment not suitable for child's age (${childAge} months)`);
}

// Check if already assigned
if (assessment.is_assigned) {
errors.push('Assessment already assigned to this child');
}

// Check subscription requirements
if (assessment.requires_premium && !child.subscription?.is_premium) {
errors.push('Premium subscription required for this assessment');
}

return errors;
};

// Age-based assessment filtering
const filterAssessmentsByAge = (assessments: Assessment[], childAge: number) => {
return assessments.filter(assessment =>
childAge >= assessment.min_age && childAge <= assessment.max_age
);
};

📋 Therapist Child Assessments

Assessment List Logic (No Locking)

Assessment Display Logic:

  • Shows all assessments with therapist speciality and child age as filters
  • No locking mechanism - therapist can start any assessment without assignment
  • Ordered by: Screening, Case History, CDDC (GM, FM, ADL, RL, EL, Cog, Soc, Emo), PLS, ORO-Motor, Others (created_at in Ascending order)
  • Supports direct assessment initiation without prior assignment

Assessment Filtering Logic:

  • Speciality Filtering: Shows assessments matching therapist's speciality
  • Age-based Filtering: Filters assessments suitable for child's current age
  • Category Ordering: Maintains specific order for assessment categories
  • Real-time Updates: Updates assessment list when filters change
// AssessmentList.tsx - Therapist assessment list with no locking
const AssessmentList = ({ navigation, route }: ScreenProps) => {
const { childId } = route.params;
const { therapistId } = useSelector(selectUserData);
const [filter, setFilter] = useState<AssessmentFilter>({
speciality: '',
age: 0,
category: 'ALL'
});

// Get all assessments with therapist speciality and child age filters
const { data: assessmentsData, loading, error, refetch } = useGetTherapistAssessmentsQuery({
variables: {
therapist_id: therapistId,
child_age: childAge,
speciality: filter.speciality,
category: filter.category
},
fetchPolicy: 'cache-and-network'
});

// Assessment ordering logic with specific category sequence
const orderedAssessments = useMemo(() => {
const assessments = assessmentsData?.assessments || [];
const categoryOrder = [
'Screening',
'Case History',
'CDDC_GM',
'CDDC_FM',
'CDDC_ADL',
'CDDC_RL',
'CDDC_EL',
'CDDC_Cog',
'CDDC_Soc',
'CDDC_Emo',
'PLS',
'ORO_Motor',
'Others'
];

return assessments.sort((a, b) => {
const aIndex = categoryOrder.indexOf(a.category);
const bIndex = categoryOrder.indexOf(b.category);

if (aIndex !== bIndex) {
return aIndex - bIndex;
}

return new Date(a.created_at).getTime() - new Date(b.created_at).getTime();
});
}, [assessmentsData]);

// Direct assessment start without assignment
const handleStartAssessment = (assessmentId: string) => {
navigation.navigate('therapist/assessment-details', {
assessmentId,
childId,
isDirectStart: true
});
};
};

Assessment Assignment - All Tab

Assignment Filtering Logic:

  • Shows assessments with therapist speciality + child subscription plan + age filters
  • Displays number of questions in CDDC based on child's age
  • Only allows assignment if child's subscription plan has "Assign Assessments" feature
  • Excludes assessments already started/completed by parent from their ALL tab
  • Maintains same ordering as assessment list

Subscription Validation Logic:

  • Feature Check: Validates "Assign Assessments" feature in child's subscription
  • Age Compatibility: Ensures assessment is suitable for child's age
  • Parent Progress: Excludes assessments with existing parent progress
  • Assignment Status: Prevents duplicate assignments
// AssignAssessmentAll.tsx - Assessment assignment with subscription validation
const AssignAssessmentAll = ({ navigation, route }: ScreenProps) => {
const { childId } = route.params;
const { therapistId } = useSelector(selectUserData);
const [selectedAssessments, setSelectedAssessments] = useState<string[]>([]);

// Get child subscription details
const { data: childSubscriptionData } = useGetChildSubscriptionQuery({
variables: { child_id: childId },
fetchPolicy: 'cache-and-network'
});

// Get available assessments with subscription and age filtering
const { data: availableAssessments, loading, refetch } = useGetAvailableAssessmentsQuery({
variables: {
child_id: childId,
therapist_id: therapistId,
child_age: childAge,
speciality: therapistSpeciality
},
fetchPolicy: 'cache-and-network'
});

// Get parent progress to exclude already started assessments
const { data: parentProgressData } = useGetParentAssessmentProgressQuery({
variables: { child_id: childId },
fetchPolicy: 'cache-and-network'
});

// Assessment filtering with subscription and progress validation
const filteredAssessments = useMemo(() => {
const assessments = availableAssessments?.assessments || [];
const parentProgress = parentProgressData?.assessment_responses || [];
const subscriptionFeatures = childSubscriptionData?.child_subscription?.features || [];

return assessments.filter(assessment => {
// Check subscription feature
const hasAssignFeature = subscriptionFeatures.includes('Assign Assessments');
if (!hasAssignFeature) return false;

// Check age compatibility
const isAgeCompatible = childAge >= assessment.min_age && childAge <= assessment.max_age;
if (!isAgeCompatible) return false;

// Exclude assessments with parent progress
const hasParentProgress = parentProgress.some(
progress => progress.assessment_id === assessment.id
);
if (hasParentProgress) return false;

return true;
});
}, [availableAssessments, parentProgressData, childSubscriptionData, childAge]);

// CDDC question count based on child age
const getCDDCQuestionCount = (assessment: Assessment) => {
if (assessment.category.startsWith('CDDC')) {
if (childAge <= 24) return 15; // 0-2 years
if (childAge <= 60) return 20; // 2-5 years
if (childAge <= 96) return 25; // 5-8 years
return 30; // 8+ years
}
return assessment.question_count;
};

// Assessment assignment with comprehensive validation
const handleAssignAssessments = async () => {
if (selectedAssessments.length === 0) {
toast.show({ text: 'Please select assessments to assign' });
return;
}

await assignAssessments({
variables: {
assessment_ids: selectedAssessments,
child_id: childId,
therapist_id: therapistId
}
});
};
};

Assessment Assignment - Assigned Tab

Assigned Assessment Display Logic:

  • Shows all assigned assessments regardless of who assigned them (Admin, Current Therapist, Other therapists, Past therapists)
  • Includes assessments started/completed by parent from their ALL tab
  • Ordered by assigned date with latest assigned at the top
  • Displays assignment source and progress status

Assignment Source Tracking:

  • Admin Assignments: Shows admin-assigned assessments
  • Therapist Assignments: Shows current and other therapist assignments
  • Parent Progress: Shows parent-started assessments from ALL tab
  • Historical Assignments: Shows past therapist assignments
// AssignAssessmentAssigned.tsx - Assigned assessments with source tracking
const AssignAssessmentAssigned = ({ navigation, route }: ScreenProps) => {
const { childId } = route.params;
const { therapistId } = useSelector(selectUserData);

// Get all assigned assessments with source tracking
const { data: assignedAssessments, loading, refetch } = useGetAssignedAssessmentsQuery({
variables: {
child_id: childId,
include_parent_progress: true
},
fetchPolicy: 'cache-and-network'
});

// Get parent progress for assessments started from ALL tab
const { data: parentProgressData } = useGetParentAssessmentProgressQuery({
variables: { child_id: childId },
fetchPolicy: 'cache-and-network'
});

// Combined assigned assessments with parent progress
const combinedAssignedAssessments = useMemo(() => {
const assigned = assignedAssessments?.assigned_assessments || [];
const parentProgress = parentProgressData?.assessment_responses || [];

// Add parent progress as "assigned" assessments
const parentStartedAssessments = parentProgress.map(progress => ({
id: progress.id,
assessment_id: progress.assessment_id,
child_id: progress.child_id,
assigned_by: 'PARENT',
assigned_at: progress.created_at,
status: progress.status,
assessment: progress.assessment,
is_parent_started: true
}));

// Combine and sort by assigned date (latest first)
return [...assigned, ...parentStartedAssessments].sort((a, b) =>
new Date(b.assigned_at).getTime() - new Date(a.assigned_at).getTime()
);
}, [assignedAssessments, parentProgressData]);

// Assignment source display logic
const getAssignmentSource = (assignment: any) => {
if (assignment.is_parent_started) return 'Started by Parent';
if (assignment.assigned_by === 'ADMIN') return 'Assigned by Admin';
if (assignment.assigned_by === therapistId) return 'Assigned by You';
return 'Assigned by Other Therapist';
};
};

📚 Lesson Plan Assignment

Lesson Plan List Logic (No Locking)

Lesson Plan Display Logic:

  • Shows all lesson plans with therapist speciality and child age as filters
  • No locking mechanism - therapist can start any lesson plan without assignment
  • Ordered by: Screening, Case History, CDDC (GM, FM, ADL, RL, EL, Cog, Soc, Emo), PLS, ORO-Motor, Others (created_at in Ascending order)
  • Supports direct lesson plan initiation without prior assignment
// LessonPlanList.tsx - Therapist lesson plan list with no locking
const LessonPlanList = ({ navigation, route }: ScreenProps) => {
const { childId } = route.params;
const { therapistId } = useSelector(selectUserData);
const [filter, setFilter] = useState<LessonPlanFilter>({
speciality: '',
age: 0,
category: 'ALL'
});

// Get all lesson plans with therapist speciality and child age filters
const { data: lessonPlansData, loading, error, refetch } = useGetTherapistLessonPlansQuery({
variables: {
therapist_id: therapistId,
child_age: childAge,
speciality: filter.speciality,
category: filter.category
},
fetchPolicy: 'cache-and-network'
});

// Lesson plan ordering logic with specific category sequence
const orderedLessonPlans = useMemo(() => {
const lessonPlans = lessonPlansData?.lesson_plans || [];
const categoryOrder = [
'Screening',
'Case History',
'CDDC_GM',
'CDDC_FM',
'CDDC_ADL',
'CDDC_RL',
'CDDC_EL',
'CDDC_Cog',
'CDDC_Soc',
'CDDC_Emo',
'PLS',
'ORO_Motor',
'Others'
];

return lessonPlans.sort((a, b) => {
const aIndex = categoryOrder.indexOf(a.category);
const bIndex = categoryOrder.indexOf(b.category);

if (aIndex !== bIndex) {
return aIndex - bIndex;
}

return new Date(a.created_at).getTime() - new Date(b.created_at).getTime();
});
}, [lessonPlansData]);

// Direct lesson plan start without assignment
const handleStartLessonPlan = (lessonPlanId: string) => {
navigation.navigate('therapist/lesson-plan-details', {
lessonPlanId,
childId,
isDirectStart: true
});
};
};

Lesson Plan Assignment - All Tab

Assignment Filtering Logic:

  • Shows lesson plans with therapist speciality + child subscription plan as filters
  • Only allows assignment if child's subscription plan has "Assign Lesson Plan" feature
  • Excludes lesson plans already started/completed by parent from their ALL tab
  • Removes search icon, keeps only filter icon for quick filtering
  • Maintains same ordering as lesson plan list

Subscription Validation Logic:

  • Feature Check: Validates "Assign Lesson Plan" feature in child's subscription
  • Parent Progress: Excludes lesson plans with existing parent progress
  • Assignment Status: Prevents duplicate assignments
// AssignLessonPlanAll.tsx - Lesson plan assignment with subscription validation
const AssignLessonPlanAll = ({ navigation, route }: ScreenProps) => {
const { childId } = route.params;
const { therapistId } = useSelector(selectUserData);
const [selectedLessonPlans, setSelectedLessonPlans] = useState<string[]>([]);

// Get child subscription details
const { data: childSubscriptionData } = useGetChildSubscriptionQuery({
variables: { child_id: childId },
fetchPolicy: 'cache-and-network'
});

// Get available lesson plans with subscription filtering
const { data: availableLessonPlans, loading, refetch } = useGetAvailableLessonPlansQuery({
variables: {
child_id: childId,
therapist_id: therapistId,
child_age: childAge,
speciality: therapistSpeciality
},
fetchPolicy: 'cache-and-network'
});

// Get parent progress to exclude already started lesson plans
const { data: parentProgressData } = useGetParentLessonPlanProgressQuery({
variables: { child_id: childId },
fetchPolicy: 'cache-and-network'
});

// Lesson plan filtering with subscription and progress validation
const filteredLessonPlans = useMemo(() => {
const lessonPlans = availableLessonPlans?.lesson_plans || [];
const parentProgress = parentProgressData?.lesson_plan_responses || [];
const subscriptionFeatures = childSubscriptionData?.child_subscription?.features || [];

return lessonPlans.filter(lessonPlan => {
// Check subscription feature
const hasAssignFeature = subscriptionFeatures.includes('Assign Lesson Plan');
if (!hasAssignFeature) return false;

// Exclude lesson plans with parent progress
const hasParentProgress = parentProgress.some(
progress => progress.lesson_plan_id === lessonPlan.id
);
if (hasParentProgress) return false;

return true;
});
}, [availableLessonPlans, parentProgressData, childSubscriptionData]);

// Lesson plan assignment with comprehensive validation
const handleAssignLessonPlans = async () => {
if (selectedLessonPlans.length === 0) {
toast.show({ text: 'Please select lesson plans to assign' });
return;
}

await assignLessonPlans({
variables: {
lesson_plan_ids: selectedLessonPlans,
child_id: childId,
therapist_id: therapistId
}
});
};
};

Lesson Plan Assignment - Assigned Tab

Assigned Lesson Plan Display Logic:

  • Shows all assigned lesson plans regardless of who assigned them (Admin, any therapists)
  • Includes lesson plans started/completed by parent from their ALL tab
  • Ordered by assigned date with latest assigned at the top
  • Displays assignment source and progress status
// AssignLessonPlanAssigned.tsx - Assigned lesson plans with source tracking
const AssignLessonPlanAssigned = ({ navigation, route }: ScreenProps) => {
const { childId } = route.params;
const { therapistId } = useSelector(selectUserData);

// Get all assigned lesson plans with source tracking
const { data: assignedLessonPlans, loading, refetch } = useGetAssignedLessonPlansQuery({
variables: {
child_id: childId,
include_parent_progress: true
},
fetchPolicy: 'cache-and-network'
});

// Get parent progress for lesson plans started from ALL tab
const { data: parentProgressData } = useGetParentLessonPlanProgressQuery({
variables: { child_id: childId },
fetchPolicy: 'cache-and-network'
});

// Combined assigned lesson plans with parent progress
const combinedAssignedLessonPlans = useMemo(() => {
const assigned = assignedLessonPlans?.assigned_lesson_plans || [];
const parentProgress = parentProgressData?.lesson_plan_responses || [];

// Add parent progress as "assigned" lesson plans
const parentStartedLessonPlans = parentProgress.map(progress => ({
id: progress.id,
lesson_plan_id: progress.lesson_plan_id,
child_id: progress.child_id,
assigned_by: 'PARENT',
assigned_at: progress.created_at,
status: progress.status,
lesson_plan: progress.lesson_plan,
is_parent_started: true
}));

// Combine and sort by assigned date (latest first)
return [...assigned, ...parentStartedLessonPlans].sort((a, b) =>
new Date(b.assigned_at).getTime() - new Date(a.assigned_at).getTime()
);
}, [assignedLessonPlans, parentProgressData]);

// Assignment source display logic
const getAssignmentSource = (assignment: any) => {
if (assignment.is_parent_started) return 'Started by Parent';
if (assignment.assigned_by === 'ADMIN') return 'Assigned by Admin';
if (assignment.assigned_by === therapistId) return 'Assigned by You';
return 'Assigned by Other Therapist';
};
};

Assign Lesson Plans Screen

// AssignLessonPlans.tsx - Lesson plan assignment screen
const AssignLessonPlans = ({ navigation, route }: ScreenProps) => {
const { childId } = route.params;
const [activeTab, setActiveTab] = useState(0);
const [selectedPlans, setSelectedPlans] = useState<string[]>([]);

const { therapistId } = useSelector(selectUserData);
const toast = useToast();

// Get available lesson plans
const {
data: lessonPlansData,
loading: lessonPlansLoading,
refetch: refetchLessonPlans,
} = useGetAvailableLessonPlansQuery({
variables: {
child_id: childId,
therapist_id: therapistId,
limit: 50,
offset: 0
},
fetchPolicy: 'cache-and-network',
});

// Get assigned lesson plans
const {
data: assignedPlansData,
loading: assignedPlansLoading,
refetch: refetchAssignedPlans,
} = useGetAssignedLessonPlansQuery({
variables: {
child_id: childId,
therapist_id: therapistId
},
fetchPolicy: 'cache-and-network',
});

const [assignLessonPlans, { loading: assignLoading }] = useAssignLessonPlansMutation({
onCompleted: (data) => {
if (data.assignLessonPlans.success) {
toast.show({ text: 'Lesson plans assigned successfully' });
setSelectedPlans([]);
refetchLessonPlans();
refetchAssignedPlans();
} else {
toast.show({ text: data.assignLessonPlans.message || 'Failed to assign lesson plans' });
}
},
onError: (error) => {
toast.show({ text: error.message || 'Failed to assign lesson plans' });
}
});

const handleAssignLessonPlans = () => {
if (selectedPlans.length === 0) {
toast.show({ text: 'Please select lesson plans to assign' });
return;
}

assignLessonPlans({
variables: {
lesson_plan_ids: selectedPlans,
child_id: childId,
therapist_id: therapistId
}
});
};

const handleSelectPlan = (planId: string) => {
setSelectedPlans(prev =>
prev.includes(planId)
? prev.filter(id => id !== planId)
: [...prev, planId]
);
};

return (
<SafeAreaView style={commonFlexStyles.FLEX}>
<StatusBar backgroundColor={colors.white} barStyle={'dark-content'} />

<Box flex={1}>
<LessonPlanHeader
child={childData?.child_by_pk}
onBack={() => navigation.goBack()}
selectedCount={selectedPlans.length}
onAssign={handleAssignLessonPlans}
loading={assignLoading}
/>

<TabView
navigationState={{
index: activeTab,
routes: [
{ key: 'available', title: 'Available' },
{ key: 'assigned', title: 'Assigned' }
]
}}
renderScene={({ route }) => {
if (route.key === 'available') {
return (
<AvailableLessonPlans
lessonPlans={lessonPlansData?.lesson_plans}
loading={lessonPlansLoading}
selectedPlans={selectedPlans}
onSelectPlan={handleSelectPlan}
/>
);
} else {
return (
<AssignedLessonPlans
assignments={assignedPlansData?.lesson_plan_assignments}
loading={assignedPlansLoading}
onUnassign={handleUnassignLessonPlan}
/>
);
}
}}
onIndexChange={setActiveTab}
renderTabBar={renderTabBar}
/>
</Box>
</SafeAreaView>
);
};

📈 Progress Tracking

Child Progress Screen

// ChildProgress.tsx - Progress tracking screen
const ChildProgress = ({ child }: { child: Child }) => {
const [selectedPeriod, setSelectedPeriod] = useState('month');
const [selectedMetric, setSelectedMetric] = useState('overall');

const { data: progressData, loading } = useGetChildProgressQuery({
variables: {
child_id: child.id,
period: selectedPeriod,
metric: selectedMetric
},
fetchPolicy: 'cache-and-network'
});

const progressMetrics = useMemo(() => {
if (!progressData) return null;

return {
overallProgress: progressData.overall_progress,
assessmentProgress: progressData.assessment_progress,
lessonPlanProgress: progressData.lesson_plan_progress,
milestones: progressData.milestones,
improvements: progressData.improvements,
challenges: progressData.challenges
};
}, [progressData]);

return (
<ScrollView>
<ProgressHeader
child={child}
selectedPeriod={selectedPeriod}
onPeriodChange={setSelectedPeriod}
selectedMetric={selectedMetric}
onMetricChange={setSelectedMetric}
/>

<ProgressOverview
metrics={progressMetrics}
loading={loading}
/>

<ProgressChart
data={progressData?.progress_chart}
type={selectedMetric}
/>

<MilestonesSection
milestones={progressMetrics?.milestones}
/>

<ImprovementsSection
improvements={progressMetrics?.improvements}
/>
</ScrollView>
);
};

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,
averageScore: analyticsData.average_score,
improvementRate: analyticsData.improvement_rate,
strengths: analyticsData.strengths,
areasForImprovement: analyticsData.areas_for_improvement
};
}, [analyticsData]);

return analytics;
};

🔍 Child Search & Filtering

Search Child List Screen

// SearchChildList.tsx - Child search functionality
const SearchChildListScreen = ({ navigation }: ScreenProps) => {
const [value, setValue] = useState('');
const { therapistId } = useSelector(selectUserData);

const { loading, error, data } = useTherapistChildQuery({
fetchPolicy: 'cache-and-network',
variables: { therapist_id: therapistId },
});

const [childList, setChildList] = useState<
TherapistChildQuery['child_therapist'] | undefined
>(data?.child_therapist);

useEffect(() => {
if (value !== '') {
const newData = data?.child_therapist.filter(i =>
i?.child?.name?.toLowerCase().includes(value.toLowerCase()),
);
setChildList(newData);
} else {
setChildList(data?.child_therapist);
}
}, [value, setChildList, data?.child_therapist]);

const _renderItem = ({ item }: { item: TherapistChildQuery['child_therapist'] }) => {
const onItemPress = () =>
navigation.replace('therapist/child-details', {
childId: item?.child?.id,
});

return <ChildListCard handleCardPress={onItemPress} item={item} />;
};

return (
<Box flex={1}>
<SearchHeader
value={value}
onChangeText={setValue}
placeholder="Search children..."
onBack={() => navigation.goBack()}
/>

{loading ? (
<ScreenLoader />
) : error ? (
<ErrorBox />
) : (
<FlatList
data={childList}
keyExtractor={item => item?.child?.id}
renderItem={_renderItem}
refreshControl={<RefreshControl refreshing={loading} onRefresh={refetch} />}
/>
)}
</Box>
);
};

Child Filtering

// ChildFilterModal.tsx - Advanced filtering
const ChildFilterModal = ({ visible, onClose, onApply, currentFilter }) => {
const [filter, setFilter] = useState(currentFilter);

const ageGroups = [
{ label: '0-2 years', minimum: 0, maximum: 24 },
{ label: '2-4 years', minimum: 24, maximum: 48 },
{ label: '4-6 years', minimum: 48, maximum: 72 },
{ label: '6+ years', minimum: 72, maximum: 120 }
];

const handleApplyFilter = () => {
onApply(filter);
onClose();
};

const handleClearFilter = () => {
setFilter(filterDefaultState);
onApply(filterDefaultState);
onClose();
};

return (
<Modal visible={visible} animationType="slide">
<SafeAreaView style={commonFlexStyles.FLEX}>
<Box flex={1} backgroundColor="white">
<FilterHeader
onClose={onClose}
onClear={handleClearFilter}
onApply={handleApplyFilter}
/>

<ScrollView>
<AgeGroupFilter
ageGroups={ageGroups}
selectedGroups={filter.ageGroups}
onSelectionChange={(groups) => setFilter(prev => ({ ...prev, ageGroups: groups }))}
/>

<DateRangeFilter
dateRange={filter.dateRange}
onDateRangeChange={(range) => setFilter(prev => ({ ...prev, dateRange: range }))}
/>

<StatusFilter
statuses={['active', 'inactive', 'pending']}
selectedStatuses={filter.statuses}
onSelectionChange={(statuses) => setFilter(prev => ({ ...prev, statuses }))}
/>
</ScrollView>
</Box>
</SafeAreaView>
</Modal>
);
};

📱 Mobile-Specific Features

Touch Interactions

// Child card touch interactions
const useChildGestures = () => {
const handleSwipeLeft = (child: Child) => {
// Swipe left to unassign
showUnassignConfirmation(child);
};

const handleSwipeRight = (child: Child) => {
// Swipe right to assign content
showAssignmentOptions(child);
};

const handleLongPress = (child: Child) => {
// Long press to show child options
showChildOptions(child);
};

return {
handleSwipeLeft,
handleSwipeRight,
handleLongPress
};
};

Offline Child Management

// Offline child management capabilities
const useOfflineChildManagement = () => {
const [offlineAssignments, setOfflineAssignments] = useState([]);

const saveOfflineAssignment = (assignment: Assignment) => {
const offlineAssignment = {
...assignment,
id: `offline_${Date.now()}`,
isOffline: true,
createdAt: new Date().toISOString()
};

setOfflineAssignments(prev => [...prev, offlineAssignment]);
mmkvStorage.set('offline_assignments', JSON.stringify([...offlineAssignments, offlineAssignment]));
};

const syncOfflineAssignments = async () => {
if (offlineAssignments.length === 0) return;

try {
for (const assignment of offlineAssignments) {
await executeAssignment(assignment);
}

setOfflineAssignments([]);
mmkvStorage.delete('offline_assignments');

toast.show({ text: 'Offline assignments synced successfully' });
} catch (error) {
toast.show({ text: 'Failed to sync offline assignments' });
}
};

return {
saveOfflineAssignment,
syncOfflineAssignments,
offlineAssignments
};
};

🎨 UI Components

Child List Card

// ChildListCard.tsx
const ChildListCard = ({ item, handleCardPress }) => {
const { colors } = useTheme<Theme>();

return (
<TouchableOpacity
style={styles.childCard}
onPress={() => handleCardPress(item)}
>
<Box flexDirection="row" alignItems="center">
<FastImage
source={{ uri: item?.child?.profile_image?.path }}
style={styles.profileImage}
/>

<Box flex={1} marginLeft="m">
<Text variant="heading5">{item?.child?.name}</Text>
<Text variant="body2" color="secondary63">
Age: {item?.child?.age} years
</Text>
<Text variant="body2" color="secondary63">
Parent: {item?.child?.parent?.user?.name}
</Text>
</Box>

<Box alignItems="flex-end">
<StatusBadge status={item?.status} />
<Text variant="caption" color="secondary63">
{item?.child?.appointments?.length || 0} appointments
</Text>
</Box>
</Box>

<Box flexDirection="row" justifyContent="space-between" marginTop="m">
<ProgressIndicator
assessments={item?.child?.assessments?.length || 0}
lessonPlans={item?.child?.lesson_plans?.length || 0}
/>

<TouchableOpacity onPress={() => handleCardPress(item)}>
<ChevronRight color={colors.secondary63} />
</TouchableOpacity>
</Box>
</TouchableOpacity>
);
};

🔧 GraphQL Integration

Child Management Queries

# Get therapist's children
query TherapistChild($therapist_id: String!) {
child_therapist(
where: { therapist_id: { _eq: $therapist_id } }
order_by: { created_at: desc }
) {
id
child {
id
name
age
dob
profile_image { path }
parent {
user { name email phone }
}
appointments {
id
date
status
}
}
status
created_at
}
}

# Get child details
query GetChildDetails($id: uuid!) {
child_by_pk(id: $id) {
id
name
age
dob
profile_image { path }
parent {
user { name email phone }
}
assessments {
id
title
status
assigned_date
}
lesson_plans {
id
title
status
assigned_date
}
appointments {
id
date
status
therapist { user { name } }
}
}
}

Assignment Mutations

# Assign assessment
mutation AssignAssessment($assessment_id: uuid!, $child_id: uuid!, $therapist_id: uuid!) {
assignAssessment(
assessment_id: $assessment_id
child_id: $child_id
therapist_id: $therapist_id
) {
success
message
assignment_id
}
}

# Assign lesson plans
mutation AssignLessonPlans($lesson_plan_ids: [uuid!]!, $child_id: uuid!, $therapist_id: uuid!) {
assignLessonPlans(
lesson_plan_ids: $lesson_plan_ids
child_id: $child_id
therapist_id: $therapist_id
) {
success
message
assignment_ids
}
}

# Unassign assessment
mutation UnassignAssessment($assignment_id: uuid!) {
unassignAssessment(assignment_id: $assignment_id) {
success
message
}
}

🎯 Best Practices

Child Management

  • Age-based Filtering: Filter content based on child's age
  • Progress Tracking: Monitor child's development progress
  • Assignment Validation: Validate assignments before applying
  • Real-time Updates: Keep assignments synchronized

User Experience

  • Visual Feedback: Clear assignment status indicators
  • Touch Interactions: Intuitive swipe and tap gestures
  • Offline Support: Allow offline assignment management
  • Search & Filter: Easy child discovery and filtering

Performance

  • Data Caching: Cache child data locally
  • Lazy Loading: Load child data on demand
  • Optimistic Updates: Update UI before server confirmation
  • Background Sync: Sync assignments in background

🎯 Next Steps