From b7cae81d8f66d0aead1524361051f80b5808b1c8 Mon Sep 17 00:00:00 2001 From: Ida631 <52692119+Ida631@users.noreply.github.com> Date: Thu, 19 Dec 2024 09:56:44 +1300 Subject: [PATCH] Lulin/task/add reference link (#81) --- .../viewModels/settings_view_model.dart | 4 +- .../settings/views/apply_expert_page.dart | 50 +++++++++++++++++-- .../views/manage_composite_experts_page.dart | 30 +++++++++-- .../repositories/user_repository_impl.dart | 3 +- packages/domain/lib/entities/application.dart | 3 ++ .../user_repository.dart | 2 +- .../domain/lib/usecases/user_usercase.dart | 4 +- pubspec.yaml | 1 + 8 files changed, 83 insertions(+), 14 deletions(-) diff --git a/lib/presentation/settings/viewModels/settings_view_model.dart b/lib/presentation/settings/viewModels/settings_view_model.dart index f00ac7c..4f06e82 100644 --- a/lib/presentation/settings/viewModels/settings_view_model.dart +++ b/lib/presentation/settings/viewModels/settings_view_model.dart @@ -221,10 +221,10 @@ class SettingsViewModel extends ChangeNotifier { } } - Future submitApplication(String reason) async { + Future submitApplication(String reason, String link) async { String result = ''; try { - submission = await userUserCase.submitApplication(reason); + submission = await userUserCase.submitApplication(reason, link); if (submission == 'success') { result = 'Application successfully submitted. Please wait for approval.'; return result; diff --git a/lib/presentation/settings/views/apply_expert_page.dart b/lib/presentation/settings/views/apply_expert_page.dart index c053636..4b13bb4 100644 --- a/lib/presentation/settings/views/apply_expert_page.dart +++ b/lib/presentation/settings/views/apply_expert_page.dart @@ -13,15 +13,35 @@ class ApplyExpertPage extends StatefulWidget { class _ApplyExpertPage extends State { final TextEditingController _reasonController = TextEditingController(); + final TextEditingController _profileLinkController = TextEditingController(); bool _isLoading = false; + @override @override void dispose() { _reasonController.dispose(); + _profileLinkController.dispose(); super.dispose(); } + + bool _isUrlValid(String url) { + final urlPattern = + r'^(https?:\/\/)?(www\.)?([a-zA-Z0-9-_]+\.)+[a-zA-Z]{2,}(:\d+)?(\/.*)?$'; + return RegExp(urlPattern).hasMatch(url); + } + + Future _submitApplication() async { + final profileLink = _profileLinkController.text; + + if (profileLink.isNotEmpty && !_isUrlValid(profileLink)) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text("Please enter a valid URL.")), + ); + return; + } + setState(() { _isLoading = true; }); @@ -29,7 +49,7 @@ class _ApplyExpertPage extends State { try { // Access the view model and submit the application final viewModel = Provider.of(context, listen: false); - final result = await viewModel.submitApplication(_reasonController.text); + final result = await viewModel.submitApplication(_reasonController.text, profileLink); if (result == 'Application successfully submitted. Please wait for approval.' || result == 'This user has already submitted an expert application. Please wait for approval.' || @@ -56,7 +76,7 @@ class _ApplyExpertPage extends State { await Future.delayed(Duration(milliseconds: 300)); // Optional delay Navigator.pop(context, 'submit'); // Navigate back after dialog interaction } catch (error) { - // Show error message if update fails + print("Submission error: $error"); // Log the error for debugging ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Failed to submit: $error")), ); @@ -70,14 +90,24 @@ class _ApplyExpertPage extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text("Expert Application")), + appBar: AppBar(title: const Text("Expert Applications")), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Reason (optional):", + "Filling these fields will increase your chances of approval and speed up the process.", + style: TextStyle( + fontSize: 16, // Slightly larger text + fontWeight: FontWeight.w500, // Medium weight for emphasis + color: Colors.blueAccent, // Use a subtle blue color for emphasis + ), + textAlign: TextAlign.center, // Center the text horizontally + ), + SizedBox(height: 25.0), + Text( + "Reason:", style: TextStyle(fontSize: 16.0), ), TextFormField( @@ -87,6 +117,18 @@ class _ApplyExpertPage extends State { ), ), SizedBox(height: 20.0), + Text( + "LinkedIn or Other Professional Profile Link:", + style: TextStyle(fontSize: 16.0), + ), + TextFormField( + controller: _profileLinkController, + decoration: InputDecoration( + hintText: "a link to showcase your professional work...", + ), + keyboardType: TextInputType.url, // URL-specific keyboard + ), + SizedBox(height: 20.0), _isLoading ? Center(child: CircularProgressIndicator()) // Center the loading indicator : Center( diff --git a/lib/presentation/settings/views/manage_composite_experts_page.dart b/lib/presentation/settings/views/manage_composite_experts_page.dart index fce04b5..e674e72 100644 --- a/lib/presentation/settings/views/manage_composite_experts_page.dart +++ b/lib/presentation/settings/views/manage_composite_experts_page.dart @@ -1,12 +1,11 @@ - -import 'package:domain/entities/application.dart'; import 'package:domain/entities/user.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../viewModels/manage_composite_experts_view_model.dart'; +import 'package:url_launcher/url_launcher.dart'; class ManageCompositeExpertsPage extends StatefulWidget { const ManageCompositeExpertsPage({Key? key}) : super(key: key); @@ -28,6 +27,7 @@ class _ManageCompositeExpertsPageState extends State } + @override Widget build(BuildContext context) { return Scaffold( @@ -52,8 +52,30 @@ class _ManageCompositeExpertsPageState extends State child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Reason: ${application?.reason}"), - Text("User ID: ${application?.userId}"), + Text("Reason: ${application?.reason?.isNotEmpty == true ? application?.reason : "Not available"}", ), + application?.link?.isNotEmpty == true + ? GestureDetector( + onTap: () async { + final Uri uri = Uri.parse(application!.link!); + if (!await launchUrl(uri, mode: LaunchMode.externalApplication)) { + throw 'Could not launch ${application.link}'; + } + }, + child: RichText( + text: TextSpan( + text: "Profile Link: ", // The first part with black color + style: const TextStyle(color: Colors.black), // Style for "Profile Link:" + children: [ + TextSpan( + text: application.link, // The link with blue color + style: const TextStyle(color: Colors.blue), + ), + ], + ), + ) + ) + : const Text("Profile Link: Not available"), + FutureBuilder( future: userFuture, // Call your function builder: (context, snapshot) { diff --git a/packages/data/lib/repositories/user_repository_impl.dart b/packages/data/lib/repositories/user_repository_impl.dart index c54cadb..fa783d6 100644 --- a/packages/data/lib/repositories/user_repository_impl.dart +++ b/packages/data/lib/repositories/user_repository_impl.dart @@ -69,7 +69,7 @@ class UserRepositoryImpl implements UserRepository { } @override - Future submitApplication(String? reason) async { + Future submitApplication(String? reason, String? link) async { final baseURL = await apiEnvironment.getBaseUrl(); final url = Uri.parse('$baseURL/experts/register-expert'); final response = await authClient.post( @@ -78,6 +78,7 @@ class UserRepositoryImpl implements UserRepository { body: jsonEncode( { if (reason != null) 'reason': reason, + if (link != null) 'link': link, }, ), ); diff --git a/packages/domain/lib/entities/application.dart b/packages/domain/lib/entities/application.dart index fba9d0a..3215559 100644 --- a/packages/domain/lib/entities/application.dart +++ b/packages/domain/lib/entities/application.dart @@ -1,11 +1,13 @@ class CompositeExpertRequest { final int userId; String? reason; + String? link; CompositeExpertRequest({ required this.userId, this.reason, + this.link }); // Factory constructor to create a User instance from JSON @@ -13,6 +15,7 @@ class CompositeExpertRequest { return CompositeExpertRequest( userId: json['userId'], reason: json['reason'] ?? '', + link: json['link'] ?? '', ); } } diff --git a/packages/domain/lib/repositories_abstract/user_repository.dart b/packages/domain/lib/repositories_abstract/user_repository.dart index e1473d7..d023060 100644 --- a/packages/domain/lib/repositories_abstract/user_repository.dart +++ b/packages/domain/lib/repositories_abstract/user_repository.dart @@ -5,7 +5,7 @@ abstract class UserRepository { Future fetchMe(); Future updateMe(String newName); Future deleteAccount(); - Future submitApplication(String? reason); + Future submitApplication(String? reason, String? link); Future getUserById(int userId); Future becomeExpert(int userId); } diff --git a/packages/domain/lib/usecases/user_usercase.dart b/packages/domain/lib/usecases/user_usercase.dart index 1d4a421..b00ff0a 100644 --- a/packages/domain/lib/usecases/user_usercase.dart +++ b/packages/domain/lib/usecases/user_usercase.dart @@ -23,8 +23,8 @@ class UserUseCase { await tokenProvider.deleteToken(); } - Future submitApplication(String? reason) async{ - String result = await repository.submitApplication(reason); + Future submitApplication(String? reason, String? link) async{ + String result = await repository.submitApplication(reason, link); return result; } diff --git a/pubspec.yaml b/pubspec.yaml index e074304..9ee9741 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -53,6 +53,7 @@ dependencies: + dev_dependencies: flutter_test: sdk: flutter