import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:marco/helpers/theme/app_theme.dart'; import 'package:marco/helpers/utils/mixins/ui_mixin.dart'; import 'package:marco/helpers/widgets/my_card.dart'; import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/controller/permission_controller.dart'; import 'package:marco/controller/task_Planning/daily_task_Planning_controller.dart'; import 'package:marco/controller/project_controller.dart'; import 'package:percent_indicator/percent_indicator.dart'; import 'package:marco/model/dailyTaskPlanning/assign_task_bottom_sheet .dart'; import 'package:marco/helpers/widgets/my_custom_skeleton.dart'; import 'package:marco/helpers/utils/permission_constants.dart'; import 'package:marco/helpers/widgets/my_refresh_indicator.dart'; import 'package:marco/controller/tenant/service_controller.dart'; import 'package:marco/helpers/widgets/tenant/service_selector.dart'; class DailyTaskPlanningScreen extends StatefulWidget { DailyTaskPlanningScreen({super.key}); @override State createState() => _DailyTaskPlanningScreenState(); } class _DailyTaskPlanningScreenState extends State with UIMixin { final DailyTaskPlanningController dailyTaskPlanningController = Get.put(DailyTaskPlanningController()); final PermissionController permissionController = Get.put(PermissionController()); final ProjectController projectController = Get.find(); final ServiceController serviceController = Get.put(ServiceController()); @override void initState() { super.initState(); final projectId = projectController.selectedProjectId.value; if (projectId.isNotEmpty) { dailyTaskPlanningController.fetchTaskData(projectId); serviceController.fetchServices(projectId); // <-- Fetch services here } ever( projectController.selectedProjectId, (newProjectId) { if (newProjectId.isNotEmpty) { dailyTaskPlanningController.fetchTaskData(newProjectId); serviceController .fetchServices(newProjectId); } }, ); } @override Widget build(BuildContext context) { return Scaffold( appBar: PreferredSize( preferredSize: const Size.fromHeight(72), child: AppBar( backgroundColor: const Color(0xFFF5F5F5), elevation: 0.5, automaticallyImplyLeading: false, titleSpacing: 0, title: Padding( padding: MySpacing.xy(16, 0), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ IconButton( icon: const Icon(Icons.arrow_back_ios_new, color: Colors.black, size: 20), onPressed: () => Get.offNamed('/dashboard'), ), MySpacing.width(8), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ MyText.titleLarge( 'Daily Task Planning', fontWeight: 700, color: Colors.black, ), MySpacing.height(2), GetBuilder( builder: (projectController) { final projectName = projectController.selectedProject?.name ?? 'Select Project'; return Row( children: [ const Icon(Icons.work_outline, size: 14, color: Colors.grey), MySpacing.width(4), Expanded( child: MyText.bodySmall( projectName, fontWeight: 600, overflow: TextOverflow.ellipsis, color: Colors.grey[700], ), ), ], ); }, ), ], ), ), ], ), ), ), ), body: SafeArea( child: MyRefreshIndicator( onRefresh: () async { final projectId = projectController.selectedProjectId.value; if (projectId.isNotEmpty) { try { await dailyTaskPlanningController.fetchTaskData(projectId); } catch (e) { debugPrint('Error refreshing task data: ${e.toString()}'); } } }, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), // <-- always allow drag padding: MySpacing.x(0), child: ConstrainedBox( // <-- ensures full screen height constraints: BoxConstraints( minHeight: MediaQuery.of(context).size.height - kToolbarHeight - MediaQuery.of(context).padding.top, ), child: GetBuilder( init: dailyTaskPlanningController, tag: 'daily_task_Planning_controller', builder: (controller) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MySpacing.height(flexSpacing), Padding( padding: MySpacing.x(10), child: ServiceSelector( controller: serviceController, height: 40, onSelectionChanged: (service) async { final projectId = projectController.selectedProjectId.value; if (projectId.isNotEmpty) { await dailyTaskPlanningController.fetchTaskData( projectId, // serviceId: service // ?.id, ); } }, ), ), MySpacing.height(flexSpacing), Padding( padding: MySpacing.x(8), child: dailyProgressReportTab(), ), ], ); }, ), ), ), ), ), ); } Widget dailyProgressReportTab() { return Obx(() { final isLoading = dailyTaskPlanningController.isLoading.value; final dailyTasks = dailyTaskPlanningController.dailyTasks; if (isLoading) { return SkeletonLoaders.dailyProgressPlanningSkeletonCollapsedOnly(); } if (dailyTasks.isEmpty) { return Center( child: MyText.bodySmall( "No Progress Report Found", fontWeight: 600, ), ); } final buildingExpansionState = {}; final floorExpansionState = {}; Widget buildExpandIcon(bool isExpanded) { return Container( width: 32, height: 32, decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.grey.shade200, ), child: Icon( isExpanded ? Icons.remove : Icons.add, size: 20, color: Colors.black87, ), ); } return StatefulBuilder(builder: (context, setMainState) { final filteredBuildings = dailyTasks.expand((task) { return task.buildings.where((building) { return building.floors.any((floor) => floor.workAreas.any((area) => area.workItems.isNotEmpty)); }); }).toList(); if (filteredBuildings.isEmpty) { return Center( child: MyText.bodySmall( "No Progress Report Found", fontWeight: 600, ), ); } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: filteredBuildings.map((building) { final buildingKey = building.id.toString(); return MyCard.bordered( borderRadiusAll: 10, paddingAll: 0, margin: MySpacing.bottom(10), child: Theme( data: Theme.of(context) .copyWith(dividerColor: Colors.transparent), child: ExpansionTile( onExpansionChanged: (expanded) { setMainState(() { buildingExpansionState[buildingKey] = expanded; }); }, trailing: buildExpandIcon( buildingExpansionState[buildingKey] ?? false), tilePadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 0), collapsedShape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), leading: Container( decoration: BoxDecoration( color: Colors.blueAccent.withOpacity(0.1), shape: BoxShape.circle, ), padding: const EdgeInsets.all(8), child: Icon( Icons.location_city_rounded, color: Colors.blueAccent, size: 24, ), ), title: MyText.titleMedium( building.name, fontWeight: 700, maxLines: 1, overflow: TextOverflow.ellipsis, ), childrenPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 0), children: building.floors.expand((floor) { final validWorkAreas = floor.workAreas .where((area) => area.workItems.isNotEmpty); // For each valid work area, return a Floor+WorkArea ExpansionTile return validWorkAreas.map((area) { final floorWorkAreaKey = "${buildingKey}_${floor.floorName}_${area.areaName}"; final isExpanded = floorExpansionState[floorWorkAreaKey] ?? false; final workItems = area.workItems; final totalPlanned = workItems.fold( 0, (sum, wi) => sum + (wi.workItem.plannedWork ?? 0)); final totalCompleted = workItems.fold(0, (sum, wi) => sum + (wi.workItem.completedWork ?? 0)); final totalProgress = totalPlanned == 0 ? 0.0 : (totalCompleted / totalPlanned).clamp(0.0, 1.0); return ExpansionTile( onExpansionChanged: (expanded) { setMainState(() { floorExpansionState[floorWorkAreaKey] = expanded; }); }, trailing: Icon( isExpanded ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down, size: 28, color: Colors.black54, ), tilePadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 0), title: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( flex: 3, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText.titleSmall( "Floor: ${floor.floorName}", fontWeight: 600, color: Colors.teal, maxLines: null, overflow: TextOverflow.visible, softWrap: true, ), MySpacing.height(4), MyText.titleSmall( "Work Area: ${area.areaName}", fontWeight: 600, color: Colors.blueGrey, maxLines: null, overflow: TextOverflow.visible, softWrap: true, ), ], ), ), MySpacing.width(12), CircularPercentIndicator( radius: 20.0, lineWidth: 4.0, animation: true, percent: totalProgress, center: Text( "${(totalProgress * 100).toStringAsFixed(0)}%", style: TextStyle( fontWeight: FontWeight.bold, fontSize: 10.0, ), ), circularStrokeCap: CircularStrokeCap.round, progressColor: totalProgress >= 1.0 ? Colors.green : (totalProgress >= 0.5 ? Colors.amber : Colors.red), backgroundColor: Colors.grey[300]!, ), ], ), childrenPadding: const EdgeInsets.only( left: 16, right: 0, bottom: 8), children: area.workItems.map((wItem) { final item = wItem.workItem; final completed = item.completedWork ?? 0; final planned = item.plannedWork ?? 0; final progress = (planned == 0) ? 0.0 : (completed / planned).clamp(0.0, 1.0); return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: MyText.bodyMedium( item.activityMaster?.name ?? "No Activity", fontWeight: 600, maxLines: 2, overflow: TextOverflow.visible, softWrap: true, ), ), MySpacing.width(8), if (item.workCategoryMaster?.name != null) Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 4), decoration: BoxDecoration( color: Colors.blue.shade100, borderRadius: BorderRadius.circular(20), ), child: MyText.bodySmall( item.workCategoryMaster!.name!, fontWeight: 500, color: Colors.blue.shade800, ), ), ], ), MySpacing.height(4), Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( flex: 3, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MySpacing.height(8), MyText.bodySmall( "Completed: $completed / $planned", fontWeight: 600, color: const Color.fromARGB( 221, 0, 0, 0), ), ], ), ), MySpacing.width(16), if (progress < 1.0 && permissionController.hasPermission( Permissions.assignReportTask)) IconButton( icon: Icon( Icons.person_add_alt_1_rounded, color: Color.fromARGB(255, 46, 161, 233), ), onPressed: () { final pendingTask = (planned - completed) .clamp(0, planned) .toInt(); showModalBottomSheet( context: context, isScrollControlled: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular(16)), ), builder: (context) => AssignTaskBottomSheet( buildingName: building.name, floorName: floor.floorName, workAreaName: area.areaName, workLocation: area.areaName, activityName: item.activityMaster?.name ?? "Unknown Activity", pendingTask: pendingTask, workItemId: item.id.toString(), assignmentDate: DateTime.now(), ), ); }, ), ], ), MySpacing.height(8), Stack( children: [ Container( height: 5, 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), ), ), ), ], ), 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]), ), ], ), ); }).toList(), ); }).toList(); }).toList(), ), ), ); }).toList(), ); }); }); } }