added multiple bucket assignment
This commit is contained in:
parent
5dc2db0a8b
commit
cd92d4d309
@ -10,7 +10,7 @@ class AddContactController extends GetxController {
|
||||
final RxList<String> tags = <String>[].obs;
|
||||
|
||||
final RxString selectedCategory = ''.obs;
|
||||
final RxString selectedBucket = ''.obs;
|
||||
final RxList<String> selectedBuckets = <String>[].obs;
|
||||
final RxString selectedProject = ''.obs;
|
||||
|
||||
final RxList<String> enteredTags = <String>[].obs;
|
||||
@ -50,7 +50,7 @@ class AddContactController extends GetxController {
|
||||
void resetForm() {
|
||||
selectedCategory.value = '';
|
||||
selectedProject.value = '';
|
||||
selectedBucket.value = '';
|
||||
selectedBuckets.clear();
|
||||
enteredTags.clear();
|
||||
filteredSuggestions.clear();
|
||||
filteredOrgSuggestions.clear();
|
||||
@ -100,7 +100,21 @@ class AddContactController extends GetxController {
|
||||
isSubmitting.value = true;
|
||||
|
||||
final categoryId = categoriesMap[selectedCategory.value];
|
||||
final bucketId = bucketsMap[selectedBucket.value];
|
||||
final bucketIds = selectedBuckets
|
||||
.map((name) => bucketsMap[name])
|
||||
.whereType<String>()
|
||||
.toList();
|
||||
|
||||
if (bucketIds.isEmpty) {
|
||||
showAppSnackbar(
|
||||
title: "Missing Buckets",
|
||||
message: "Please select at least one bucket.",
|
||||
type: SnackbarType.warning,
|
||||
);
|
||||
isSubmitting.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
final projectIds = selectedProjects
|
||||
.map((name) => projectsMap[name])
|
||||
.whereType<String>()
|
||||
@ -126,10 +140,10 @@ class AddContactController extends GetxController {
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedBucket.value.trim().isEmpty || bucketId == null) {
|
||||
if (selectedBuckets.isEmpty) {
|
||||
showAppSnackbar(
|
||||
title: "Missing Bucket",
|
||||
message: "Please select a bucket.",
|
||||
message: "Please select at least one bucket.",
|
||||
type: SnackbarType.warning,
|
||||
);
|
||||
isSubmitting.value = false;
|
||||
@ -151,7 +165,7 @@ class AddContactController extends GetxController {
|
||||
if (selectedCategory.value.isNotEmpty && categoryId != null)
|
||||
"contactCategoryId": categoryId,
|
||||
if (projectIds.isNotEmpty) "projectIds": projectIds,
|
||||
"bucketIds": [bucketId],
|
||||
"bucketIds": bucketIds,
|
||||
if (enteredTags.isNotEmpty) "tags": tagObjects,
|
||||
if (emails.isNotEmpty) "contactEmails": emails,
|
||||
if (phones.isNotEmpty) "contactPhones": phones,
|
||||
|
@ -74,12 +74,20 @@ class _AddContactBottomSheetState extends State<AddContactBottomSheet> {
|
||||
|
||||
ever(controller.isInitialized, (bool ready) {
|
||||
if (ready) {
|
||||
// Buckets - map all
|
||||
if (c.bucketIds.isNotEmpty) {
|
||||
final names = c.bucketIds
|
||||
.map((id) {
|
||||
return controller.bucketsMap.entries
|
||||
.firstWhereOrNull((e) => e.value == id)
|
||||
?.key;
|
||||
})
|
||||
.whereType<String>()
|
||||
.toList();
|
||||
controller.selectedBuckets.assignAll(names);
|
||||
}
|
||||
// Projects and Category mapping - as before
|
||||
final projectIds = c.projectIds;
|
||||
final bucketId = c.bucketIds.firstOrNull;
|
||||
final category = c.contactCategory?.name;
|
||||
|
||||
if (category != null) controller.selectedCategory.value = category;
|
||||
|
||||
if (projectIds != null) {
|
||||
controller.selectedProjects.assignAll(
|
||||
projectIds
|
||||
@ -90,16 +98,12 @@ class _AddContactBottomSheetState extends State<AddContactBottomSheet> {
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
if (bucketId != null) {
|
||||
final name = controller.bucketsMap.entries
|
||||
.firstWhereOrNull((e) => e.value == bucketId)
|
||||
?.key;
|
||||
if (name != null) controller.selectedBucket.value = name;
|
||||
}
|
||||
final category = c.contactCategory?.name;
|
||||
if (category != null) controller.selectedCategory.value = category;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
showAdvanced.value = false; // Optional
|
||||
emailCtrls.add(TextEditingController());
|
||||
emailLabels.add('Office'.obs);
|
||||
phoneCtrls.add(TextEditingController());
|
||||
@ -363,10 +367,129 @@ class _AddContactBottomSheetState extends State<AddContactBottomSheet> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _bucketMultiSelectField() {
|
||||
return _multiSelectField(
|
||||
items: controller.buckets
|
||||
.map((name) => FilterItem(id: name, name: name))
|
||||
.toList(),
|
||||
fallback: "Choose Buckets",
|
||||
selectedValues: controller.selectedBuckets,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _multiSelectField({
|
||||
required List<FilterItem> items,
|
||||
required String fallback,
|
||||
required RxList<String> selectedValues,
|
||||
}) {
|
||||
if (items.isEmpty) return const SizedBox.shrink();
|
||||
|
||||
return Obx(() {
|
||||
final selectedNames = items
|
||||
.where((f) => selectedValues.contains(f.id))
|
||||
.map((f) => f.name)
|
||||
.join(", ");
|
||||
final displayText = selectedNames.isNotEmpty ? selectedNames : fallback;
|
||||
|
||||
return Builder(
|
||||
builder: (context) {
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
final RenderBox button = context.findRenderObject() as RenderBox;
|
||||
final RenderBox overlay =
|
||||
Overlay.of(context).context.findRenderObject() as RenderBox;
|
||||
final position = button.localToGlobal(Offset.zero);
|
||||
|
||||
await showMenu(
|
||||
context: context,
|
||||
position: RelativeRect.fromLTRB(
|
||||
position.dx,
|
||||
position.dy + button.size.height,
|
||||
overlay.size.width - position.dx - button.size.width,
|
||||
0,
|
||||
),
|
||||
items: [
|
||||
PopupMenuItem(
|
||||
enabled: false,
|
||||
child: StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return SizedBox(
|
||||
width: 250,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: items.map((f) {
|
||||
final isChecked = selectedValues.contains(f.id);
|
||||
return CheckboxListTile(
|
||||
dense: true,
|
||||
title: Text(f.name),
|
||||
value: isChecked,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
controlAffinity:
|
||||
ListTileControlAffinity.leading,
|
||||
side: const BorderSide(
|
||||
color: Colors.black, width: 1.5),
|
||||
fillColor:
|
||||
MaterialStateProperty.resolveWith<Color>(
|
||||
(states) {
|
||||
if (states
|
||||
.contains(MaterialState.selected)) {
|
||||
return Colors.indigo; // selected color
|
||||
}
|
||||
return Colors
|
||||
.white; // unselected background
|
||||
}),
|
||||
checkColor: Colors.white, // tick color
|
||||
onChanged: (val) {
|
||||
if (val == true) {
|
||||
selectedValues.add(f.id);
|
||||
} else {
|
||||
selectedValues.remove(f.id);
|
||||
}
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
padding: MySpacing.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade100,
|
||||
border: Border.all(color: Colors.grey.shade300),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: MyText(
|
||||
displayText,
|
||||
style: const TextStyle(color: Colors.black87),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const Icon(Icons.arrow_drop_down, color: Colors.grey),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void _handleSubmit() {
|
||||
bool valid = formKey.currentState?.validate() ?? false;
|
||||
|
||||
if (controller.selectedBucket.value.isEmpty) {
|
||||
if (controller.selectedBuckets.isEmpty) {
|
||||
bucketError.value = "Bucket is required";
|
||||
valid = false;
|
||||
} else {
|
||||
@ -430,29 +553,14 @@ class _AddContactBottomSheetState extends State<AddContactBottomSheet> {
|
||||
MySpacing.height(16),
|
||||
_textField("Organization", orgCtrl, required: true),
|
||||
MySpacing.height(16),
|
||||
_labelWithStar("Bucket", required: true),
|
||||
_labelWithStar("Buckets", required: true),
|
||||
MySpacing.height(8),
|
||||
Stack(
|
||||
children: [
|
||||
_popupSelector(controller.selectedBucket, controller.buckets,
|
||||
"Choose Bucket"),
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 56,
|
||||
child: Obx(() => bucketError.value.isEmpty
|
||||
? const SizedBox.shrink()
|
||||
: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Text(bucketError.value,
|
||||
style: const TextStyle(
|
||||
color: Colors.red, fontSize: 12)),
|
||||
)),
|
||||
),
|
||||
_bucketMultiSelectField(),
|
||||
],
|
||||
),
|
||||
MySpacing.height(24),
|
||||
MySpacing.height(12),
|
||||
Obx(() => GestureDetector(
|
||||
onTap: () => showAdvanced.toggle(),
|
||||
child: Row(
|
||||
@ -562,3 +670,9 @@ class _AddContactBottomSheetState extends State<AddContactBottomSheet> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class FilterItem {
|
||||
final String id;
|
||||
final String name;
|
||||
FilterItem({required this.id, required this.name});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user