diff --git a/patient/lib/presentation/activities/daily_activities_screen.dart b/patient/lib/presentation/activities/daily_activities_screen.dart index 182ba99..7e11f2b 100644 --- a/patient/lib/presentation/activities/daily_activities_screen.dart +++ b/patient/lib/presentation/activities/daily_activities_screen.dart @@ -2,6 +2,7 @@ import 'package:easy_date_timeline/easy_date_timeline.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import '../../core/core.dart'; import '../../core/theme/theme.dart'; import '../../provider/task_provider.dart'; @@ -55,18 +56,35 @@ class _DailyActivitiesScreenState extends State @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('Daily Activities'), - centerTitle: true, - leading: IconButton( - icon: const Icon(Icons.arrow_back_ios_new_rounded), - onPressed: () { - // context.read().updateActivityInBackground(); // TODO: Uncomment this when the backend is ready - Navigator.pop(context); - }, + return PopScope( + canPop: false, + onPopInvokedWithResult: (didPop, result) async { + if (didPop) return; + final taskProvider = context.read(); + try { + await taskProvider.updateActivityInBackground(); + if (taskProvider.syncStatus == ApiStatus.failure && mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Failed to save your progress'), + backgroundColor: Colors.red, + duration: Duration(seconds: 2), + ), + ); + } + } finally { + if (mounted) Navigator.pop(context); + } + }, + child: Scaffold( + appBar: AppBar( + title: const Text('Daily Activities'), + centerTitle: true, + leading: IconButton( + icon: const Icon(Icons.arrow_back_ios_new_rounded), + onPressed: () => Navigator.maybePop(context), + ), ), - ), body: Consumer( builder: (context, taskProvider, child) { final tasks = taskProvider.tasks; @@ -229,6 +247,7 @@ class _DailyActivitiesScreenState extends State ); }, ), + ), ); } } diff --git a/patient/lib/provider/task_provider.dart b/patient/lib/provider/task_provider.dart index 983a406..d701232 100644 --- a/patient/lib/provider/task_provider.dart +++ b/patient/lib/provider/task_provider.dart @@ -7,6 +7,7 @@ class TaskProvider extends ChangeNotifier { DateTime _selectedDate = DateTime.now(); final PatientRepository _patientRepository; ApiStatus _apiStatus = ApiStatus.initial; + ApiStatus _syncStatus = ApiStatus.initial; String? _activityId; String? _activitySetId; @@ -39,9 +40,13 @@ class TaskProvider extends ChangeNotifier { } } - Future updateActivityCompletion(List tasks) async { + Future updateActivityCompletion() async { try { - final result = await _patientRepository.updateActivityCompletion(tasks: _allTasks, activityId: _activityId, activitySetId: _activitySetId); + final result = await _patientRepository.updateActivityCompletion( + tasks: _allTasks, + activityId: _activityId, + activitySetId: _activitySetId, + ); if(result is ActionResultSuccess) { _apiStatus = ApiStatus.success; } else { @@ -60,7 +65,7 @@ class TaskProvider extends ChangeNotifier { _selectedDate = date; notifyListeners(); if(_allTasks.isNotEmpty) { - updateActivityCompletion(_allTasks); + updateActivityCompletion(); } getTodayActivities(date: date); } @@ -76,6 +81,21 @@ class TaskProvider extends ChangeNotifier { notifyListeners(); } + /// Updates activity completion status in the background (typically before navigation) + /// This method syncs the current task completion status to the backend + Future updateActivityInBackground() async { + if (_allTasks.isEmpty || _syncStatus == ApiStatus.loading) { + return; + } + _syncStatus = ApiStatus.loading; + notifyListeners(); + await updateActivityCompletion(); + // updateActivityCompletion sets _apiStatus, copy it to _syncStatus + _syncStatus = _apiStatus; + notifyListeners(); + } + int get completedTasksCount => tasks.where((task) => task.isCompleted ?? false).length; int get totalTasksCount => tasks.length; + ApiStatus get syncStatus => _syncStatus; } diff --git a/therapist/lib/presentation/home/home_screen.dart b/therapist/lib/presentation/home/home_screen.dart index a30e396..dccfaf7 100644 --- a/therapist/lib/presentation/home/home_screen.dart +++ b/therapist/lib/presentation/home/home_screen.dart @@ -36,8 +36,11 @@ class _HomeScreenState extends State { WidgetsBinding.instance.addPostFrameCallback((_) { Provider.of(context, listen: false) .fetchConsultationRequests(); - Provider.of(context, listen: false) - .fetchPatientsMappedToTherapist(); + final therapistProvider = Provider.of(context, listen: false); + therapistProvider.fetchPatientsMappedToTherapist(); + therapistProvider.fetchTotals(); + Provider.of(context, listen: false) + .fetchTherapistSessions(); }); _refreshTimer = Timer.periodic(const Duration(minutes: 3), (_) { @@ -211,72 +214,54 @@ class HomeContent extends StatelessWidget { child: ListView( padding: const EdgeInsets.all(20), children: [ - Container( - padding: const EdgeInsets.all(20), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(16), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 10, - offset: const Offset(0, 5), - ), - ], - ), - child: const Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - StatsCard( - imagePath: 'assets/icon1.png', - backgroundColor: Color(0xFFFEE8E8), - label: 'Patients', - value: '02', - ), - StatsCard( - imagePath: 'assets/icon2.png', - backgroundColor: Color(0xFFF1E8FE), - label: 'Sessions', - value: '20', + Consumer( + builder: (context, therapistProvider, _) { + if (therapistProvider.isTotalsLoading) { + return const Center(child: CircularProgressIndicator()); + } + + return Container( + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 10, + offset: const Offset(0, 5), + ), + ], ), - StatsCard( - imagePath: 'assets/icon3.png', - backgroundColor: Color(0xFFE8FEF0), - label: 'Therapies', - value: '13', + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + StatsCard( + imagePath: 'assets/icon1.png', + backgroundColor: const Color(0xFFFEE8E8), + label: 'Patients', + value: therapistProvider.totalPatients.toString(), + ), + StatsCard( + imagePath: 'assets/icon2.png', + backgroundColor: const Color(0xFFF1E8FE), + label: 'Sessions', + value: therapistProvider.totalSessions.toString(), + ), + StatsCard( + imagePath: 'assets/icon3.png', + backgroundColor: const Color(0xFFE8FEF0), + label: 'Therapies', + value: therapistProvider.totalTherapies.toString(), + ), + ], ), - ], - ), + ); + }, ), const SizedBox(height: 24), - //_buildConsultationRequestSection(context), - // _buildConsultationRequestSection(context), - Consumer( - builder: (context, provider, _) { - if(provider.patientsStatus == ApiStatus.loading) { - return const Center(child: CircularProgressIndicator()); - } else if(provider.patientsStatus == ApiStatus.failure) { - return const Center(child: Text('No patients found')); - } else if(provider.patientsStatus == ApiStatus.success) { - if(provider.patients.isEmpty) { - return const Center(child: Text('No patients found')); - } else { - return Column( - children: provider.patients.map((patient) => PatientCard( - name: patient.patientName, - id: patient.patientId, - phone: patient.phoneNo, - email: patient.email, - package: '-', - duration: '-', - imagePath: 'assets/abdul.png', - )).toList(), - ); - } - } - return const SizedBox.shrink(); - }, - ), + // _buildConsultationRequestSection(context), + ], ), ), diff --git a/therapist/lib/provider/therapist_provider.dart b/therapist/lib/provider/therapist_provider.dart index 3cae5e8..26e4e2e 100644 --- a/therapist/lib/provider/therapist_provider.dart +++ b/therapist/lib/provider/therapist_provider.dart @@ -15,6 +15,10 @@ class TherapistDataProvider extends ChangeNotifier { bool _isLoading = false; bool get isLoading => _isLoading; + bool _isTotalsLoading = false; + bool get isTotalsLoading => _isTotalsLoading; + bool _isPatientsLoading = false; + bool get isPatientsLoading => _isPatientsLoading; String _errorMessage = ''; String get errorMessage => _errorMessage; @@ -152,6 +156,15 @@ class TherapistDataProvider extends ChangeNotifier { notifyListeners(); } + int _totalPatients = 0; + int get totalPatients => _totalPatients; + + int _totalSessions = 0; + int get totalSessions => _totalSessions; + + int _totalTherapies = 0; + int get totalTherapies => _totalTherapies; + // Fetch therapies based on selected profession Future fetchTherapies(int professionId) async { _isLoading = true; @@ -170,6 +183,36 @@ class TherapistDataProvider extends ChangeNotifier { notifyListeners(); } + Future fetchTotals() async { + _isTotalsLoading = true; + notifyListeners(); + try { + final results = await Future.wait([ + _therapistRepository.getTotalPatients(), + _therapistRepository.getTotalSessions(), + _therapistRepository.getTotalTherapies(), + ]); + + final patientsResult = results[0]; + if (patientsResult is ActionResultSuccess) { + _totalPatients = (patientsResult.data as num?)?.toInt() ?? 0; + } + + final sessionsResult = results[1]; + if (sessionsResult is ActionResultSuccess) { + _totalSessions = (sessionsResult.data as num?)?.toInt() ?? 0; + } + + final therapiesResult = results[2]; + if (therapiesResult is ActionResultSuccess) { + _totalTherapies = (therapiesResult.data as num?)?.toInt() ?? 0; + } + } finally { + _isTotalsLoading = false; + notifyListeners(); + } + } + // Set selected profession and fetch related data Future setSelectedProfession(int professionId, String professionName) async { _selectedProfessionId = professionId; @@ -209,19 +252,24 @@ class TherapistDataProvider extends ChangeNotifier { } Future fetchPatientsMappedToTherapist() async { + _isPatientsLoading = true; _patientsStatus = ApiStatus.loading; notifyListeners(); - final result = await _therapistRepository.fetchPatientsMappedToTherapist(); - - if(result is ActionResultSuccess) { - _patients = result.data; - _patientsStatus = ApiStatus.success; - } else if(result is ActionResultFailure) { - _patients = []; - _patientsStatus = ApiStatus.failure; - _errorMessage = result.errorMessage!; + try { + final result = await _therapistRepository.fetchPatientsMappedToTherapist(); + + if(result is ActionResultSuccess) { + _patients = result.data; + _patientsStatus = ApiStatus.success; + } else if(result is ActionResultFailure) { + _patients = []; + _patientsStatus = ApiStatus.failure; + _errorMessage = result.errorMessage!; + } + } finally { + _isPatientsLoading = false; + notifyListeners(); } - notifyListeners(); } } diff --git a/therapist/lib/repository/supabase_therapist_repository.dart b/therapist/lib/repository/supabase_therapist_repository.dart index d56e1f8..49937c5 100644 --- a/therapist/lib/repository/supabase_therapist_repository.dart +++ b/therapist/lib/repository/supabase_therapist_repository.dart @@ -129,21 +129,54 @@ class SupabaseTherapistRepository implements TherapistRepository { } @override - Future getTotalPatients() { - // TODO: implement getTotalPatients - throw UnimplementedError(); + Future getTotalPatients() async { + try { + final response = await _supabaseClient + .from('patient') + .select('id') + .eq('therapist_id', _supabaseClient.auth.currentUser!.id); + + final count = response is List ? response.length : 0; + return ActionResultSuccess(data: count, statusCode: 200); + } catch (e) { + return ActionResultFailure(errorMessage: e.toString(), statusCode: 400); + } } - + @override - Future getTotalSessions() { - // TODO: implement getTotalSessions - throw UnimplementedError(); + Future getTotalSessions() async { + try { + final response = await _supabaseClient + .from('session') + .select('id') + .eq('therapist_id', _supabaseClient.auth.currentUser!.id); + + final count = response is List ? response.length : 0; + return ActionResultSuccess(data: count, statusCode: 200); + } catch (e) { + return ActionResultFailure(errorMessage: e.toString(), statusCode: 400); + } } - + @override - Future getTotalTherapies() { - // TODO: implement getTotalTherapies - throw UnimplementedError(); + Future getTotalTherapies() async { + try { + final response = await _supabaseClient + .from('therapy_goal') + .select('therapy_type_id') + .eq('therapist_id', _supabaseClient.auth.currentUser!.id); + + final count = response is List + ? response + .map((e) => e['therapy_type_id']) + .where((id) => id != null) + .toSet() + .length + : 0; + return ActionResultSuccess(data: count, statusCode: 200); + } catch (e) { + return ActionResultFailure(errorMessage: e.toString(), statusCode: 400); + } } @override