marco.pms.mobileapp/lib/controller/directory/directory_controller.dart
Vaibhav Surve a0f1602f4e feat(directory): add contact profile and directory management features
- Implemented ContactProfileResponse and related models for handling contact details.
- Created ContactTagResponse and ContactTag models for managing contact tags.
- Added DirectoryCommentResponse and DirectoryComment models for comment management.
- Developed DirectoryFilterBottomSheet for filtering contacts.
- Introduced OrganizationListModel for organization data handling.
- Updated routes to include DirectoryMainScreen.
- Enhanced DashboardScreen to navigate to the new directory page.
- Created ContactDetailScreen for displaying detailed contact information.
- Developed DirectoryMainScreen for managing and displaying contacts.
- Added dependencies for font_awesome_flutter and flutter_html in pubspec.yaml.
2025-07-02 15:57:39 +05:30

166 lines
5.1 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/model/directory/contact_model.dart';
import 'package:marco/model/directory/contact_bucket_list_model.dart';
import 'package:marco/model/directory/directory_comment_model.dart';
class DirectoryController extends GetxController {
RxList<ContactModel> allContacts = <ContactModel>[].obs;
RxList<ContactModel> filteredContacts = <ContactModel>[].obs;
RxList<ContactCategory> contactCategories = <ContactCategory>[].obs;
RxList<String> selectedCategories = <String>[].obs;
RxList<String> selectedBuckets = <String>[].obs;
RxBool isActive = true.obs;
RxBool isLoading = false.obs;
RxList<ContactBucket> contactBuckets = <ContactBucket>[].obs;
RxString searchQuery = ''.obs;
RxBool showFabMenu = false.obs;
RxMap<String, List<DirectoryComment>> contactCommentsMap =
<String, List<DirectoryComment>>{}.obs;
@override
void onInit() {
super.onInit();
fetchContacts();
fetchBuckets();
}
void extractCategoriesFromContacts() {
final uniqueCategories = <String, ContactCategory>{};
for (final contact in allContacts) {
final category = contact.contactCategory;
if (category != null && !uniqueCategories.containsKey(category.id)) {
uniqueCategories[category.id] = category;
}
}
contactCategories.value = uniqueCategories.values.toList();
}
Future<void> fetchCommentsForContact(String contactId) async {
try {
final data = await ApiService.getDirectoryComments(contactId);
logSafe("Fetched comments for contact $contactId: $data");
if (data != null ) {
final comments = data.map((e) => DirectoryComment.fromJson(e)).toList();
contactCommentsMap[contactId] = comments;
} else {
contactCommentsMap[contactId] = [];
}
contactCommentsMap.refresh();
} catch (e) {
logSafe("Error fetching comments for contact $contactId: $e",
level: LogLevel.error);
contactCommentsMap[contactId] = [];
contactCommentsMap.refresh();
}
}
Future<void> fetchBuckets() async {
try {
final response = await ApiService.getContactBucketList();
if (response != null && response['data'] is List) {
final buckets = (response['data'] as List)
.map((e) => ContactBucket.fromJson(e))
.toList();
contactBuckets.assignAll(buckets);
} else {
contactBuckets.clear();
}
} catch (e) {
logSafe("Bucket fetch error: $e", level: LogLevel.error);
}
}
Future<void> fetchContacts({bool active = true}) async {
try {
isLoading.value = true;
final response = await ApiService.getDirectoryData(isActive: active);
if (response != null) {
final contacts = response.map((e) => ContactModel.fromJson(e)).toList();
allContacts.assignAll(contacts);
extractCategoriesFromContacts();
applyFilters();
} else {
allContacts.clear();
filteredContacts.clear();
}
} catch (e) {
logSafe("Directory fetch error: $e", level: LogLevel.error);
} finally {
isLoading.value = false;
}
}
void applyFilters() {
final query = searchQuery.value.toLowerCase();
filteredContacts.value = allContacts.where((contact) {
// 1. Category filter
final categoryMatch = selectedCategories.isEmpty ||
(contact.contactCategory != null &&
selectedCategories.contains(contact.contactCategory!.id));
// 2. Bucket filter
final bucketMatch = selectedBuckets.isEmpty ||
contact.bucketIds.any((id) => selectedBuckets.contains(id));
// 3. Search filter: match name, organization, email, or tags
final nameMatch = contact.name.toLowerCase().contains(query);
final orgMatch = contact.organization.toLowerCase().contains(query);
final emailMatch = contact.contactEmails
.any((e) => e.emailAddress.toLowerCase().contains(query));
final tagMatch =
contact.tags.any((tag) => tag.name.toLowerCase().contains(query));
final searchMatch =
query.isEmpty || nameMatch || orgMatch || emailMatch || tagMatch;
return categoryMatch && bucketMatch && searchMatch;
}).toList();
}
void toggleCategory(String categoryId) {
if (selectedCategories.contains(categoryId)) {
selectedCategories.remove(categoryId);
} else {
selectedCategories.add(categoryId);
}
}
void toggleBucket(String bucketId) {
if (selectedBuckets.contains(bucketId)) {
selectedBuckets.remove(bucketId);
} else {
selectedBuckets.add(bucketId);
}
}
void updateSearchQuery(String value) {
searchQuery.value = value;
applyFilters();
}
String getBucketNames(ContactModel contact, List<ContactBucket> allBuckets) {
return contact.bucketIds
.map((id) => allBuckets.firstWhereOrNull((b) => b.id == id)?.name ?? '')
.where((name) => name.isNotEmpty)
.join(', ');
}
bool hasActiveFilters() {
return selectedCategories.isNotEmpty ||
selectedBuckets.isNotEmpty ||
searchQuery.value.trim().isNotEmpty;
}
}