From e638117a1147785439f49ebd7a703b6150ae3b1b Mon Sep 17 00:00:00 2001 From: MIRaiyan <99160923+MIRaiyan@users.noreply.github.com> Date: Wed, 7 May 2025 12:55:04 +0600 Subject: [PATCH 1/2] update pass(not done) --- lib/profile/profile.dart | 10 +- lib/profile/update_password.dart | 156 ++++++++++++++++++ .../{update.dart => update_profile.dart} | 0 lib/service/profile_provider.dart | 36 +++- pubspec.lock | 4 +- 5 files changed, 202 insertions(+), 4 deletions(-) create mode 100644 lib/profile/update_password.dart rename lib/profile/{update.dart => update_profile.dart} (100%) diff --git a/lib/profile/profile.dart b/lib/profile/profile.dart index 536c6b4..f86f6ad 100644 --- a/lib/profile/profile.dart +++ b/lib/profile/profile.dart @@ -2,7 +2,8 @@ import 'package:bracu_core/auth/login.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../service/profile_provider.dart'; -import 'update.dart'; +import 'update_profile.dart'; +import 'update_password.dart'; class Profile extends StatelessWidget { const Profile({super.key}); @@ -100,6 +101,13 @@ class Profile extends StatelessWidget { ), ), ); + } else if (options[index] == 'Change Password') { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const UpdatePasswordPage(), + ), + ); } }, child: Card( diff --git a/lib/profile/update_password.dart b/lib/profile/update_password.dart new file mode 100644 index 0000000..42a29c5 --- /dev/null +++ b/lib/profile/update_password.dart @@ -0,0 +1,156 @@ +import 'package:bracu_core/api/api_root.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import '../service/profile_provider.dart'; +import 'package:provider/provider.dart'; + + +class UpdatePasswordPage extends StatefulWidget { + const UpdatePasswordPage({super.key}); + + @override + State createState() => _UpdatePasswordPageState(); +} + +class _UpdatePasswordPageState extends State { + final _formKey = GlobalKey(); + bool _hideCurrent = true; + bool _hideNew = true; + bool _hideConfirm = true; + + final _currentController = TextEditingController(); + final _newController = TextEditingController(); + final _confirmController = TextEditingController(); + + final Color orangeColor = Color(0xFFF57C00); // Use same orange for both AppBar & button + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Change Password'), + backgroundColor: orangeColor, // Orange AppBar + ), + body: Padding( + padding: const EdgeInsets.all(16), + child: Form( + key: _formKey, + child: Column( + children: [ + _buildPasswordField( + controller: _currentController, + label: 'Current Password', + isHidden: _hideCurrent, + onToggle: () => setState(() => _hideCurrent = !_hideCurrent), + ), + SizedBox(height: 20), + _buildPasswordField( + controller: _newController, + label: 'New Password', + isHidden: _hideNew, + onToggle: () => setState(() => _hideNew = !_hideNew), + ), + SizedBox(height: 20), + _buildPasswordField( + controller: _confirmController, + label: 'Confirm Password', + isHidden: _hideConfirm, + onToggle: () => setState(() => _hideConfirm = !_hideConfirm), + ), + SizedBox(height: 30), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: orangeColor, // Orange button + minimumSize: Size(double.infinity, 50), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + onPressed: () async { + if (_formKey.currentState!.validate()) { + final token = await Provider.of( + context, + listen: false, + ).loadAuthToken(); + + try { + final response = await http.put( + Uri.parse('${api_root}/api/user/update_password'), + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $token', + }, + body: jsonEncode({ + 'oldPassword': _currentController.text, + 'newPassword': _newController.text, + }), + ); + + if (response.statusCode == 200) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Password updated successfully')), + ); + Navigator.pop(context); + } else { + final error = jsonDecode(response.body)['message'] ?? 'Something went wrong'; + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Failed: $error')), + ); + } + } catch (e) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Error: ${e.toString()}')), + ); + } + } + }, + child: Text( + 'Update Password', + style: TextStyle(fontSize: 16), + ), + ), + ], + ), + ), + ), + ); + } + + + Widget _buildPasswordField({ + required TextEditingController controller, + required String label, + required bool isHidden, + required VoidCallback onToggle, + }) { + return TextFormField( + controller: controller, + obscureText: isHidden, + decoration: InputDecoration( + labelText: label, + filled: true, + fillColor: Colors.white, + suffixIcon: IconButton( + icon: Icon(isHidden ? Icons.visibility_off : Icons.visibility), + onPressed: onToggle, + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.black38), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide(color: Colors.teal), + ), + ), + validator: (value) { + if (value == null || value.isEmpty) return 'Required'; + if (label == 'Confirm Password' && value != _newController.text) { + return 'Passwords do not match'; + } + return null; + }, + ); + } +} diff --git a/lib/profile/update.dart b/lib/profile/update_profile.dart similarity index 100% rename from lib/profile/update.dart rename to lib/profile/update_profile.dart diff --git a/lib/service/profile_provider.dart b/lib/service/profile_provider.dart index d888d35..7c55c6e 100644 --- a/lib/service/profile_provider.dart +++ b/lib/service/profile_provider.dart @@ -4,7 +4,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../profile/profile.dart'; -import '../profile/update.dart'; +import '../profile/update_profile.dart'; class ProfileProvider with ChangeNotifier { String _firstName = ''; @@ -287,6 +287,40 @@ class ProfileProvider with ChangeNotifier { } } + Future updatePassword({ + required String currentPassword, + required String newPassword, + }) async { + try { + final token = await loadAuthToken(); // your stored JWT + final url = Uri.parse('https://bracu-core-backend.vercel.app/api/user/update_password'); // adjust endpoint + + final response = await http.put( + url, + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $token', + }, + body: jsonEncode({ + 'currentPassword': currentPassword, + 'newPassword': newPassword, + }), + ); + + if (response.statusCode == 200) { + return true; + } else { + debugPrint('Failed to update password: ${response.body}'); + return false; + } + } catch (e) { + debugPrint('Error in updatePassword: $e'); + return false; + } + } + + + Future updateProfile({ required String studentId, required String phoneNumber, diff --git a/pubspec.lock b/pubspec.lock index 6d209e9..8066470 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -681,10 +681,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.4" + version: "14.2.5" web: dependency: transitive description: From dfa5ef76203c35e73326b944fe33f99668a5a64f Mon Sep 17 00:00:00 2001 From: MIRaiyan <99160923+MIRaiyan@users.noreply.github.com> Date: Wed, 7 May 2025 15:46:03 +0600 Subject: [PATCH 2/2] update pass(incomplete) --- lib/profile/profile.dart | 79 +++++++++++++++++++++-------- pubspec.lock | 104 ++++++++++++++++++++++++++++++++++----- pubspec.yaml | 1 + 3 files changed, 151 insertions(+), 33 deletions(-) diff --git a/lib/profile/profile.dart b/lib/profile/profile.dart index f86f6ad..f1e72f5 100644 --- a/lib/profile/profile.dart +++ b/lib/profile/profile.dart @@ -4,6 +4,7 @@ import 'package:provider/provider.dart'; import '../service/profile_provider.dart'; import 'update_profile.dart'; import 'update_password.dart'; +import 'package:share_plus/share_plus.dart'; class Profile extends StatelessWidget { const Profile({super.key}); @@ -26,7 +27,6 @@ class Profile extends StatelessWidget { backgroundColor: Colors.white, scrolledUnderElevation: 0, ), - body: SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Column( @@ -49,7 +49,9 @@ class Profile extends StatelessWidget { Center( child: CircleAvatar( radius: 50, - backgroundImage: NetworkImage(profileProvider.profilePicture?.isNotEmpty == true + backgroundImage: NetworkImage(profileProvider + .profilePicture?.isNotEmpty == + true ? profileProvider.profilePicture! : 'https://decisionsystemsgroup.github.io/workshop-html/img/john-doe.jpg'), ), @@ -65,14 +67,25 @@ class Profile extends StatelessWidget { ); } - Widget _buildGridOptions(BuildContext context, ProfileProvider profileProvider) { - List options = ['Update Profile', 'Change Password', 'Location Setting']; - List icons = [Icons.person_outline, Icons.lock_outline, Icons.location_on_outlined]; + Widget _buildGridOptions( + BuildContext context, ProfileProvider profileProvider) { + List options = [ + 'Update Profile', + 'Change Password', + 'Location Setting' + ]; + List icons = [ + Icons.person_outline, + Icons.lock_outline, + Icons.location_on_outlined + ]; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Profile settings", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, letterSpacing: 1.5)), + Text("Profile settings", + style: TextStyle( + fontSize: 18, fontWeight: FontWeight.bold, letterSpacing: 1.5)), SizedBox(height: 10), GridView.builder( shrinkWrap: true, @@ -96,8 +109,10 @@ class Profile extends StatelessWidget { permanentAddress: profileProvider.permanentAddress, presentAddress: profileProvider.currentAddress, emergencyContactName: profileProvider.emergencyName, - emergencyContactRelation: profileProvider.emergencyRelation, - emergencyContactPhoneNumber: profileProvider.emergencyPhoneNumber, + emergencyContactRelation: + profileProvider.emergencyRelation, + emergencyContactPhoneNumber: + profileProvider.emergencyPhoneNumber, ), ), ); @@ -119,11 +134,22 @@ class Profile extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon(icons[index], size: 40, color: Colors.teal,), + Icon( + icons[index], + size: 40, + color: Colors.teal, + ), SizedBox(height: 10), Padding( padding: const EdgeInsets.all(4), - child: Text(options[index], textAlign: TextAlign.center, style: TextStyle(fontSize: 14,),maxLines: 2,), + child: Text( + options[index], + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14, + ), + maxLines: 2, + ), ), ], ), @@ -139,26 +165,35 @@ class Profile extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('App Settings', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, letterSpacing: 1.5)), + Text('App Settings', + style: TextStyle( + fontSize: 18, fontWeight: FontWeight.bold, letterSpacing: 1.5)), SizedBox(height: 10), ListView( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), children: [ - _buildListTile(Icons.notifications_none_outlined, 'Notification Setting', Colors.black, context), - _buildListTile(Icons.star_border_outlined, 'Rate App', Colors.black, context), - _buildListTile(Icons.feedback_outlined, 'Feedback', Colors.black, context), - _buildListTile(Icons.share_outlined, 'Invite', Colors.black, context), + _buildListTile(Icons.notifications_none_outlined, + 'Notification Setting', Colors.black, context), + _buildListTile( + Icons.star_border_outlined, 'Rate App', Colors.black, context), + _buildListTile( + Icons.feedback_outlined, 'Feedback', Colors.black, context), + _buildListTile( + Icons.share_outlined, 'Invite', Colors.black, context), _buildListTile(Icons.help_outline, 'Help', Colors.black, context), - _buildListTile(Icons.privacy_tip_outlined, 'Privacy Policy', Colors.black, context), - _buildListTile(Icons.logout_outlined, 'Logout', Colors.red, context), + _buildListTile(Icons.privacy_tip_outlined, 'Privacy Policy', + Colors.black, context), + _buildListTile( + Icons.logout_outlined, 'Logout', Colors.red, context), ], ), ], ); } - Widget _buildListTile(IconData icon, String title, Color iconColor, BuildContext context) { + Widget _buildListTile( + IconData icon, String title, Color iconColor, BuildContext context) { return Card( color: Colors.white, margin: EdgeInsets.symmetric(vertical: 2), @@ -180,7 +215,8 @@ class Profile extends StatelessWidget { } void _showLogoutDialog(BuildContext context) { - final profileProvider = Provider.of(context, listen: false); + final profileProvider = + Provider.of(context, listen: false); showDialog( context: context, builder: (BuildContext context) { @@ -197,7 +233,8 @@ class Profile extends StatelessWidget { TextButton( onPressed: () { profileProvider.updateLoginStatus(false); - Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => login())); + Navigator.pushReplacement( + context, MaterialPageRoute(builder: (context) => login())); }, child: Text('Logout'), ), @@ -206,4 +243,4 @@ class Profile extends StatelessWidget { }, ); } -} \ No newline at end of file +} diff --git a/pubspec.lock b/pubspec.lock index 8066470..f8e120d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: args - sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 url: "https://pub.dev" source: hosted - version: "2.6.0" + version: "2.7.0" async: dependency: transitive description: @@ -85,10 +85,10 @@ packages: dependency: "direct main" description: name: connectivity_plus - sha256: "04bf81bb0b77de31557b58d052b24b3eee33f09a6e7a8c68a3e247c7df19ec27" + sha256: "051849e2bd7c7b3bc5844ea0d096609ddc3a859890ec3a9ac4a65a2620cc1f99" url: "https://pub.dev" source: hosted - version: "6.1.3" + version: "6.1.4" connectivity_plus_platform_interface: dependency: transitive description: @@ -97,6 +97,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" + url: "https://pub.dev" + source: hosted + version: "0.3.4+2" crypto: dependency: transitive description: @@ -161,6 +169,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" flutter: dependency: "direct main" description: flutter @@ -268,10 +284,10 @@ packages: dependency: "direct main" description: name: http - sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" http_parser: dependency: transitive description: @@ -432,6 +448,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2" + url: "https://pub.dev" + source: hosted + version: "2.2.15" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + url: "https://pub.dev" + source: hosted + version: "2.4.1" path_provider_linux: dependency: transitive description: @@ -484,26 +524,42 @@ packages: dependency: "direct main" description: name: provider - sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" + url: "https://pub.dev" + source: hosted + version: "6.1.5" + share_plus: + dependency: "direct main" + description: + name: share_plus + sha256: b2961506569e28948d75ec346c28775bb111986bb69dc6a20754a457e3d97fa0 url: "https://pub.dev" source: hosted - version: "6.1.2" + version: "11.0.0" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: "1032d392bc5d2095a77447a805aa3f804d2ae6a4d5eef5e6ebb3bd94c1bc19ef" + url: "https://pub.dev" + source: hosted + version: "6.0.0" shared_preferences: dependency: "direct main" description: name: shared_preferences - sha256: "846849e3e9b68f3ef4b60c60cf4b3e02e9321bc7f4d8c4692cf87ffa82fc8a3a" + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" url: "https://pub.dev" source: hosted - version: "2.5.2" + version: "2.5.3" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: a768fc8ede5f0c8e6150476e14f38e2417c0864ca36bb4582be8e21925a03c22 + sha256: "9f9f3d372d4304723e6136663bb291c0b93f5e4c8a4a6314347f481a33bda2b1" url: "https://pub.dev" source: hosted - version: "2.4.6" + version: "2.4.7" shared_preferences_foundation: dependency: transitive description: @@ -557,6 +613,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" stack_trace: dependency: transitive description: @@ -669,6 +733,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.4" + uuid: + dependency: transitive + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" vector_math: dependency: transitive description: @@ -693,6 +765,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + win32: + dependency: transitive + description: + name: win32 + sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e + url: "https://pub.dev" + source: hosted + version: "5.10.1" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index a766b69..a912320 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,6 +51,7 @@ dependencies: location: ^7.0.1 geocoding: ^3.0.0 url_launcher: ^6.3.1 + share_plus: dev_dependencies: flutter_test: