Fix profile update endpoint/payload and hide dead menu entries
This commit is contained in:
@@ -178,7 +178,8 @@ class _TabbedLayoutComponentState extends State<TabbedLayoutComponent> {
|
|||||||
print("Selected: $result");
|
print("Selected: $result");
|
||||||
},
|
},
|
||||||
itemBuilder: (BuildContext context) => [
|
itemBuilder: (BuildContext context) => [
|
||||||
PopupMenuItem<String>(
|
// COMMENTED OUT: no backend endpoint (service_request/raise_ticket)
|
||||||
|
/*PopupMenuItem<String>(
|
||||||
//value: 'Option 1',
|
//value: 'Option 1',
|
||||||
child: Text(
|
child: Text(
|
||||||
'Raise A Ticket',
|
'Raise A Ticket',
|
||||||
@@ -194,7 +195,7 @@ class _TabbedLayoutComponentState extends State<TabbedLayoutComponent> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),*/
|
||||||
|
|
||||||
PopupMenuItem<String>(
|
PopupMenuItem<String>(
|
||||||
//value: 'Option 1',
|
//value: 'Option 1',
|
||||||
@@ -254,7 +255,8 @@ class _TabbedLayoutComponentState extends State<TabbedLayoutComponent> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
PopupMenuItem<String>(
|
// COMMENTED OUT
|
||||||
|
/*PopupMenuItem<String>(
|
||||||
//value: 'Option 1',
|
//value: 'Option 1',
|
||||||
child: Text(
|
child: Text(
|
||||||
'Setup Screen',
|
'Setup Screen',
|
||||||
@@ -270,9 +272,10 @@ class _TabbedLayoutComponentState extends State<TabbedLayoutComponent> {
|
|||||||
);
|
);
|
||||||
// Add your logic for menu 2 here
|
// Add your logic for menu 2 here
|
||||||
},
|
},
|
||||||
),
|
),*/
|
||||||
|
|
||||||
PopupMenuItem<String>(
|
// COMMENTED OUT: no backend endpoint (/Bookmarks -> 404)
|
||||||
|
/*PopupMenuItem<String>(
|
||||||
//value: 'Option 1',
|
//value: 'Option 1',
|
||||||
child: Text(
|
child: Text(
|
||||||
'BookMark',
|
'BookMark',
|
||||||
@@ -289,9 +292,10 @@ class _TabbedLayoutComponentState extends State<TabbedLayoutComponent> {
|
|||||||
);
|
);
|
||||||
// Add your logic for menu 2 here
|
// Add your logic for menu 2 here
|
||||||
},
|
},
|
||||||
),
|
),*/
|
||||||
|
|
||||||
PopupMenuItem<String>(
|
// COMMENTED OUT
|
||||||
|
/*PopupMenuItem<String>(
|
||||||
//value: 'Option 1',
|
//value: 'Option 1',
|
||||||
child: Text(
|
child: Text(
|
||||||
'System Parameter',
|
'System Parameter',
|
||||||
@@ -307,7 +311,7 @@ class _TabbedLayoutComponentState extends State<TabbedLayoutComponent> {
|
|||||||
);
|
);
|
||||||
// Add your logic for menu 2 here
|
// Add your logic for menu 2 here
|
||||||
},
|
},
|
||||||
),
|
),*/
|
||||||
|
|
||||||
PopupMenuItem<String>(
|
PopupMenuItem<String>(
|
||||||
//value: 'Option 1',
|
//value: 'Option 1',
|
||||||
|
|||||||
@@ -32,9 +32,8 @@ class ProfileSettingsScreen extends StatefulWidget {
|
|||||||
|
|
||||||
class _ProfileSettingsScreenState extends State<ProfileSettingsScreen> {
|
class _ProfileSettingsScreenState extends State<ProfileSettingsScreen> {
|
||||||
ApiServiceProfileManagement apiService = ApiServiceProfileManagement();
|
ApiServiceProfileManagement apiService = ApiServiceProfileManagement();
|
||||||
String? fetchedimageurl =
|
Uint8List? _imageBytes; // Uint8List to store the picked image data
|
||||||
'http://43.205.154.152:30165/assets/images/profile-icon.png';
|
Uint8List? _fetchedImageBytes; // image bytes fetched from server
|
||||||
Uint8List? _imageBytes; // Uint8List to store the image data
|
|
||||||
String? _imageFileName;
|
String? _imageFileName;
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
@@ -48,7 +47,7 @@ class _ProfileSettingsScreenState extends State<ProfileSettingsScreen> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
//fetchProfileImageData();
|
fetchProfileImageData();
|
||||||
fetchUserProfileData();
|
fetchUserProfileData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,6 +65,8 @@ class _ProfileSettingsScreenState extends State<ProfileSettingsScreen> {
|
|||||||
_imageBytes = imageBytes;
|
_imageBytes = imageBytes;
|
||||||
_imageFileName = pickedImage.name; // Store the file name
|
_imageFileName = pickedImage.name; // Store the file name
|
||||||
});
|
});
|
||||||
|
// Upload immediately after pick (same as authsec_flutter_new) and refresh
|
||||||
|
await _submitImage();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
@@ -127,10 +128,13 @@ class _ProfileSettingsScreenState extends State<ProfileSettingsScreen> {
|
|||||||
}
|
}
|
||||||
if (response.statusCode >= 200 && response.statusCode <= 209) {
|
if (response.statusCode >= 200 && response.statusCode <= 209) {
|
||||||
final Map<String, dynamic> jsonData = json.decode(response.body);
|
final Map<String, dynamic> jsonData = json.decode(response.body);
|
||||||
final trustedImageUrl = Uri.dataFromString(jsonData['image'],
|
final String base64Image = jsonData['image'] as String;
|
||||||
mimeType: 'image/*', encoding: Encoding.getByName('utf-8'))
|
// Same as authsec_flutter_new: strip data-url prefix, base64 decode
|
||||||
.toString();
|
final Uint8List imageBytes =
|
||||||
fetchedimageurl = trustedImageUrl;
|
base64Decode(base64Image.split(',').last);
|
||||||
|
setState(() {
|
||||||
|
_fetchedImageBytes = imageBytes;
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
throw Exception('Failed to load data: ${response.statusCode}');
|
throw Exception('Failed to load data: ${response.statusCode}');
|
||||||
}
|
}
|
||||||
@@ -170,6 +174,13 @@ class _ProfileSettingsScreenState extends State<ProfileSettingsScreen> {
|
|||||||
try {
|
try {
|
||||||
final token = await TokenManager.getToken();
|
final token = await TokenManager.getToken();
|
||||||
await apiService.createFile(_imageBytes!, _imageFileName!, token!);
|
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) {
|
} catch (e) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -193,21 +204,27 @@ class _ProfileSettingsScreenState extends State<ProfileSettingsScreen> {
|
|||||||
|
|
||||||
Future<void> _updateProfile() async {
|
Future<void> _updateProfile() async {
|
||||||
if (_formKey.currentState!.validate()) {
|
if (_formKey.currentState!.validate()) {
|
||||||
// Create a JSON object with the form data
|
// 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 = {
|
final profileData = {
|
||||||
|
'userId': userId,
|
||||||
'fullName': fullNameController.text,
|
'fullName': fullNameController.text,
|
||||||
'pronouns': pronounsController.text,
|
|
||||||
'role': roleController.text,
|
|
||||||
'department': departmentController.text,
|
|
||||||
'email': emailController.text,
|
'email': emailController.text,
|
||||||
'aboutMe': aboutMeController.text,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//api/user-profile
|
|
||||||
|
|
||||||
final token = await TokenManager.getToken();
|
final token = await TokenManager.getToken();
|
||||||
final String baseUrl = ApiConstants.baseUrl;
|
final String baseUrl = ApiConstants.baseUrl;
|
||||||
final String apiUrl = '$baseUrl/api/user-profile';
|
final String apiUrl = '$baseUrl/api/updateAppUserDto/$userId';
|
||||||
try {
|
try {
|
||||||
final response = await http.put(Uri.parse(apiUrl),
|
final response = await http.put(Uri.parse(apiUrl),
|
||||||
headers: {
|
headers: {
|
||||||
@@ -218,14 +235,31 @@ class _ProfileSettingsScreenState extends State<ProfileSettingsScreen> {
|
|||||||
if (response.statusCode == 401) {
|
if (response.statusCode == 401) {
|
||||||
LogoutService.logout();
|
LogoutService.logout();
|
||||||
}
|
}
|
||||||
if (response.statusCode <= 209) {
|
if (response.statusCode >= 200 && response.statusCode <= 209) {
|
||||||
print("success");
|
print("success");
|
||||||
|
if (mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(content: Text('Profile updated')),
|
||||||
|
);
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
print(response.statusCode);
|
print(response.statusCode);
|
||||||
|
print(response.body);
|
||||||
|
if (mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(
|
||||||
|
'Failed to update profile: ${response.statusCode}')),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw Exception('Failed to Update: $e');
|
if (mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Failed to update: $e')),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -277,22 +311,21 @@ class _ProfileSettingsScreenState extends State<ProfileSettingsScreen> {
|
|||||||
children: [
|
children: [
|
||||||
CircleAvatar(
|
CircleAvatar(
|
||||||
radius: 70, // Adjust as needed
|
radius: 70, // Adjust as needed
|
||||||
backgroundImage: NetworkImage(
|
backgroundImage: (_imageBytes ?? _fetchedImageBytes) != null
|
||||||
"$fetchedimageurl"), // Replace with your API URL
|
? MemoryImage(_imageBytes ?? _fetchedImageBytes!)
|
||||||
|
: null,
|
||||||
|
child: (_imageBytes ?? _fetchedImageBytes) != null
|
||||||
|
? null
|
||||||
|
: const Icon(Icons.person, size: 60),
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
SizedBox(height: 10),
|
||||||
CustomButton(
|
CustomButton(
|
||||||
height: getVerticalSize(50),
|
height: getVerticalSize(50),
|
||||||
text: _imageBytes == null
|
text: "Pick a Profile Photo",
|
||||||
? "Pick a Profile Photo"
|
|
||||||
: 'Upload Success',
|
|
||||||
margin: getMargin(top: 24, bottom: 5),
|
margin: getMargin(top: 24, bottom: 5),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
if (_imageBytes != null) {
|
// Pick + auto-upload, same as authsec_flutter_new
|
||||||
_submitImage();
|
await _uploadImageFile();
|
||||||
} else {
|
|
||||||
_uploadImageFile();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -330,9 +363,6 @@ class _ProfileSettingsScreenState extends State<ProfileSettingsScreen> {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
onChanged: (value) {
|
|
||||||
fullNameController.text = value;
|
|
||||||
},
|
|
||||||
margin: getMargin(top: 6))
|
margin: getMargin(top: 6))
|
||||||
])),
|
])),
|
||||||
Padding(
|
Padding(
|
||||||
@@ -349,9 +379,6 @@ class _ProfileSettingsScreenState extends State<ProfileSettingsScreen> {
|
|||||||
focusNode: FocusNode(),
|
focusNode: FocusNode(),
|
||||||
hintText: "Enter Pronouns",
|
hintText: "Enter Pronouns",
|
||||||
controller: pronounsController,
|
controller: pronounsController,
|
||||||
onChanged: (value) {
|
|
||||||
pronounsController.text = value;
|
|
||||||
},
|
|
||||||
margin: getMargin(top: 6))
|
margin: getMargin(top: 6))
|
||||||
])),
|
])),
|
||||||
Padding(
|
Padding(
|
||||||
@@ -368,9 +395,6 @@ class _ProfileSettingsScreenState extends State<ProfileSettingsScreen> {
|
|||||||
focusNode: FocusNode(),
|
focusNode: FocusNode(),
|
||||||
hintText: "Enter Role",
|
hintText: "Enter Role",
|
||||||
controller: roleController,
|
controller: roleController,
|
||||||
onChanged: (value) {
|
|
||||||
roleController.text = value;
|
|
||||||
},
|
|
||||||
margin: getMargin(top: 6))
|
margin: getMargin(top: 6))
|
||||||
])),
|
])),
|
||||||
Padding(
|
Padding(
|
||||||
@@ -387,9 +411,6 @@ class _ProfileSettingsScreenState extends State<ProfileSettingsScreen> {
|
|||||||
focusNode: FocusNode(),
|
focusNode: FocusNode(),
|
||||||
hintText: "Enter Department or Team",
|
hintText: "Enter Department or Team",
|
||||||
controller: departmentController,
|
controller: departmentController,
|
||||||
onChanged: (value) {
|
|
||||||
departmentController.text = value;
|
|
||||||
},
|
|
||||||
margin: getMargin(top: 6))
|
margin: getMargin(top: 6))
|
||||||
])),
|
])),
|
||||||
Padding(
|
Padding(
|
||||||
@@ -412,9 +433,6 @@ class _ProfileSettingsScreenState extends State<ProfileSettingsScreen> {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
onChanged: (value) {
|
|
||||||
emailController.text = value;
|
|
||||||
},
|
|
||||||
margin: getMargin(top: 6))
|
margin: getMargin(top: 6))
|
||||||
])),
|
])),
|
||||||
Padding(
|
Padding(
|
||||||
@@ -432,9 +450,6 @@ class _ProfileSettingsScreenState extends State<ProfileSettingsScreen> {
|
|||||||
hintText: "About Me",
|
hintText: "About Me",
|
||||||
controller: aboutMeController,
|
controller: aboutMeController,
|
||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
onChanged: (value) {
|
|
||||||
aboutMeController.text = value;
|
|
||||||
},
|
|
||||||
margin: getMargin(top: 6))
|
margin: getMargin(top: 6))
|
||||||
])),
|
])),
|
||||||
CustomButton(
|
CustomButton(
|
||||||
|
|||||||
Reference in New Issue
Block a user