Files
authsec_flutter/lib/screens/profileManagement/Profilesettings.dart
T
2026-10-02 22:52:46 +05:30

550 lines
21 KiB
Dart

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 '../../l10n/app_localizations.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 '../../widgets/language_switcher.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<String, dynamic> userData;
ProfileSettingsScreen({required this.userData});
@override
_ProfileSettingsScreenState createState() => _ProfileSettingsScreenState();
}
class _ProfileSettingsScreenState extends State<ProfileSettingsScreen> {
ApiServiceProfileManagement apiService = ApiServiceProfileManagement();
Uint8List? _imageBytes; // Uint8List to store the picked image data
Uint8List? _fetchedImageBytes; // image bytes fetched from server
String? _imageFileName;
final _formKey = GlobalKey<FormState>();
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<void> _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<void> 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<String, dynamic> 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<void> 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<String, dynamic> 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<void> _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<void> _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<void> _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: <Widget>[
// 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: App language (EN | HI) - switches ARB + dynamic strings together
Text(
AppLocalizations.of(context)!.language,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
SizedBox(height: 10),
const LanguageSwitcher(),
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>[
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>[
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>[
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),
],
),
),
);
}
}