feat: Implement pagination and service filtering in daily task fetching logic
This commit is contained in:
parent
fc081c779e
commit
7d211e24f8
@ -24,8 +24,12 @@ class DailyTaskController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RxBool isLoading = true.obs;
|
RxBool isLoading = true.obs;
|
||||||
|
RxBool isLoadingMore = false.obs;
|
||||||
Map<String, List<TaskModel>> groupedDailyTasks = {};
|
Map<String, List<TaskModel>> groupedDailyTasks = {};
|
||||||
|
// Pagination
|
||||||
|
int currentPage = 1;
|
||||||
|
int pageSize = 20;
|
||||||
|
bool hasMore = true;
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
@ -47,48 +51,49 @@ class DailyTaskController extends GetxController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> fetchTaskData(String? projectId) async {
|
Future<void> fetchTaskData(
|
||||||
if (projectId == null) {
|
String projectId, {
|
||||||
logSafe("fetchTaskData: Skipped, projectId is null",
|
List<String>? serviceIds,
|
||||||
level: LogLevel.warning);
|
int pageNumber = 1,
|
||||||
return;
|
int pageSize = 20,
|
||||||
|
bool isLoadMore = false,
|
||||||
|
}) async {
|
||||||
|
if (!isLoadMore) {
|
||||||
|
isLoading.value = true;
|
||||||
|
currentPage = 1;
|
||||||
|
hasMore = true;
|
||||||
|
groupedDailyTasks.clear();
|
||||||
|
dailyTasks.clear();
|
||||||
|
} else {
|
||||||
|
isLoadingMore.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
isLoading.value = true;
|
|
||||||
|
|
||||||
final response = await ApiService.getDailyTasks(
|
final response = await ApiService.getDailyTasks(
|
||||||
projectId,
|
projectId,
|
||||||
dateFrom: startDateTask,
|
dateFrom: startDateTask,
|
||||||
dateTo: endDateTask,
|
dateTo: endDateTask,
|
||||||
|
serviceIds: serviceIds,
|
||||||
|
pageNumber: pageNumber,
|
||||||
|
pageSize: pageSize,
|
||||||
);
|
);
|
||||||
|
|
||||||
isLoading.value = false;
|
if (response != null && response.isNotEmpty) {
|
||||||
|
|
||||||
if (response != null) {
|
|
||||||
groupedDailyTasks.clear();
|
|
||||||
|
|
||||||
for (var taskJson in response) {
|
for (var taskJson in response) {
|
||||||
final task = TaskModel.fromJson(taskJson);
|
final task = TaskModel.fromJson(taskJson);
|
||||||
final assignmentDateKey =
|
final assignmentDateKey =
|
||||||
task.assignmentDate.toIso8601String().split('T')[0];
|
task.assignmentDate.toIso8601String().split('T')[0];
|
||||||
|
|
||||||
groupedDailyTasks.putIfAbsent(assignmentDateKey, () => []).add(task);
|
groupedDailyTasks.putIfAbsent(assignmentDateKey, () => []).add(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
dailyTasks = groupedDailyTasks.values.expand((list) => list).toList();
|
dailyTasks = groupedDailyTasks.values.expand((list) => list).toList();
|
||||||
|
currentPage = pageNumber;
|
||||||
logSafe(
|
|
||||||
"Daily tasks fetched and grouped: ${dailyTasks.length} for project $projectId",
|
|
||||||
level: LogLevel.info,
|
|
||||||
);
|
|
||||||
|
|
||||||
update();
|
|
||||||
} else {
|
} else {
|
||||||
logSafe(
|
hasMore = false;
|
||||||
"Failed to fetch daily tasks for project $projectId",
|
|
||||||
level: LogLevel.error,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isLoading.value = false;
|
||||||
|
isLoadingMore.value = false;
|
||||||
|
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> selectDateRangeForTaskData(
|
Future<void> selectDateRangeForTaskData(
|
||||||
@ -119,17 +124,23 @@ class DailyTaskController extends GetxController {
|
|||||||
level: LogLevel.info,
|
level: LogLevel.info,
|
||||||
);
|
);
|
||||||
|
|
||||||
await controller.fetchTaskData(controller.selectedProjectId);
|
// ✅ Add null check before calling fetchTaskData
|
||||||
|
final projectId = controller.selectedProjectId;
|
||||||
|
if (projectId != null && projectId.isNotEmpty) {
|
||||||
|
await controller.fetchTaskData(projectId);
|
||||||
|
} else {
|
||||||
|
logSafe("Project ID is null or empty, skipping fetchTaskData",
|
||||||
|
level: LogLevel.warning);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void refreshTasksFromNotification({
|
void refreshTasksFromNotification({
|
||||||
required String projectId,
|
required String projectId,
|
||||||
required String taskAllocationId,
|
required String taskAllocationId,
|
||||||
}) async {
|
}) async {
|
||||||
// re-fetch tasks
|
// re-fetch tasks
|
||||||
await fetchTaskData(projectId);
|
await fetchTaskData(projectId);
|
||||||
|
|
||||||
update(); // rebuilds UI
|
|
||||||
}
|
|
||||||
|
|
||||||
|
update(); // rebuilds UI
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2022,21 +2022,35 @@ class ApiService {
|
|||||||
|
|
||||||
// === Daily Task APIs ===
|
// === Daily Task APIs ===
|
||||||
|
|
||||||
static Future<List<dynamic>?> getDailyTasks(
|
static Future<List<dynamic>?> getDailyTasks(
|
||||||
String projectId, {
|
String projectId, {
|
||||||
DateTime? dateFrom,
|
DateTime? dateFrom,
|
||||||
DateTime? dateTo,
|
DateTime? dateTo,
|
||||||
}) async {
|
List<String>? serviceIds,
|
||||||
final query = {
|
int pageNumber = 1,
|
||||||
"projectId": projectId,
|
int pageSize = 20,
|
||||||
if (dateFrom != null)
|
}) async {
|
||||||
"dateFrom": DateFormat('yyyy-MM-dd').format(dateFrom),
|
final filterBody = {
|
||||||
if (dateTo != null) "dateTo": DateFormat('yyyy-MM-dd').format(dateTo),
|
"serviceIds": serviceIds ?? [],
|
||||||
};
|
};
|
||||||
return _getRequest(ApiEndpoints.getDailyTask, queryParams: query).then(
|
|
||||||
(res) =>
|
final query = {
|
||||||
res != null ? _parseResponse(res, label: 'Daily Tasks') : null);
|
"projectId": projectId,
|
||||||
}
|
"pageNumber": pageNumber.toString(),
|
||||||
|
"pageSize": pageSize.toString(),
|
||||||
|
if (dateFrom != null)
|
||||||
|
"dateFrom": DateFormat('yyyy-MM-dd').format(dateFrom),
|
||||||
|
if (dateTo != null) "dateTo": DateFormat('yyyy-MM-dd').format(dateTo),
|
||||||
|
"filter": jsonEncode(filterBody),
|
||||||
|
};
|
||||||
|
|
||||||
|
final uri = Uri.parse(ApiEndpoints.getDailyTask).replace(queryParameters: query);
|
||||||
|
|
||||||
|
final response = await _getRequest(uri.toString());
|
||||||
|
|
||||||
|
return response != null ? _parseResponse(response, label: 'Daily Tasks') : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static Future<bool> reportTask({
|
static Future<bool> reportTask({
|
||||||
required String id,
|
required String id,
|
||||||
|
@ -25,17 +25,15 @@ class OrganizationSelector extends StatelessWidget {
|
|||||||
required List<String> items,
|
required List<String> items,
|
||||||
}) {
|
}) {
|
||||||
return PopupMenuButton<String>(
|
return PopupMenuButton<String>(
|
||||||
|
color: Colors.white,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
onSelected: (name) async {
|
onSelected: (name) async {
|
||||||
// Determine the selected organization
|
|
||||||
Organization? org = name == "All Organizations"
|
Organization? org = name == "All Organizations"
|
||||||
? null
|
? null
|
||||||
: controller.organizations.firstWhere((e) => e.name == name);
|
: controller.organizations.firstWhere((e) => e.name == name);
|
||||||
|
|
||||||
// Update controller state
|
|
||||||
controller.selectOrganization(org);
|
controller.selectOrganization(org);
|
||||||
|
|
||||||
// Trigger callback for post-selection logic
|
|
||||||
if (onSelectionChanged != null) {
|
if (onSelectionChanged != null) {
|
||||||
await onSelectionChanged!(org);
|
await onSelectionChanged!(org);
|
||||||
}
|
}
|
||||||
@ -45,9 +43,10 @@ class OrganizationSelector extends StatelessWidget {
|
|||||||
.toList(),
|
.toList(),
|
||||||
child: Container(
|
child: Container(
|
||||||
height: height,
|
height: height,
|
||||||
padding: EdgeInsets.symmetric(horizontal: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.grey.shade100,
|
color:
|
||||||
|
Colors.white,
|
||||||
border: Border.all(color: Colors.grey.shade300),
|
border: Border.all(color: Colors.grey.shade300),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
|
@ -44,28 +44,50 @@ class _DailyProgressReportScreenState extends State<DailyProgressReportScreen>
|
|||||||
Get.find<PermissionController>();
|
Get.find<PermissionController>();
|
||||||
final ProjectController projectController = Get.find<ProjectController>();
|
final ProjectController projectController = Get.find<ProjectController>();
|
||||||
final ServiceController serviceController = Get.put(ServiceController());
|
final ServiceController serviceController = Get.put(ServiceController());
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_scrollController.addListener(() {
|
||||||
|
if (_scrollController.position.pixels >=
|
||||||
|
_scrollController.position.maxScrollExtent - 100 &&
|
||||||
|
dailyTaskController.hasMore &&
|
||||||
|
!dailyTaskController.isLoadingMore.value) {
|
||||||
|
final projectId = dailyTaskController.selectedProjectId;
|
||||||
|
if (projectId != null && projectId.isNotEmpty) {
|
||||||
|
dailyTaskController.fetchTaskData(
|
||||||
|
projectId,
|
||||||
|
pageNumber: dailyTaskController.currentPage + 1,
|
||||||
|
pageSize: dailyTaskController.pageSize,
|
||||||
|
isLoadMore: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
final initialProjectId = projectController.selectedProjectId.value;
|
final initialProjectId = projectController.selectedProjectId.value;
|
||||||
if (initialProjectId.isNotEmpty) {
|
if (initialProjectId.isNotEmpty) {
|
||||||
dailyTaskController.selectedProjectId = initialProjectId;
|
dailyTaskController.selectedProjectId = initialProjectId;
|
||||||
dailyTaskController.fetchTaskData(initialProjectId);
|
dailyTaskController.fetchTaskData(initialProjectId);
|
||||||
|
serviceController.fetchServices(initialProjectId);
|
||||||
}
|
}
|
||||||
|
|
||||||
ever<String>(
|
// Update when project changes
|
||||||
projectController.selectedProjectId,
|
ever<String>(projectController.selectedProjectId, (newProjectId) async {
|
||||||
(newProjectId) async {
|
if (newProjectId.isNotEmpty &&
|
||||||
if (newProjectId.isNotEmpty &&
|
newProjectId != dailyTaskController.selectedProjectId) {
|
||||||
newProjectId != dailyTaskController.selectedProjectId) {
|
dailyTaskController.selectedProjectId = newProjectId;
|
||||||
dailyTaskController.selectedProjectId = newProjectId;
|
await dailyTaskController.fetchTaskData(newProjectId);
|
||||||
await dailyTaskController.fetchTaskData(newProjectId);
|
await serviceController.fetchServices(newProjectId);
|
||||||
dailyTaskController.update(['daily_progress_report_controller']);
|
dailyTaskController.update(['daily_progress_report_controller']);
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
);
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -158,7 +180,10 @@ class _DailyProgressReportScreenState extends State<DailyProgressReportScreen>
|
|||||||
if (projectId?.isNotEmpty ?? false) {
|
if (projectId?.isNotEmpty ?? false) {
|
||||||
await dailyTaskController.fetchTaskData(
|
await dailyTaskController.fetchTaskData(
|
||||||
projectId!,
|
projectId!,
|
||||||
// serviceId: service?.id,
|
serviceIds:
|
||||||
|
service != null ? [service.id] : null,
|
||||||
|
pageNumber: 1,
|
||||||
|
pageSize: 20,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -321,10 +346,12 @@ class _DailyProgressReportScreenState extends State<DailyProgressReportScreen>
|
|||||||
final isLoading = dailyTaskController.isLoading.value;
|
final isLoading = dailyTaskController.isLoading.value;
|
||||||
final groupedTasks = dailyTaskController.groupedDailyTasks;
|
final groupedTasks = dailyTaskController.groupedDailyTasks;
|
||||||
|
|
||||||
if (isLoading) {
|
// Initial loading skeleton
|
||||||
|
if (isLoading && dailyTaskController.currentPage == 1) {
|
||||||
return SkeletonLoaders.dailyProgressReportSkeletonLoader();
|
return SkeletonLoaders.dailyProgressReportSkeletonLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No tasks
|
||||||
if (groupedTasks.isEmpty) {
|
if (groupedTasks.isEmpty) {
|
||||||
return Center(
|
return Center(
|
||||||
child: MyText.bodySmall(
|
child: MyText.bodySmall(
|
||||||
@ -337,23 +364,33 @@ class _DailyProgressReportScreenState extends State<DailyProgressReportScreen>
|
|||||||
final sortedDates = groupedTasks.keys.toList()
|
final sortedDates = groupedTasks.keys.toList()
|
||||||
..sort((a, b) => b.compareTo(a));
|
..sort((a, b) => b.compareTo(a));
|
||||||
|
|
||||||
|
// If only one date, make it expanded by default
|
||||||
|
if (sortedDates.length == 1 &&
|
||||||
|
!dailyTaskController.expandedDates.contains(sortedDates[0])) {
|
||||||
|
dailyTaskController.expandedDates.add(sortedDates[0]);
|
||||||
|
}
|
||||||
|
|
||||||
return MyCard.bordered(
|
return MyCard.bordered(
|
||||||
borderRadiusAll: 10,
|
borderRadiusAll: 10,
|
||||||
border: Border.all(color: Colors.grey.withOpacity(0.2)),
|
border: Border.all(color: Colors.grey.withOpacity(0.2)),
|
||||||
shadow: MyShadow(elevation: 1, position: MyShadowPosition.bottom),
|
shadow: MyShadow(elevation: 1, position: MyShadowPosition.bottom),
|
||||||
paddingAll: 8,
|
paddingAll: 8,
|
||||||
child: ListView.separated(
|
child: ListView.builder(
|
||||||
|
controller: _scrollController,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
itemCount: sortedDates.length,
|
itemCount: sortedDates.length + 1, // +1 for loading indicator
|
||||||
separatorBuilder: (_, __) => Column(
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
Divider(color: Colors.grey.withOpacity(0.3), thickness: 1),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
itemBuilder: (context, dateIndex) {
|
itemBuilder: (context, dateIndex) {
|
||||||
|
// Bottom loading indicator
|
||||||
|
if (dateIndex == sortedDates.length) {
|
||||||
|
return Obx(() => dailyTaskController.isLoadingMore.value
|
||||||
|
? const Padding(
|
||||||
|
padding: EdgeInsets.all(16.0),
|
||||||
|
child: Center(child: CircularProgressIndicator()),
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink());
|
||||||
|
}
|
||||||
|
|
||||||
final dateKey = sortedDates[dateIndex];
|
final dateKey = sortedDates[dateIndex];
|
||||||
final tasksForDate = groupedTasks[dateKey]!;
|
final tasksForDate = groupedTasks[dateKey]!;
|
||||||
final date = DateTime.tryParse(dateKey);
|
final date = DateTime.tryParse(dateKey);
|
||||||
@ -389,7 +426,6 @@ class _DailyProgressReportScreenState extends State<DailyProgressReportScreen>
|
|||||||
return Column(
|
return Column(
|
||||||
children: tasksForDate.asMap().entries.map((entry) {
|
children: tasksForDate.asMap().entries.map((entry) {
|
||||||
final task = entry.value;
|
final task = entry.value;
|
||||||
final index = entry.key;
|
|
||||||
|
|
||||||
final activityName =
|
final activityName =
|
||||||
task.workItem?.activityMaster?.activityName ?? 'N/A';
|
task.workItem?.activityMaster?.activityName ?? 'N/A';
|
||||||
@ -407,134 +443,121 @@ class _DailyProgressReportScreenState extends State<DailyProgressReportScreen>
|
|||||||
? (completed / planned).clamp(0.0, 1.0)
|
? (completed / planned).clamp(0.0, 1.0)
|
||||||
: 0.0;
|
: 0.0;
|
||||||
final parentTaskID = task.id;
|
final parentTaskID = task.id;
|
||||||
return Column(
|
|
||||||
children: [
|
return Padding(
|
||||||
Padding(
|
padding: const EdgeInsets.only(bottom: 8),
|
||||||
padding: const EdgeInsets.only(bottom: 8),
|
child: MyContainer(
|
||||||
child: MyContainer(
|
paddingAll: 12,
|
||||||
paddingAll: 12,
|
child: Column(
|
||||||
child: Column(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
children: [
|
||||||
|
MyText.bodyMedium(activityName, fontWeight: 600),
|
||||||
|
const SizedBox(height: 2),
|
||||||
|
MyText.bodySmall(location, color: Colors.grey),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () => _showTeamMembersBottomSheet(
|
||||||
|
task.teamMembers),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.group,
|
||||||
|
size: 18, color: Colors.blueAccent),
|
||||||
|
const SizedBox(width: 6),
|
||||||
|
MyText.bodyMedium('Team',
|
||||||
|
color: Colors.blueAccent,
|
||||||
|
fontWeight: 600),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
MyText.bodySmall(
|
||||||
|
"Completed: $completed / $planned",
|
||||||
|
fontWeight: 600,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
Stack(
|
||||||
children: [
|
children: [
|
||||||
MyText.bodyMedium(activityName,
|
Container(
|
||||||
fontWeight: 600),
|
height: 5,
|
||||||
const SizedBox(height: 2),
|
decoration: BoxDecoration(
|
||||||
MyText.bodySmall(location,
|
color: Colors.grey[300],
|
||||||
color: Colors.grey),
|
borderRadius: BorderRadius.circular(6),
|
||||||
const SizedBox(height: 8),
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () => _showTeamMembersBottomSheet(
|
|
||||||
task.teamMembers),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.group,
|
|
||||||
size: 18, color: Colors.blueAccent),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
MyText.bodyMedium('Team',
|
|
||||||
color: Colors.blueAccent,
|
|
||||||
fontWeight: 600),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
FractionallySizedBox(
|
||||||
MyText.bodySmall(
|
widthFactor: progress,
|
||||||
"Completed: $completed / $planned",
|
child: Container(
|
||||||
fontWeight: 600,
|
height: 5,
|
||||||
color: Colors.black87,
|
decoration: BoxDecoration(
|
||||||
),
|
color: progress >= 1.0
|
||||||
const SizedBox(height: 6),
|
? Colors.green
|
||||||
Stack(
|
: progress >= 0.5
|
||||||
children: [
|
? Colors.amber
|
||||||
Container(
|
: Colors.red,
|
||||||
height: 5,
|
borderRadius: BorderRadius.circular(6),
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.grey[300],
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.circular(6),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
FractionallySizedBox(
|
|
||||||
widthFactor: progress,
|
|
||||||
child: Container(
|
|
||||||
height: 5,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: progress >= 1.0
|
|
||||||
? Colors.green
|
|
||||||
: progress >= 0.5
|
|
||||||
? Colors.amber
|
|
||||||
: Colors.red,
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.circular(6),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
MyText.bodySmall(
|
|
||||||
"${(progress * 100).toStringAsFixed(1)}%",
|
|
||||||
fontWeight: 500,
|
|
||||||
color: progress >= 1.0
|
|
||||||
? Colors.green[700]
|
|
||||||
: progress >= 0.5
|
|
||||||
? Colors.amber[800]
|
|
||||||
: Colors.red[700],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
SingleChildScrollView(
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
if ((task.reportedDate == null ||
|
|
||||||
task.reportedDate
|
|
||||||
.toString()
|
|
||||||
.isEmpty) &&
|
|
||||||
permissionController.hasPermission(
|
|
||||||
Permissions
|
|
||||||
.assignReportTask)) ...[
|
|
||||||
TaskActionButtons.reportButton(
|
|
||||||
context: context,
|
|
||||||
task: task,
|
|
||||||
completed: completed.toInt(),
|
|
||||||
refreshCallback: _refreshData,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
] else if (task.approvedBy == null &&
|
|
||||||
permissionController.hasPermission(
|
|
||||||
Permissions.approveTask)) ...[
|
|
||||||
TaskActionButtons.reportActionButton(
|
|
||||||
context: context,
|
|
||||||
task: task,
|
|
||||||
parentTaskID: parentTaskID,
|
|
||||||
workAreaId: workAreaId.toString(),
|
|
||||||
activityId: activityId.toString(),
|
|
||||||
completed: completed.toInt(),
|
|
||||||
refreshCallback: _refreshData,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 5),
|
|
||||||
],
|
|
||||||
TaskActionButtons.commentButton(
|
|
||||||
context: context,
|
|
||||||
task: task,
|
|
||||||
parentTaskID: parentTaskID,
|
|
||||||
workAreaId: workAreaId.toString(),
|
|
||||||
activityId: activityId.toString(),
|
|
||||||
refreshCallback: _refreshData,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 4),
|
||||||
|
MyText.bodySmall(
|
||||||
|
"${(progress * 100).toStringAsFixed(1)}%",
|
||||||
|
fontWeight: 500,
|
||||||
|
color: progress >= 1.0
|
||||||
|
? Colors.green[700]
|
||||||
|
: progress >= 0.5
|
||||||
|
? Colors.amber[800]
|
||||||
|
: Colors.red[700],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
if ((task.reportedDate == null ||
|
||||||
|
task.reportedDate
|
||||||
|
.toString()
|
||||||
|
.isEmpty) &&
|
||||||
|
permissionController.hasPermission(
|
||||||
|
Permissions.assignReportTask)) ...[
|
||||||
|
TaskActionButtons.reportButton(
|
||||||
|
context: context,
|
||||||
|
task: task,
|
||||||
|
completed: completed.toInt(),
|
||||||
|
refreshCallback: _refreshData,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
] else if (task.approvedBy == null &&
|
||||||
|
permissionController.hasPermission(
|
||||||
|
Permissions.approveTask)) ...[
|
||||||
|
TaskActionButtons.reportActionButton(
|
||||||
|
context: context,
|
||||||
|
task: task,
|
||||||
|
parentTaskID: parentTaskID,
|
||||||
|
workAreaId: workAreaId.toString(),
|
||||||
|
activityId: activityId.toString(),
|
||||||
|
completed: completed.toInt(),
|
||||||
|
refreshCallback: _refreshData,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 5),
|
||||||
|
],
|
||||||
|
TaskActionButtons.commentButton(
|
||||||
|
context: context,
|
||||||
|
task: task,
|
||||||
|
parentTaskID: parentTaskID,
|
||||||
|
workAreaId: workAreaId.toString(),
|
||||||
|
activityId: activityId.toString(),
|
||||||
|
refreshCallback: _refreshData,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
if (index != tasksForDate.length - 1)
|
),
|
||||||
Divider(
|
|
||||||
color: Colors.grey.withOpacity(0.2),
|
|
||||||
thickness: 1,
|
|
||||||
height: 1),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
);
|
);
|
||||||
|
@ -37,18 +37,19 @@ class _DailyTaskPlanningScreenState extends State<DailyTaskPlanningScreen>
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
// Initial fetch if a project is already selected
|
|
||||||
final projectId = projectController.selectedProjectId.value;
|
final projectId = projectController.selectedProjectId.value;
|
||||||
if (projectId.isNotEmpty) {
|
if (projectId.isNotEmpty) {
|
||||||
dailyTaskPlanningController.fetchTaskData(projectId);
|
dailyTaskPlanningController.fetchTaskData(projectId);
|
||||||
|
serviceController.fetchServices(projectId); // <-- Fetch services here
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reactive fetch on project ID change
|
|
||||||
ever<String>(
|
ever<String>(
|
||||||
projectController.selectedProjectId,
|
projectController.selectedProjectId,
|
||||||
(newProjectId) {
|
(newProjectId) {
|
||||||
if (newProjectId.isNotEmpty) {
|
if (newProjectId.isNotEmpty) {
|
||||||
dailyTaskPlanningController.fetchTaskData(newProjectId);
|
dailyTaskPlanningController.fetchTaskData(newProjectId);
|
||||||
|
serviceController
|
||||||
|
.fetchServices(newProjectId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -150,8 +151,7 @@ class _DailyTaskPlanningScreenState extends State<DailyTaskPlanningScreen>
|
|||||||
Padding(
|
Padding(
|
||||||
padding: MySpacing.x(10),
|
padding: MySpacing.x(10),
|
||||||
child: ServiceSelector(
|
child: ServiceSelector(
|
||||||
controller:
|
controller: serviceController,
|
||||||
serviceController,
|
|
||||||
height: 40,
|
height: 40,
|
||||||
onSelectionChanged: (service) async {
|
onSelectionChanged: (service) async {
|
||||||
final projectId =
|
final projectId =
|
||||||
|
Loading…
x
Reference in New Issue
Block a user