diff --git a/patient/assets/illustrations/i9n_adhd.png b/patient/assets/illustrations/i9n_adhd.png new file mode 100644 index 0000000..5f12c34 Binary files /dev/null and b/patient/assets/illustrations/i9n_adhd.png differ diff --git a/patient/assets/illustrations/i9n_aq10.png b/patient/assets/illustrations/i9n_aq10.png new file mode 100644 index 0000000..4c85cf8 Binary files /dev/null and b/patient/assets/illustrations/i9n_aq10.png differ diff --git a/patient/assets/illustrations/i9n_autism.png b/patient/assets/illustrations/i9n_autism.png new file mode 100644 index 0000000..fef9696 Binary files /dev/null and b/patient/assets/illustrations/i9n_autism.png differ diff --git a/patient/lib/gen/assets.gen.dart b/patient/lib/gen/assets.gen.dart index 3f9b8d6..3185966 100644 --- a/patient/lib/gen/assets.gen.dart +++ b/patient/lib/gen/assets.gen.dart @@ -63,17 +63,17 @@ class $AssetsIllustrationsGen { SvgGenImage get i9nActivities => const SvgGenImage('assets/illustrations/i9n_activities.svg'); - /// File path: assets/illustrations/i9n_adhd.svg - SvgGenImage get i9nAdhd => - const SvgGenImage('assets/illustrations/i9n_adhd.svg'); + /// File path: assets/illustrations/i9n_adhd.png + AssetGenImage get i9nAdhd => + const AssetGenImage('assets/illustrations/i9n_adhd.png'); - /// File path: assets/illustrations/i9n_aq10.svg - SvgGenImage get i9nAq10 => - const SvgGenImage('assets/illustrations/i9n_aq10.svg'); + /// File path: assets/illustrations/i9n_aq10.png + AssetGenImage get i9nAq10 => + const AssetGenImage('assets/illustrations/i9n_aq10.png'); - /// File path: assets/illustrations/i9n_autism.svg - SvgGenImage get i9nAutism => - const SvgGenImage('assets/illustrations/i9n_autism.svg'); + /// File path: assets/illustrations/i9n_autism.png + AssetGenImage get i9nAutism => + const AssetGenImage('assets/illustrations/i9n_autism.png'); /// File path: assets/illustrations/i9n_goals.svg SvgGenImage get i9nGoals => @@ -88,7 +88,7 @@ class $AssetsIllustrationsGen { const SvgGenImage('assets/illustrations/i9n_milestones.svg'); /// List of all assets - List get values => [ + List get values => [ i9nActivities, i9nAdhd, i9nAq10, diff --git a/patient/lib/presentation/assessments/assessment_screen.dart b/patient/lib/presentation/assessments/assessment_screen.dart index 3f4fe6d..7f8971e 100644 --- a/patient/lib/presentation/assessments/assessment_screen.dart +++ b/patient/lib/presentation/assessments/assessment_screen.dart @@ -5,8 +5,6 @@ import 'package:patient/model/assessment_models/assessment_models.dart'; import 'package:patient/presentation/widgets/snackbar_service.dart'; import 'package:patient/provider/assessment_provider.dart'; import 'package:provider/provider.dart'; -import 'package:patient/presentation/result/result.dart'; - class AssessmentScreen extends StatefulWidget { const AssessmentScreen({ @@ -26,25 +24,22 @@ class AssessmentScreenState extends State { super.initState(); } - @override - void didChangeDependencies() { - super.didChangeDependencies(); - WidgetsBinding.instance.addPostFrameCallback((_) { - final assessmentProvider = context.read(); - if (assessmentProvider.submitAssessmentStatus.isSuccess) { - SnackbarService.showSuccess( - '${assessmentProvider.assessmentResultModel?.message}'); - } else if (assessmentProvider.submitAssessmentStatus.isFailure) { - SnackbarService.showError( - 'Something went wrong. Please try again later.'); - } - }); + void _handleSubmit() async { + final assessmentProvider = context.read(); + await assessmentProvider.submitAssessment(); + + if (assessmentProvider.submitAssessmentStatus.isSuccess) { + SnackbarService.showSuccess( + '${assessmentProvider.assessmentResultModel?.message}'); + } else if (assessmentProvider.submitAssessmentStatus.isFailure) { + final errorMessage = assessmentProvider.errorMessage ?? + 'Something went wrong. Please try again later.'; + SnackbarService.showError(errorMessage); + } } @override Widget build(BuildContext context) { - Provider.of(context, listen: true) - .submitAssessmentStatus; return Scaffold( body: SafeArea( child: Consumer( @@ -98,36 +93,33 @@ class AssessmentScreenState extends State { ), const SizedBox(height: 20), Center( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 25), - child: SizedBox( - width: double.infinity, - height: 50, - child: ElevatedButton( - onPressed: () { - - context.read().submitAssessment(); - - }, - style: ElevatedButton.styleFrom( - backgroundColor: AppTheme.secondaryColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 25), + child: SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + onPressed: _handleSubmit, + style: ElevatedButton.styleFrom( + backgroundColor: AppTheme.secondaryColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30), + ), + elevation: 2, + padding: const EdgeInsets.symmetric(vertical: 12), ), - elevation: 2, // Small shadow effect - padding: const EdgeInsets.symmetric(vertical: 12), - ), - child: const Text( - 'Submit Assessment', - style: TextStyle( - fontSize: 17, - color: Colors.white, - fontWeight: FontWeight.bold, + child: const Text( + 'Submit Assessment', + style: TextStyle( + fontSize: 17, + color: Colors.white, + fontWeight: FontWeight.bold, + ), ), ), ), ), - )), + ), ], ), ); @@ -152,7 +144,6 @@ class QuestionCard extends StatelessWidget { @override Widget build(BuildContext context) { - return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, @@ -195,23 +186,22 @@ class QuestionCard extends StatelessWidget { side: const BorderSide( color: Color(0xFF666666), width: 1.5, - ), - Expanded( - child: Text( - optionText, - style: const TextStyle( - fontSize: 14, - color: AppTheme.subtitleColor, - ), + ), + Expanded( + child: Text( + optionText, + style: const TextStyle( + fontSize: 14, + color: AppTheme.subtitleColor, ), ), - ], - ), - ); - }), - ], - ), + ), + ], + ), + ); + }), + ], ); } -} \ No newline at end of file +} diff --git a/patient/lib/presentation/assessments/widgets/assessment_card.dart b/patient/lib/presentation/assessments/widgets/assessment_card.dart index b34c217..acc0ecd 100644 --- a/patient/lib/presentation/assessments/widgets/assessment_card.dart +++ b/patient/lib/presentation/assessments/widgets/assessment_card.dart @@ -24,40 +24,36 @@ class AssessmentCard extends StatelessWidget { borderRadius: BorderRadius.circular(16), ), child: Padding( - padding: const EdgeInsets.only(top: 18.0, left: 18.0, right: 18.0), - child: Row( + padding: const EdgeInsets.all(18.0), + child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - assessment.name, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w700, - color: AppTheme.primaryColor, - ), - ), - const SizedBox(height: 8), - Text( - assessment.description, - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Colors.black, - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - AssessmentIcon( - icon: assessment.imageUrl, - ), - ], - ), - ], + Text( + assessment.name, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w700, + color: AppTheme.primaryColor, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 8), + Text( + assessment.description, + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.black, + ), + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 16), + Align( + alignment: Alignment.centerRight, + child: AssessmentIcon( + icon: assessment.imageUrl, ), ), ], diff --git a/patient/lib/presentation/assessments/widgets/assessment_icon.dart b/patient/lib/presentation/assessments/widgets/assessment_icon.dart index 34e8b28..a55ecdb 100644 --- a/patient/lib/presentation/assessments/widgets/assessment_icon.dart +++ b/patient/lib/presentation/assessments/widgets/assessment_icon.dart @@ -10,11 +10,27 @@ class AssessmentIcon extends StatelessWidget { @override Widget build(BuildContext context) { - return Image.network( - icon, - width: 80, - height: 80, - fit: BoxFit.contain, - ); + // Check if the icon is a network URL or an asset path + if (icon.startsWith('http')) { + return Image.network( + icon, + width: 80, + height: 80, + fit: BoxFit.contain, + errorBuilder: (context, error, stackTrace) { + return const Icon(Icons.error_outline, size: 80); + }, + ); + } else { + return Image.asset( + icon, + width: 80, + height: 80, + fit: BoxFit.contain, + errorBuilder: (context, error, stackTrace) { + return const Icon(Icons.error_outline, size: 80); + }, + ); + } } } diff --git a/patient/lib/provider/assessment_provider.dart b/patient/lib/provider/assessment_provider.dart index 005bca9..5ad4f5f 100644 --- a/patient/lib/provider/assessment_provider.dart +++ b/patient/lib/provider/assessment_provider.dart @@ -15,34 +15,38 @@ class AssessmentProvider with ChangeNotifier { String _selectedAssesssmentId = ''; - AssessmentAnswerModel _assessmentAnswerModel = AssessmentAnswerModel(questions: [], assessmentId: ''); + AssessmentAnswerModel _assessmentAnswerModel = + AssessmentAnswerModel(questions: [], assessmentId: ''); AssessmentAnswerModel? get assessmentAnswerModel => _assessmentAnswerModel; - ApiStatus _submitAssessmentStatus = ApiStatus.initial; + ApiStatus _submitAssessmentStatus = ApiStatus.initial; ApiStatus get submitAssessmentStatus => _submitAssessmentStatus; + String? _errorMessage; + String? get errorMessage => _errorMessage; + AssessmentResultModel? _assessmentResultModel; AssessmentResultModel? get assessmentResultModel => _assessmentResultModel; - - set assessmentAnswers(AssessmentQuestionAnswerModel answers) { - final index = _assessmentAnswerModel.questions.indexWhere((element) => element.questionId == answers.questionId); - if(index != -1) { + set assessmentAnswers(AssessmentQuestionAnswerModel answers) { + final index = _assessmentAnswerModel.questions + .indexWhere((element) => element.questionId == answers.questionId); + if (index != -1) { _assessmentAnswerModel.questions[index] = answers; } else { _assessmentAnswerModel.questions.add(answers); } notifyListeners(); - } + } String get selectedAssessmentId => _selectedAssesssmentId; set selectedAssessmentId(String value) { _selectedAssesssmentId = value; - _assessmentAnswerModel = _assessmentAnswerModel.copyWith(assessmentId: value, questions: []); + _assessmentAnswerModel = + _assessmentAnswerModel.copyWith(assessmentId: value, questions: []); notifyListeners(); } - void fetchAllAssessments() async { final result = await _repository.fetchAllAssessments(); if (result is ActionResultSuccess) { @@ -54,15 +58,18 @@ class AssessmentProvider with ChangeNotifier { //implement the submitAssessment method to submit the assessment when the submitted_assessment table is created Future submitAssessment() async { _submitAssessmentStatus = ApiStatus.initial; + _errorMessage = null; notifyListeners(); - final result = await _repository.submitAssessment(_assessmentAnswerModel.toEntity()); + + final result = + await _repository.submitAssessment(_assessmentAnswerModel.toEntity()); if (result is ActionResultSuccess) { _submitAssessmentStatus = ApiStatus.success; _assessmentResultModel = result.data; } else { _submitAssessmentStatus = ApiStatus.failure; + _errorMessage = (result as ActionResultFailure).errorMessage; } notifyListeners(); } - } diff --git a/patient/lib/repository/supabase_assessments_repository.dart b/patient/lib/repository/supabase_assessments_repository.dart index c41b0b4..5bf105c 100644 --- a/patient/lib/repository/supabase_assessments_repository.dart +++ b/patient/lib/repository/supabase_assessments_repository.dart @@ -19,7 +19,7 @@ class SupabaseAssessmentsRepository implements AssessmentsRepository { .eq('id', id) .limit(1) .maybeSingle(); -print('Response: $response'); + print('Response: $response'); return response != null ? [response] : []; } @@ -39,8 +39,26 @@ print('Response: $response'); @override Future submitAssessment(AssessmentAnswerEntity answers) async { try { + // Validate that all questions have been answered + if (answers.questions.isEmpty) { + return ActionResultFailure( + errorMessage: 'Please answer all questions before submitting', + statusCode: 400, + ); + } + + // Check for unanswered questions + final unansweredQuestions = + answers.questions.where((q) => q.answerId.isEmpty).toList(); + if (unansweredQuestions.isNotEmpty) { + return ActionResultFailure( + errorMessage: 'Please answer all questions before submitting', + statusCode: 400, + ); + } + final jwtToken = dotenv.env['SUPABASE_ANON_KEY']!; - final resposne = await _supabase.functions.invoke( + final response = await _supabase.functions.invoke( 'evaluate-assessments', headers: { 'Content-Type': 'application/json', @@ -48,15 +66,39 @@ print('Response: $response'); }, body: answers.toMap(), ); - if (resposne.data != null) { - final data = AssessmentResultEntityMapper.fromMap(resposne.data); + + if (response.data != null) { + final data = AssessmentResultEntityMapper.fromMap(response.data); return ActionResultSuccess(data: data.toModel(), statusCode: 200); } else { return ActionResultFailure( - errorMessage: 'Some error Occurred', statusCode: 400); + errorMessage: 'Failed to evaluate assessment. Please try again.', + statusCode: 400, + ); } } catch (e) { - return ActionResultFailure(errorMessage: e.toString(), statusCode: 500); + if (e.toString().contains('NetworkError')) { + return ActionResultFailure( + errorMessage: 'Network error. Please check your internet connection.', + statusCode: 500, + ); + } else if (e.toString().contains('401')) { + return ActionResultFailure( + errorMessage: 'Authentication error. Please log in again.', + statusCode: 401, + ); + } else if (e.toString().contains('404')) { + return ActionResultFailure( + errorMessage: + 'Server error: Assessment evaluation service is not available. Please try again later.', + statusCode: 404, + ); + } else { + return ActionResultFailure( + errorMessage: 'An unexpected error occurred: ${e.toString()}', + statusCode: 500, + ); + } } } }