- Added TaskListModel for managing daily tasks with JSON parsing. - Introduced WorkStatusResponseModel and WorkStatus for handling work status data. - Created MenuResponse and MenuItem models for dynamic menu management. - Updated routes to reflect correct naming conventions for task planning screens. - Enhanced DashboardScreen to include dynamic menu functionality and improved task statistics display. - Developed DailyProgressReportScreen for displaying daily progress reports with filtering options. - Implemented DailyTaskPlanningScreen for planning daily tasks with detailed views and actions. - Refactored left navigation bar to align with updated task planning routes.
214 lines
7.2 KiB
Dart
214 lines
7.2 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:marco/controller/task_planning/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,
|
|
required String activity,
|
|
required String completedWork,
|
|
required String unit,
|
|
required Function(String) onCategoryChanged,
|
|
required String parentTaskId,
|
|
required int plannedTask,
|
|
required String activityId,
|
|
required String workAreaId,
|
|
required VoidCallback onSubmit,
|
|
}) {
|
|
final controller = Get.put(AddTaskController());
|
|
final TextEditingController plannedTaskController =
|
|
TextEditingController(text: plannedTask.toString());
|
|
final TextEditingController descriptionController = TextEditingController();
|
|
|
|
Get.bottomSheet(
|
|
StatefulBuilder(
|
|
builder: (context, setState) {
|
|
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;
|
|
|
|
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,
|
|
);
|
|
});
|
|
}
|
|
},
|
|
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: [
|
|
Text(
|
|
selectedName,
|
|
style: const TextStyle(
|
|
fontSize: 14, color: Colors.black87),
|
|
),
|
|
const Icon(Icons.arrow_drop_down),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
),
|
|
isScrollControlled: true,
|
|
);
|
|
}
|
|
|
|
Widget _sectionTitle(IconData icon, String title) {
|
|
return Row(
|
|
children: [
|
|
Icon(icon, color: Colors.grey[700], size: 18),
|
|
const SizedBox(width: 8),
|
|
MyText.bodyMedium(title, fontWeight: 600),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _customTextField({
|
|
required TextEditingController controller,
|
|
required String hint,
|
|
int maxLines = 1,
|
|
TextInputType keyboardType = TextInputType.text,
|
|
}) {
|
|
return TextField(
|
|
controller: controller,
|
|
maxLines: maxLines,
|
|
keyboardType: keyboardType,
|
|
decoration: InputDecoration(
|
|
hintText: hint,
|
|
filled: true,
|
|
fillColor: Colors.grey.shade100,
|
|
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _infoCardSection(List<Widget> children) {
|
|
return Container(
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: Colors.grey.shade100,
|
|
borderRadius: BorderRadius.circular(12),
|
|
border: Border.all(color: Colors.grey.shade300),
|
|
),
|
|
child: Column(children: children),
|
|
);
|
|
}
|
|
|
|
Widget _infoRowWithIcon(IconData icon, String title, String value) {
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 6),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Icon(icon, color: Colors.grey[700], size: 18),
|
|
const SizedBox(width: 8),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
MyText.bodyMedium(title, fontWeight: 600),
|
|
const SizedBox(height: 2),
|
|
MyText.bodySmall(value, color: Colors.grey[800]),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|