Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 67 additions & 22 deletions lib/profile/profile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ 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';
import 'package:share_plus/share_plus.dart';

class Profile extends StatelessWidget {
const Profile({super.key});
Expand All @@ -25,7 +27,6 @@ class Profile extends StatelessWidget {
backgroundColor: Colors.white,
scrolledUnderElevation: 0,
),

body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
Expand All @@ -48,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'),
),
Expand All @@ -64,14 +67,25 @@ class Profile extends StatelessWidget {
);
}

Widget _buildGridOptions(BuildContext context, ProfileProvider profileProvider) {
List<String> options = ['Update Profile', 'Change Password', 'Location Setting'];
List<IconData> icons = [Icons.person_outline, Icons.lock_outline, Icons.location_on_outlined];
Widget _buildGridOptions(
BuildContext context, ProfileProvider profileProvider) {
List<String> options = [
'Update Profile',
'Change Password',
'Location Setting'
];
List<IconData> 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,
Expand All @@ -95,11 +109,20 @@ 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,
),
),
);
} else if (options[index] == 'Change Password') {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const UpdatePasswordPage(),
),
);
}
},
child: Card(
Expand All @@ -111,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,
),
),
],
),
Expand All @@ -131,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),
Expand All @@ -172,7 +215,8 @@ class Profile extends StatelessWidget {
}

void _showLogoutDialog(BuildContext context) {
final profileProvider = Provider.of<ProfileProvider>(context, listen: false);
final profileProvider =
Provider.of<ProfileProvider>(context, listen: false);
showDialog(
context: context,
builder: (BuildContext context) {
Expand All @@ -189,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'),
),
Expand All @@ -198,4 +243,4 @@ class Profile extends StatelessWidget {
},
);
}
}
}
156 changes: 156 additions & 0 deletions lib/profile/update_password.dart
Original file line number Diff line number Diff line change
@@ -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<UpdatePasswordPage> createState() => _UpdatePasswordPageState();
}

class _UpdatePasswordPageState extends State<UpdatePasswordPage> {
final _formKey = GlobalKey<FormState>();
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<ProfileProvider>(
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;
},
);
}
}
File renamed without changes.
36 changes: 35 additions & 1 deletion lib/service/profile_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '';
Expand Down Expand Up @@ -287,6 +287,40 @@ class ProfileProvider with ChangeNotifier {
}
}

Future<bool> 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<bool> updateProfile({
required String studentId,
required String phoneNumber,
Expand Down
Loading