marco.pms.mobileapp/lib/controller/directory/add_contact_controller.dart

293 lines
9.0 KiB
Dart

import 'package:get/get.dart';
import 'package:marco/helpers/services/api_service.dart';
import 'package:marco/helpers/services/app_logger.dart';
import 'package:marco/helpers/widgets/my_snackbar.dart';
class AddContactController extends GetxController {
final RxList<String> categories = <String>[].obs;
final RxList<String> buckets = <String>[].obs;
final RxList<String> globalProjects = <String>[].obs;
final RxList<String> tags = <String>[].obs;
final RxString selectedCategory = ''.obs;
final RxString selectedBucket = ''.obs;
final RxString selectedProject = ''.obs;
final RxList<String> enteredTags = <String>[].obs;
final RxList<String> filteredSuggestions = <String>[].obs;
final RxList<String> organizationNames = <String>[].obs;
final RxList<String> filteredOrgSuggestions = <String>[].obs;
final RxMap<String, String> categoriesMap = <String, String>{}.obs;
final RxMap<String, String> bucketsMap = <String, String>{}.obs;
final RxMap<String, String> projectsMap = <String, String>{}.obs;
final RxMap<String, String> tagsMap = <String, String>{}.obs;
final RxBool isInitialized = false.obs;
final RxList<String> selectedProjects = <String>[].obs;
@override
void onInit() {
super.onInit();
logSafe("AddContactController initialized", level: LogLevel.debug);
fetchInitialData();
}
Future<void> fetchInitialData() async {
logSafe("Fetching initial dropdown data", level: LogLevel.debug);
await Future.wait([
fetchBuckets(),
fetchGlobalProjects(),
fetchTags(),
fetchCategories(),
fetchOrganizationNames(),
]);
// ✅ Mark initialization as done
isInitialized.value = true;
}
void resetForm() {
selectedCategory.value = '';
selectedProject.value = '';
selectedBucket.value = '';
enteredTags.clear();
filteredSuggestions.clear();
filteredOrgSuggestions.clear();
selectedProjects.clear();
}
Future<void> fetchBuckets() async {
try {
final response = await ApiService.getContactBucketList();
if (response != null && response['data'] is List) {
final names = <String>[];
for (var item in response['data']) {
if (item['name'] != null && item['id'] != null) {
bucketsMap[item['name']] = item['id'].toString();
names.add(item['name']);
}
}
buckets.assignAll(names);
logSafe("Fetched \${names.length} buckets");
}
} catch (e) {
logSafe("Failed to fetch buckets: \$e", level: LogLevel.error);
}
}
Future<void> fetchOrganizationNames() async {
try {
final orgs = await ApiService.getOrganizationList();
organizationNames.assignAll(orgs);
logSafe("Fetched \${orgs.length} organization names");
} catch (e) {
logSafe("Failed to load organization names: \$e", level: LogLevel.error);
}
}
Future<void> submitContact({
String? id,
required String name,
required String organization,
required List<Map<String, String>> emails,
required List<Map<String, String>> phones,
required String address,
required String description,
}) async {
final categoryId = categoriesMap[selectedCategory.value];
final bucketId = bucketsMap[selectedBucket.value];
final projectIds = selectedProjects
.map((name) => projectsMap[name])
.whereType<String>()
.toList();
// === Required validations only for name, organization, and bucket ===
if (name.trim().isEmpty) {
showAppSnackbar(
title: "Missing Name",
message: "Please enter the contact name.",
type: SnackbarType.warning,
);
return;
}
if (organization.trim().isEmpty) {
showAppSnackbar(
title: "Missing Organization",
message: "Please enter the organization name.",
type: SnackbarType.warning,
);
return;
}
if (selectedBucket.value.trim().isEmpty || bucketId == null) {
showAppSnackbar(
title: "Missing Bucket",
message: "Please select a bucket.",
type: SnackbarType.warning,
);
return;
}
// === Build body (include optional fields if available) ===
try {
final tagObjects = enteredTags.map((tagName) {
final tagId = tagsMap[tagName];
return tagId != null
? {"id": tagId, "name": tagName}
: {"name": tagName};
}).toList();
final body = {
if (id != null) "id": id,
"name": name.trim(),
"organization": organization.trim(),
if (selectedCategory.value.isNotEmpty && categoryId != null)
"contactCategoryId": categoryId,
if (projectIds.isNotEmpty) "projectIds": projectIds,
"bucketIds": [bucketId],
if (enteredTags.isNotEmpty) "tags": tagObjects,
if (emails.isNotEmpty) "contactEmails": emails,
if (phones.isNotEmpty) "contactPhones": phones,
if (address.trim().isNotEmpty) "address": address.trim(),
if (description.trim().isNotEmpty) "description": description.trim(),
};
logSafe("${id != null ? 'Updating' : 'Creating'} contact");
final response = id != null
? await ApiService.updateContact(id, body)
: await ApiService.createContact(body);
if (response == true) {
Get.back(result: true);
showAppSnackbar(
title: "Success",
message: id != null
? "Contact updated successfully"
: "Contact created successfully",
type: SnackbarType.success,
);
} else {
showAppSnackbar(
title: "Error",
message: "Failed to ${id != null ? 'update' : 'create'} contact",
type: SnackbarType.error,
);
}
} catch (e) {
logSafe("Submit contact error: $e", level: LogLevel.error);
showAppSnackbar(
title: "Error",
message: "Something went wrong",
type: SnackbarType.error,
);
}
}
void filterOrganizationSuggestions(String query) {
if (query.trim().isEmpty) {
filteredOrgSuggestions.clear();
return;
}
final lower = query.toLowerCase();
filteredOrgSuggestions.assignAll(
organizationNames
.where((name) => name.toLowerCase().contains(lower))
.toList(),
);
logSafe("Filtered organization suggestions for: \$query",
level: LogLevel.debug);
}
Future<void> fetchGlobalProjects() async {
try {
final response = await ApiService.getGlobalProjects();
if (response != null) {
final names = <String>[];
for (var item in response) {
final name = item['name']?.toString().trim();
final id = item['id']?.toString().trim();
if (name != null && id != null && name.isNotEmpty) {
projectsMap[name] = id;
names.add(name);
}
}
globalProjects.assignAll(names);
logSafe("Fetched \${names.length} global projects");
}
} catch (e) {
logSafe("Failed to fetch global projects: \$e", level: LogLevel.error);
}
}
Future<void> fetchTags() async {
try {
final response = await ApiService.getContactTagList();
if (response != null && response['data'] is List) {
tags.assignAll(List<String>.from(
response['data'].map((e) => e['name'] ?? '').where((e) => e != ''),
));
logSafe("Fetched \${tags.length} tags");
}
} catch (e) {
logSafe("Failed to fetch tags: \$e", level: LogLevel.error);
}
}
void filterSuggestions(String query) {
if (query.trim().isEmpty) {
filteredSuggestions.clear();
return;
}
final lower = query.toLowerCase();
filteredSuggestions.assignAll(
tags
.where((tag) =>
tag.toLowerCase().contains(lower) && !enteredTags.contains(tag))
.toList(),
);
logSafe("Filtered tag suggestions for: \$query", level: LogLevel.debug);
}
void clearSuggestions() {
filteredSuggestions.clear();
logSafe("Cleared tag suggestions", level: LogLevel.debug);
}
Future<void> fetchCategories() async {
try {
final response = await ApiService.getContactCategoryList();
if (response != null && response['data'] is List) {
final names = <String>[];
for (var item in response['data']) {
final name = item['name']?.toString().trim();
final id = item['id']?.toString().trim();
if (name != null && id != null && name.isNotEmpty) {
categoriesMap[name] = id;
names.add(name);
}
}
categories.assignAll(names);
logSafe("Fetched \${names.length} contact categories");
}
} catch (e) {
logSafe("Failed to fetch categories: \$e", level: LogLevel.error);
}
}
void addEnteredTag(String tag) {
if (tag.trim().isNotEmpty && !enteredTags.contains(tag.trim())) {
enteredTags.add(tag.trim());
logSafe("Added tag: \$tag", level: LogLevel.debug);
}
}
void removeEnteredTag(String tag) {
enteredTags.remove(tag);
logSafe("Removed tag: \$tag", level: LogLevel.debug);
}
}