onufitness_mobile/lib/screens/accounts/Controllers/edit_account_controller.dart
2026-01-13 11:36:24 +05:30

362 lines
11 KiB
Dart

import 'dart:convert';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart';
import 'package:onufitness/constants/api_endpoints.dart';
import 'package:onufitness/models/user_details_response_model.dart';
import 'package:onufitness/services/api_services/base_api_services.dart';
import 'package:onufitness/utils/custom_sneakbar.dart';
class EditAccountController extends GetxController {
// User data
UserDetailsResponseModel userDetailsResponseModel =
UserDetailsResponseModel();
final ImagePicker _picker = ImagePicker();
var profileImage = Rx<XFile?>(null);
var isUploadingImage = false.obs;
TextEditingController firstNameController = TextEditingController();
TextEditingController lastNameController = TextEditingController();
TextEditingController aboutMeController = TextEditingController();
TextEditingController facebookController = TextEditingController();
TextEditingController instagramController = TextEditingController();
TextEditingController linkedInController = TextEditingController();
TextEditingController twitterController = TextEditingController();
TextEditingController phoneNumberController = TextEditingController();
// Form Data
var selectedDate = "".obs;
var selectedGender = 'Male'.obs;
var selectedCountryCode = 'IN'.obs;
// Loading states
var isUpdating = false.obs;
var isLoading = false.obs;
@override
void onInit() {
firstNameController = TextEditingController();
lastNameController = TextEditingController();
aboutMeController = TextEditingController();
facebookController = TextEditingController();
instagramController = TextEditingController();
linkedInController = TextEditingController();
twitterController = TextEditingController();
phoneNumberController = TextEditingController();
super.onInit();
loadUserData();
}
RxBool fetchUserDetailsLoading = false.obs;
Future<bool> fetchUserDetails() async {
fetchUserDetailsLoading(true);
update();
try {
final response = await ApiBase.getRequest(
extendedURL: ApiUrl.fetchUserDetails,
);
if (response.statusCode == 200 || response.statusCode == 201) {
userDetailsResponseModel = userDetailsResponseModelFromJson(
response.body,
);
_populateFormFields();
update();
return true;
} else {
customSnackbar(title: "Error", message: "Failed to fetch user details");
return false;
}
} catch (e) {
log("Exception: $e");
customSnackbar(title: "Error", message: "Failed to fetch user details");
return false;
} finally {
fetchUserDetailsLoading(false);
update();
}
}
Future<void> loadUserData() async {
isLoading.value = true;
final success = await fetchUserDetails();
if (success) {
_populateFormFields();
}
isLoading.value = false;
}
void _populateFormFields() {
final userData = userDetailsResponseModel.data;
if (userData == null) return;
firstNameController.text = userData.firstName ?? '';
lastNameController.text = userData.lastName ?? '';
aboutMeController.text = userData.aboutMe ?? '';
facebookController.text = userData.facebookSocialLink ?? '';
instagramController.text = userData.instagramSocialLink ?? '';
linkedInController.text = userData.linkedInSocialLink ?? '';
twitterController.text = userData.twitterSocialLink ?? '';
phoneNumberController.text = userData.phoneNumber ?? '';
selectedGender.value = userData.gender ?? 'Male';
selectedCountryCode.value = userData.countryCode ?? 'IN';
if (userData.dateOfBirth != null) {
selectedDate.value = DateFormat(
'yyyy-MM-dd',
).format(userData.dateOfBirth!);
}
}
// Simple age validation...........................................................................
bool _isOver18(DateTime birthDate) {
final now = DateTime.now();
final age = now.year - birthDate.year;
if (now.month < birthDate.month ||
(now.month == birthDate.month && now.day < birthDate.day)) {
return (age - 1) >= 18;
}
return age >= 18;
}
Future<void> pickDate(BuildContext context) async {
final DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now().subtract(Duration(days: 18 * 365)),
firstDate: DateTime(1800),
lastDate: DateTime.now(),
);
if (pickedDate != null) {
if (_isOver18(pickedDate)) {
selectedDate.value = DateFormat('yyyy-MM-dd').format(pickedDate);
} else {
customSnackbar(
title: "Age Restriction",
message: "You must be at least 18 years old to proceed.",
duration: 2,
);
}
}
}
//............................................................................................
Future<bool> updateUserProfile() async {
isUpdating.value = true;
try {
final requestBody = {
"firstName": firstNameController.text.trim(),
"lastName": lastNameController.text.trim(),
"aboutMe": aboutMeController.text.trim(),
"dateOfBirth": selectedDate.value,
"gender": selectedGender.value,
"facebookSocialLink": facebookController.text.trim(),
"linkedInSocialLink": linkedInController.text.trim(),
"instagramSocialLink": instagramController.text.trim(),
"twitterSocialLink": twitterController.text.trim(),
"phoneNumber": phoneNumberController.text.trim(),
"countryCode": selectedCountryCode.value,
};
final response = await ApiBase.patchRequest(
extendedURL: "/api/Users/update-user",
body: requestBody,
sendHeaders: true,
);
if (response.statusCode == 200 || response.statusCode == 204) {
// Refresh user data after successful update
await fetchUserDetails();
update();
Get.back();
Future.delayed(const Duration(milliseconds: 300), () {
customSnackbar(
title: 'Success',
message: 'Profile updated successfully',
duration: 1,
);
});
return true;
} else {
customSnackbar(
title: 'Failed',
message: 'Failed to update profile details',
);
return false;
}
} catch (e) {
log('Update profile error: $e');
customSnackbar(
title: 'Failed',
message: 'Failed to update profile details',
);
return false;
} finally {
isUpdating.value = false;
}
}
Future<void> uploadProfilePictureAPIcall() async {
if (profileImage.value == null) return;
isUploadingImage.value = true;
try {
final userData = userDetailsResponseModel.data;
final userId = userData?.userId;
if (userId == null) {
customSnackbar(title: "Error", message: "User ID not found");
return;
}
final response = await ApiBase.postSingleFileRequest(
extendedURL: "${ApiUrl.uploadProfileImage}/$userId",
sendHeaders: false,
file: profileImage.value!,
fileNameKey: 'file',
);
if (response.statusCode == 200 || response.statusCode == 201) {
// Refresh user data to get updated profile image
await fetchUserDetails();
customSnackbar(
title: "Success",
message: "Image uploaded successfully",
duration: 1,
);
} else {
final errorMessage =
jsonDecode(response.body)["message"] ?? "Upload failed";
customSnackbar(title: "Error", message: errorMessage);
}
} catch (e) {
log("Upload image error: $e");
customSnackbar(title: "Error", message: "Failed to upload image");
} finally {
isUploadingImage.value = false;
}
}
// Getters for UI
String? get userProfileImageUrl =>
userDetailsResponseModel.data?.userProfilePic;
bool get hasNetworkImage => userProfileImageUrl?.isNotEmpty == true;
//........... Delete user Account............................................................................
RxBool isAccountDeleting = false.obs;
Future<bool> deleteUserAccount() async {
bool ret = false;
isAccountDeleting(true);
try {
final response = await ApiBase.patchRequest(
extendedURL: "/api/Users/delete-user-account",
);
log(response.body.toString());
if (response.statusCode == 200 || response.statusCode == 201) {
ret = true;
} else {
ret = false;
customSnackbar(
title: "Deletion Failed",
message:
"We were unable to delete your account. Please try again later.",
duration: 2,
);
}
} catch (e) {
customSnackbar(
title: "Deletion Failed",
message:
"We were unable to delete your account. Please try again later.",
duration: 2,
);
ret = false;
} finally {
isAccountDeleting(false);
}
return ret;
}
// Form validation............................................................................
bool validateForm() {
if (firstNameController.text.trim().isEmpty) {
customSnackbar(
title: "First Name empty",
message: "Please enter your first name",
duration: 2,
);
return false;
}
if (lastNameController.text.trim().isEmpty) {
customSnackbar(
title: "Last Name empty",
message: "Please enter your last name",
duration: 2,
);
return false;
}
if (phoneNumberController.text.trim().isEmpty) {
customSnackbar(
title: "Phone Number empty",
message: "Please enter your phone number",
duration: 2,
);
return false;
}
if (!isValidName(firstNameController.text.trim())) {
customSnackbar(
title: "First Name Input error",
message:
"First name should only contain letters, spaces, apostrophes, and hyphens",
duration: 2,
);
return false;
}
if (!isValidName(lastNameController.text.trim())) {
customSnackbar(
title: "Last Name Input error",
message:
"Last name should only contain letters, spaces, apostrophes, and hyphens",
duration: 2,
);
return false;
}
return true;
}
bool isValidName(String name) {
final nameRegex = RegExp(r"^[a-zA-Z\s\-\']+$");
return nameRegex.hasMatch(name) && name.length >= 2;
}
@override
void onClose() {
firstNameController.dispose();
lastNameController.dispose();
aboutMeController.dispose();
facebookController.dispose();
instagramController.dispose();
linkedInController.dispose();
twitterController.dispose();
phoneNumberController.dispose();
super.onClose();
}
}