Vaibhav_Feature-#768 #59
@ -24,6 +24,7 @@ class AddContactController extends GetxController {
|
||||
final RxMap<String, String> tagsMap = <String, String>{}.obs;
|
||||
final RxBool isInitialized = false.obs;
|
||||
final RxList<String> selectedProjects = <String>[].obs;
|
||||
final RxBool isSubmitting = false.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
@ -94,6 +95,9 @@ class AddContactController extends GetxController {
|
||||
required String address,
|
||||
required String description,
|
||||
}) async {
|
||||
if (isSubmitting.value) return;
|
||||
isSubmitting.value = true;
|
||||
|
||||
final categoryId = categoriesMap[selectedCategory.value];
|
||||
final bucketId = bucketsMap[selectedBucket.value];
|
||||
final projectIds = selectedProjects
|
||||
@ -101,13 +105,13 @@ class AddContactController extends GetxController {
|
||||
.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,
|
||||
);
|
||||
isSubmitting.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -117,6 +121,7 @@ class AddContactController extends GetxController {
|
||||
message: "Please enter the organization name.",
|
||||
type: SnackbarType.warning,
|
||||
);
|
||||
isSubmitting.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -126,10 +131,10 @@ class AddContactController extends GetxController {
|
||||
message: "Please select a bucket.",
|
||||
type: SnackbarType.warning,
|
||||
);
|
||||
isSubmitting.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// === Build body (include optional fields if available) ===
|
||||
try {
|
||||
final tagObjects = enteredTags.map((tagName) {
|
||||
final tagId = tagsMap[tagName];
|
||||
@ -182,6 +187,8 @@ class AddContactController extends GetxController {
|
||||
message: "Something went wrong",
|
||||
type: SnackbarType.error,
|
||||
);
|
||||
} finally {
|
||||
isSubmitting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,7 +137,6 @@ class _CommentTaskBottomSheetState extends State<CommentTaskBottomSheet>
|
||||
onCancel: () => Navigator.of(context).pop(),
|
||||
onSubmit: _submitComment,
|
||||
isSubmitting: controller.isLoading.value,
|
||||
submitText: 'Comment',
|
||||
bottomContent: _buildCommentsSection(),
|
||||
child: Form(
|
||||
// moved to last
|
||||
|
@ -4,8 +4,7 @@ import 'package:marco/controller/task_planing/add_task_controller.dart';
|
||||
import 'package:marco/helpers/widgets/my_text.dart';
|
||||
|
||||
import 'package:marco/helpers/widgets/my_snackbar.dart';
|
||||
|
||||
|
||||
import 'package:marco/helpers/utils/base_bottom_sheet.dart';
|
||||
|
||||
void showCreateTaskBottomSheet({
|
||||
required String workArea,
|
||||
@ -27,197 +26,120 @@ void showCreateTaskBottomSheet({
|
||||
Get.bottomSheet(
|
||||
StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final isLarge = constraints.maxWidth > 600;
|
||||
final horizontalPadding =
|
||||
isLarge ? constraints.maxWidth * 0.2 : 16.0;
|
||||
return BaseBottomSheet(
|
||||
title: "Create Task",
|
||||
onCancel: () => Get.back(),
|
||||
onSubmit: () async {
|
||||
final plannedValue =
|
||||
int.tryParse(plannedTaskController.text.trim()) ?? 0;
|
||||
final comment = descriptionController.text.trim();
|
||||
final selectedCategoryId = controller.selectedCategoryId.value;
|
||||
|
||||
return // Inside showManageTaskBottomSheet...
|
||||
if (selectedCategoryId == null) {
|
||||
showAppSnackbar(
|
||||
title: "error",
|
||||
message: "Please select a work category!",
|
||||
type: SnackbarType.error,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
SafeArea(
|
||||
child: Material(
|
||||
color: Colors.white,
|
||||
borderRadius:
|
||||
const BorderRadius.vertical(top: Radius.circular(20)),
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(maxHeight: 760),
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
horizontalPadding, 12, horizontalPadding, 24),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
final success = await controller.createTask(
|
||||
parentTaskId: parentTaskId,
|
||||
plannedTask: plannedValue,
|
||||
comment: comment,
|
||||
workAreaId: workAreaId,
|
||||
activityId: activityId,
|
||||
categoryId: selectedCategoryId,
|
||||
);
|
||||
|
||||
if (success) {
|
||||
Get.back();
|
||||
Future.delayed(const Duration(milliseconds: 300), () {
|
||||
onSubmit();
|
||||
showAppSnackbar(
|
||||
title: "Success",
|
||||
message: "Task created successfully!",
|
||||
type: SnackbarType.success,
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
submitText: "Submit",
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_infoCardSection([
|
||||
_infoRowWithIcon(
|
||||
Icons.workspaces, "Selected Work Area", workArea),
|
||||
_infoRowWithIcon(Icons.list_alt, "Selected Activity", activity),
|
||||
_infoRowWithIcon(Icons.check_circle_outline, "Completed Work",
|
||||
completedWork),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
_sectionTitle(Icons.edit_calendar, "Planned Work"),
|
||||
const SizedBox(height: 6),
|
||||
_customTextField(
|
||||
controller: plannedTaskController,
|
||||
hint: "Enter planned work",
|
||||
keyboardType: TextInputType.number,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_sectionTitle(Icons.description_outlined, "Comment"),
|
||||
const SizedBox(height: 6),
|
||||
_customTextField(
|
||||
controller: descriptionController,
|
||||
hint: "Enter task description",
|
||||
maxLines: 3,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_sectionTitle(Icons.category_outlined, "Selected Work Category"),
|
||||
const SizedBox(height: 6),
|
||||
Obx(() {
|
||||
final categoryMap = controller.categoryIdNameMap;
|
||||
final String selectedName =
|
||||
controller.selectedCategoryId.value != null
|
||||
? (categoryMap[controller.selectedCategoryId.value!] ??
|
||||
'Select Category')
|
||||
: 'Select Category';
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade100,
|
||||
border: Border.all(color: Colors.grey.shade300),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: PopupMenuButton<String>(
|
||||
padding: EdgeInsets.zero,
|
||||
onSelected: (val) {
|
||||
controller.selectCategory(val);
|
||||
onCategoryChanged(val);
|
||||
},
|
||||
itemBuilder: (context) => categoryMap.entries
|
||||
.map((entry) => PopupMenuItem<String>(
|
||||
value: entry.key,
|
||||
child: Text(entry.value),
|
||||
))
|
||||
.toList(),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Center(
|
||||
child: Container(
|
||||
width: 40,
|
||||
height: 4,
|
||||
margin: const EdgeInsets.only(bottom: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade400,
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: MyText.titleLarge(
|
||||
"Create Task",
|
||||
fontWeight: 700,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_infoCardSection([
|
||||
_infoRowWithIcon(
|
||||
Icons.workspaces, "Selected Work Area", workArea),
|
||||
_infoRowWithIcon(
|
||||
Icons.list_alt, "Selected Activity", activity),
|
||||
_infoRowWithIcon(Icons.check_circle_outline,
|
||||
"Completed Work", completedWork),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
_sectionTitle(Icons.edit_calendar, "Planned Work"),
|
||||
const SizedBox(height: 6),
|
||||
_customTextField(
|
||||
controller: plannedTaskController,
|
||||
hint: "Enter planned work",
|
||||
keyboardType: TextInputType.number,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_sectionTitle(Icons.description_outlined, "Comment"),
|
||||
const SizedBox(height: 6),
|
||||
_customTextField(
|
||||
controller: descriptionController,
|
||||
hint: "Enter task description",
|
||||
maxLines: 3,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_sectionTitle(
|
||||
Icons.category_outlined, "Selected Work Category"),
|
||||
const SizedBox(height: 6),
|
||||
Obx(() {
|
||||
final categoryMap = controller.categoryIdNameMap;
|
||||
final String selectedName =
|
||||
controller.selectedCategoryId.value != null
|
||||
? (categoryMap[controller
|
||||
.selectedCategoryId.value!] ??
|
||||
'Select Category')
|
||||
: 'Select Category';
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12, vertical: 14),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade100,
|
||||
border: Border.all(color: Colors.grey.shade300),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: PopupMenuButton<String>(
|
||||
padding: EdgeInsets.zero,
|
||||
onSelected: (val) {
|
||||
controller.selectCategory(val);
|
||||
onCategoryChanged(val);
|
||||
},
|
||||
itemBuilder: (context) => categoryMap.entries
|
||||
.map(
|
||||
(entry) => PopupMenuItem<String>(
|
||||
value: entry.key,
|
||||
child: Text(entry.value),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
selectedName,
|
||||
style: const TextStyle(
|
||||
fontSize: 14, color: Colors.black87),
|
||||
),
|
||||
const Icon(Icons.arrow_drop_down),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
const SizedBox(height: 24),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: () => Get.back(),
|
||||
icon: const Icon(Icons.close, size: 18),
|
||||
label: MyText.bodyMedium("Cancel",
|
||||
fontWeight: 600),
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: const BorderSide(color: Colors.grey),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10)),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () async {
|
||||
final plannedValue = int.tryParse(
|
||||
plannedTaskController.text.trim()) ??
|
||||
0;
|
||||
final comment =
|
||||
descriptionController.text.trim();
|
||||
final selectedCategoryId =
|
||||
controller.selectedCategoryId.value;
|
||||
if (selectedCategoryId == null) {
|
||||
showAppSnackbar(
|
||||
title: "error",
|
||||
message: "Please select a work category!",
|
||||
type: SnackbarType.error,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final success = await controller.createTask(
|
||||
parentTaskId: parentTaskId,
|
||||
plannedTask: plannedValue,
|
||||
comment: comment,
|
||||
workAreaId: workAreaId,
|
||||
activityId: activityId,
|
||||
categoryId: selectedCategoryId,
|
||||
);
|
||||
|
||||
if (success) {
|
||||
Get.back();
|
||||
Future.delayed(
|
||||
const Duration(milliseconds: 300), () {
|
||||
onSubmit();
|
||||
showAppSnackbar(
|
||||
title: "Success",
|
||||
message: "Task created successfully!",
|
||||
type: SnackbarType.success,
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.check, size: 18),
|
||||
label: MyText.bodyMedium("Submit",
|
||||
color: Colors.white, fontWeight: 600),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blueAccent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10)),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
Text(
|
||||
selectedName,
|
||||
style: const TextStyle(
|
||||
fontSize: 14, color: Colors.black87),
|
||||
),
|
||||
const Icon(Icons.arrow_drop_down),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user