import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import '../../Utils/image_constant.dart'; import '../../Utils/size_utils.dart'; import '../../providers/token_manager.dart'; import '../../resources/api_constants.dart'; import '../../theme/app_style.dart'; import '../../widgets/app_bar/appbar_image.dart'; import '../../widgets/app_bar/appbar_title.dart'; import '../../widgets/app_bar/custom_app_bar.dart'; import '../../widgets/custom_button.dart'; import '../../widgets/custom_text_form_field.dart'; import '../Login Screen/login_screen.dart'; import '../LogoutService/Logoutservice.dart'; import 'apiserviceprofilemanagement.dart'; import 'package:http/http.dart' as http; import 'changepassword.dart'; class ProfileSettingsScreen extends StatefulWidget { final Map userData; ProfileSettingsScreen({required this.userData}); @override _ProfileSettingsScreenState createState() => _ProfileSettingsScreenState(); } class _ProfileSettingsScreenState extends State { ApiServiceProfileManagement apiService = ApiServiceProfileManagement(); Uint8List? _imageBytes; // Uint8List to store the picked image data Uint8List? _fetchedImageBytes; // image bytes fetched from server String? _imageFileName; final _formKey = GlobalKey(); TextEditingController fullNameController = TextEditingController(); TextEditingController pronounsController = TextEditingController(); TextEditingController roleController = TextEditingController(); TextEditingController departmentController = TextEditingController(); TextEditingController emailController = TextEditingController(); TextEditingController aboutMeController = TextEditingController(); @override void initState() { super.initState(); fetchProfileImageData(); fetchUserProfileData(); } Future _uploadImageFile() async { final imagePicker = ImagePicker(); try { final pickedImage = await imagePicker.pickImage(source: ImageSource.gallery); if (pickedImage != null) { final imageBytes = await pickedImage.readAsBytes(); setState(() { _imageBytes = imageBytes; _imageFileName = pickedImage.name; // Store the file name }); // Upload immediately after pick (same as authsec_flutter_new) and refresh await _submitImage(); } } catch (e) { print(e); } } //api/user-profile Future fetchUserProfileData() async { final token = await TokenManager.getToken(); final String baseUrl = ApiConstants.baseUrl; final String apiUrl = '$baseUrl/api/user-profile'; try { final response = await http.get( Uri.parse(apiUrl), headers: { 'Authorization': 'Bearer $token', }, ); if (response.statusCode == 401) { LogoutService.logout(); } if (response.statusCode >= 200 && response.statusCode <= 209) { final Map jsonData = json.decode(response.body); setState(() { fullNameController.text = jsonData['fullName'] != null ? jsonData['fullName'] : ''; pronounsController.text = jsonData['pronouns'] != null ? jsonData['pronouns'] : ''; roleController.text = jsonData['role'] != null ? jsonData['role'] : ''; departmentController.text = jsonData['department'] != null ? jsonData['department'] : ''; emailController.text = jsonData['email'] != null ? jsonData['email'] : ''; aboutMeController.text = jsonData['about'] != null ? jsonData['about'] : ''; }); } else { throw Exception('Failed to load data: ${response.statusCode}'); } } catch (e) { throw Exception('Failed to load data: $e'); } } Future fetchProfileImageData() async { final token = await TokenManager.getToken(); final String baseUrl = ApiConstants.baseUrl; final String apiUrl = '$baseUrl/api/retrieve-image'; try { final response = await http.get( Uri.parse(apiUrl), headers: { 'Authorization': 'Bearer $token', }, ); if (response.statusCode == 401) { LogoutService.logout(); } if (response.statusCode >= 200 && response.statusCode <= 209) { final Map jsonData = json.decode(response.body); final String base64Image = jsonData['image'] as String; // Same as authsec_flutter_new: strip data-url prefix, base64 decode final Uint8List imageBytes = base64Decode(base64Image.split(',').last); setState(() { _fetchedImageBytes = imageBytes; }); } else { throw Exception('Failed to load data: ${response.statusCode}'); } } catch (e) { throw Exception('Failed to load data: $e'); } } Future _submitImage() async { if (_imageBytes == null) { // Show an error message if no image is selected showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: const Text('Error'), content: Text('Please select an image.'), actions: [ TextButton( child: const Text('OK'), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); return; } if (_imageFileName == null) { // Handle the case where _imageFileName is null (no file name provided) print('File name is missing.'); return; } try { final token = await TokenManager.getToken(); await apiService.createFile(_imageBytes!, _imageFileName!, token!); // Refresh displayed image (same as authsec_flutter_new getProfileImg) await fetchProfileImageData(); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Profile image updated')), ); } } catch (e) { showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: const Text('Error'), content: Text('Failed to upload image: $e'), actions: [ TextButton( child: const Text('OK'), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); } } Future _updateProfile() async { if (_formKey.currentState!.validate()) { // Same contract as authsec_flutter_new: PUT /api/updateAppUserDto/{userId} // with only backend-persisted keys (AppUserDto: fullName, email). final dynamic rawUserId = widget.userData['userId']; if (rawUserId == null) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('User id missing, please re-login')), ); } return; } final String userId = rawUserId.toString(); final profileData = { 'userId': userId, 'fullName': fullNameController.text, 'email': emailController.text, }; final token = await TokenManager.getToken(); final String baseUrl = ApiConstants.baseUrl; final String apiUrl = '$baseUrl/api/updateAppUserDto/$userId'; try { final response = await http.put(Uri.parse(apiUrl), headers: { 'Authorization': 'Bearer $token', 'Content-Type': 'application/json', }, body: json.encode(profileData)); if (response.statusCode == 401) { LogoutService.logout(); } if (response.statusCode >= 200 && response.statusCode <= 209) { print("success"); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Profile updated')), ); Navigator.of(context).pop(); } } else { print(response.statusCode); print(response.body); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Failed to update profile: ${response.statusCode}')), ); } } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Failed to update: $e')), ); } } } } Future _logoutUser() async { try { String logouturl = "${ApiConstants.baseUrl}/token/logout"; var response = await http.get(Uri.parse(logouturl)); if (response.statusCode <= 209) { // ignore: use_build_context_synchronously Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (context) => LoginScreen()), (route) => false, // Remove all routes from the stack ); } else { const Text('failed to logout'); } } catch (error) { print('Error occurred during logout: $error'); } } @override Widget build(BuildContext context) { return Scaffold( appBar: CustomAppBar( height: getVerticalSize(49), leadingWidth: 40, leading: AppbarImage( height: getSize(24), width: getSize(24), svgPath: ImageConstant.imgArrowleftBlueGray900, margin: getMargin(left: 16, top: 12, bottom: 13), onTap: () { Navigator.pop(context); }), centerTitle: true, title: AppbarTitle(text: "My Profile Settings")), body: SingleChildScrollView( padding: EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Section: Show Profile Photo Center( child: Column( children: [ CircleAvatar( radius: 70, // Adjust as needed backgroundImage: (_imageBytes ?? _fetchedImageBytes) != null ? MemoryImage(_imageBytes ?? _fetchedImageBytes!) : null, child: (_imageBytes ?? _fetchedImageBytes) != null ? null : const Icon(Icons.person, size: 60), ), SizedBox(height: 10), CustomButton( height: getVerticalSize(50), text: "Pick a Profile Photo", margin: getMargin(top: 24, bottom: 5), onTap: () async { // Pick + auto-upload, same as authsec_flutter_new await _uploadImageFile(); }, ), ], ), ), SizedBox(height: 20), // Section: Profile Form Text( 'Your Profile Information', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), SizedBox(height: 10), Form( key: _formKey, child: Column( children: [ Padding( padding: getPadding(top: 19), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ Text("Your Full Name", overflow: TextOverflow.ellipsis, textAlign: TextAlign.left, style: AppStyle.txtGilroyMedium16Bluegray900), CustomTextFormField( focusNode: FocusNode(), controller: fullNameController, hintText: "Enter Full Name", validator: (value) { if (value!.isEmpty) { return 'Please enter your full name'; } return null; }, margin: getMargin(top: 6)) ])), Padding( padding: getPadding(top: 19), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ Text("Pronouns", overflow: TextOverflow.ellipsis, textAlign: TextAlign.left, style: AppStyle.txtGilroyMedium16Bluegray900), CustomTextFormField( focusNode: FocusNode(), hintText: "Enter Pronouns", controller: pronounsController, margin: getMargin(top: 6)) ])), Padding( padding: getPadding(top: 19), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ Text("Role", overflow: TextOverflow.ellipsis, textAlign: TextAlign.left, style: AppStyle.txtGilroyMedium16Bluegray900), CustomTextFormField( focusNode: FocusNode(), hintText: "Enter Role", controller: roleController, margin: getMargin(top: 6)) ])), Padding( padding: getPadding(top: 19), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ Text("Department or Team", overflow: TextOverflow.ellipsis, textAlign: TextAlign.left, style: AppStyle.txtGilroyMedium16Bluegray900), CustomTextFormField( focusNode: FocusNode(), hintText: "Enter Department or Team", controller: departmentController, margin: getMargin(top: 6)) ])), Padding( padding: getPadding(top: 19), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ Text("Email", overflow: TextOverflow.ellipsis, textAlign: TextAlign.left, style: AppStyle.txtGilroyMedium16Bluegray900), CustomTextFormField( focusNode: FocusNode(), hintText: "Enter Email", controller: emailController, validator: (value) { if (value!.isEmpty || !value!.contains('@')) { return 'Please enter a valid email address'; } return null; }, margin: getMargin(top: 6)) ])), Padding( padding: getPadding(top: 19), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ Text("About Me", overflow: TextOverflow.ellipsis, textAlign: TextAlign.left, style: AppStyle.txtGilroyMedium16Bluegray900), CustomTextFormField( focusNode: FocusNode(), hintText: "About Me", controller: aboutMeController, maxLines: 3, margin: getMargin(top: 6)) ])), CustomButton( height: getVerticalSize(50), text: "Update Profile", margin: getMargin(top: 24, bottom: 5), onTap: _updateProfile, ), ], ), ), SizedBox(height: 20), GestureDetector( onTap: () { Navigator.push( context, MaterialPageRoute( builder: (context) => ResetPasswordScreen( userData: widget.userData, userEmail: emailController.text, ), //go to get all entity ), ); }, child: RichText( text: TextSpan( children: [ TextSpan( text: "Password:Change Password for your account Change password", style: TextStyle( fontWeight: FontWeight.normal, color: Colors.blue, ), ), ], ), ), ), SizedBox(height: 20), GestureDetector( onTap: () { print("change password"); }, child: RichText( text: TextSpan( children: [ TextSpan( text: 'Security:Logout of all sessions except this current browser Logout other sessions', style: TextStyle( fontWeight: FontWeight.normal, color: Colors.blue, ), ), ], ), ), ), SizedBox(height: 20), GestureDetector( onTap: () { _logoutUser(); }, child: RichText( text: TextSpan( children: [ TextSpan( text: 'Deactivation:Remove access to all organizations and workspace in cloudnsure Deactivate account', style: TextStyle( fontWeight: FontWeight.normal, color: Colors.blue, ), ), ], ), ), ), SizedBox(height: 20), ], ), ), ); } }