diff --git a/lib/controller/permission_controller.dart b/lib/controller/permission_controller.dart index 815c455..30253d7 100644 --- a/lib/controller/permission_controller.dart +++ b/lib/controller/permission_controller.dart @@ -117,8 +117,8 @@ class PermissionController extends GetxController { bool hasPermission(String permissionId) { final hasPerm = permissions.any((p) => p.id == permissionId); - logSafe("Checking permission $permissionId: $hasPerm", - level: LogLevel.debug); + // logSafe("Checking permission $permissionId: $hasPerm", + // level: LogLevel.debug); return hasPerm; } diff --git a/lib/helpers/widgets/my_custom_skeleton.dart b/lib/helpers/widgets/my_custom_skeleton.dart index 4537143..2f04de7 100644 --- a/lib/helpers/widgets/my_custom_skeleton.dart +++ b/lib/helpers/widgets/my_custom_skeleton.dart @@ -33,6 +33,96 @@ class SkeletonLoaders { ); } +// Daily Progress Planning - Infra (Expanded) Skeleton Loader + static Widget dailyProgressPlanningInfraSkeleton() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: List.generate(3, (floorIndex) { + return MyCard( + borderRadiusAll: 8, + paddingAll: 5, + margin: MySpacing.bottom(10), + shadow: MyShadow(elevation: 1.5), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Floor header placeholder + Container( + height: 14, + width: 160, + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(6), + ), + ), + MySpacing.height(10), + + // Divider + Divider(color: Colors.grey.withOpacity(0.3)), + + // Work areas skeleton + Column( + children: List.generate(2, (areaIndex) { + return Padding( + padding: const EdgeInsets.only(top: 8, bottom: 6), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Area title + Container( + height: 12, + width: 120, + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(4), + ), + ), + MySpacing.height(8), + + // Work items skeleton rows + Column( + children: List.generate(2, (itemIndex) { + return Padding( + padding: const EdgeInsets.only(top: 4), + child: Row( + children: [ + // Bullet / icon + Container( + width: 8, + height: 8, + decoration: BoxDecoration( + color: Colors.grey.shade300, + shape: BoxShape.circle, + ), + ), + MySpacing.width(8), + // Item text placeholder + Expanded( + child: Container( + height: 10, + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(4), + ), + ), + ), + ], + ), + ); + }), + ), + ], + ), + ); + }), + ), + ], + ), + ); + }), + ); + } + // Chart Skeleton Loader (Donut Chart) static Widget chartSkeletonLoader() { return MyCard.bordered( diff --git a/lib/view/taskPlanning/daily_task_planning.dart b/lib/view/taskPlanning/daily_task_planning.dart index e45ae50..b5d13a4 100644 --- a/lib/view/taskPlanning/daily_task_planning.dart +++ b/lib/view/taskPlanning/daily_task_planning.dart @@ -88,12 +88,15 @@ class _DailyTaskPlanningScreenState extends State color: Colors.black, ), MySpacing.height(2), - GetBuilder(builder: (projectController) { + GetBuilder( + builder: (projectController) { final projectName = - projectController.selectedProject?.name ?? 'Select Project'; + projectController.selectedProject?.name ?? + 'Select Project'; return Row( children: [ - const Icon(Icons.work_outline, size: 14, color: Colors.grey), + const Icon(Icons.work_outline, + size: 14, color: Colors.grey), MySpacing.width(4), Expanded( child: MyText.bodySmall( @@ -235,16 +238,22 @@ class _DailyTaskPlanningScreenState extends State crossAxisAlignment: CrossAxisAlignment.start, children: buildings.map((building) { final buildingKey = building.id.toString(); - final isBuildingExpanded = buildingExpansionState[buildingKey] ?? false; - final buildingLoading = dailyTaskPlanningController.buildingLoadingStates[buildingKey]?.value ?? false; - final buildingLoaded = dailyTaskPlanningController.buildingsWithDetails.contains(buildingKey); + final isBuildingExpanded = + buildingExpansionState[buildingKey] ?? false; + final buildingLoading = dailyTaskPlanningController + .buildingLoadingStates[buildingKey]?.value ?? + false; + final buildingLoaded = dailyTaskPlanningController + .buildingsWithDetails + .contains(buildingKey); return MyCard.bordered( borderRadiusAll: 10, paddingAll: 0, margin: MySpacing.bottom(10), child: Theme( - data: Theme.of(context).copyWith(dividerColor: Colors.transparent), + data: Theme.of(context) + .copyWith(dividerColor: Colors.transparent), child: ExpansionTile( onExpansionChanged: (expanded) async { setMainState(() { @@ -253,7 +262,8 @@ class _DailyTaskPlanningScreenState extends State if (expanded && !buildingLoaded && !buildingLoading) { // fetch infra details for this building lazily - final projectId = projectController.selectedProjectId.value; + final projectId = + projectController.selectedProjectId.value; if (projectId.isNotEmpty) { await dailyTaskPlanningController.fetchBuildingInfra( building.id.toString(), @@ -265,7 +275,8 @@ class _DailyTaskPlanningScreenState extends State } }, trailing: buildExpandIcon(isBuildingExpanded), - tilePadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 0), + tilePadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 0), collapsedShape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), @@ -290,18 +301,14 @@ class _DailyTaskPlanningScreenState extends State maxLines: 1, overflow: TextOverflow.ellipsis, ), - childrenPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 0), + childrenPadding: + const EdgeInsets.symmetric(horizontal: 5, vertical: 0), children: [ if (buildingLoading) Padding( - padding: const EdgeInsets.all(16.0), - child: Center( - child: SizedBox( - height: 24, - width: 24, - child: CircularProgressIndicator(strokeWidth: 2), - ), - ), + padding: const EdgeInsets.all(0.0), + child: SkeletonLoaders + .dailyProgressPlanningInfraSkeleton(), ) else if (!buildingLoaded || building.floors.isEmpty) Padding( @@ -315,36 +322,55 @@ class _DailyTaskPlanningScreenState extends State // Building is loaded and has floors; render floors -> areas -> items Column( children: building.floors.expand((floor) { - final validWorkAreas = floor.workAreas.where((area) => area.workItems.isNotEmpty); + final validWorkAreas = floor.workAreas + .where((area) => area.workItems.isNotEmpty); return validWorkAreas.map((area) { final floorWorkAreaKey = "${buildingKey}_${floor.floorName}_${area.areaName}"; - final isExpanded = floorExpansionState[floorWorkAreaKey] ?? false; + 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); + 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; + 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), + trailing: const SizedBox + .shrink(), // ← Add this to remove the default trailing icon title: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ + // Collapse/Expand icon on the LEFT + Icon( + isExpanded + ? Icons.keyboard_arrow_up + : Icons.keyboard_arrow_down, + size: 28, + color: Colors.black54, + ), + MySpacing.width(8), + + // Floor + Work area text expanded to take available space Expanded( - flex: 3, child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, children: [ MyText.titleSmall( "Floor: ${floor.floorName}", @@ -366,7 +392,6 @@ class _DailyTaskPlanningScreenState extends State ], ), ), - MySpacing.width(12), CircularPercentIndicator( radius: 20.0, lineWidth: 4.0, @@ -382,29 +407,39 @@ class _DailyTaskPlanningScreenState extends State circularStrokeCap: CircularStrokeCap.round, progressColor: totalProgress >= 1.0 ? Colors.green - : (totalProgress >= 0.5 ? Colors.amber : Colors.red), + : (totalProgress >= 0.5 + ? Colors.amber + : Colors.red), backgroundColor: Colors.grey[300]!, ), ], ), - childrenPadding: const EdgeInsets.only(left: 16, right: 0, bottom: 8), + + childrenPadding: const EdgeInsets.only( + left: 16, right: 0, bottom: 8), children: area.workItems.map((wItem) { + // keep your existing work item UI as-is 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); - + final progress = (planned == 0) + ? 0.0 + : (completed / planned).clamp(0.0, 1.0); return Padding( - padding: const EdgeInsets.symmetric(vertical: 8), + padding: + const EdgeInsets.symmetric(vertical: 8), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, children: [ Row( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, children: [ Expanded( child: MyText.bodyMedium( - item.activityMaster?.name ?? "No Activity", + item.activityMaster?.name ?? + "No Activity", fontWeight: 600, maxLines: 2, overflow: TextOverflow.visible, @@ -412,12 +447,17 @@ class _DailyTaskPlanningScreenState extends State ), ), MySpacing.width(8), - if (item.workCategoryMaster?.name != null) + if (item.workCategoryMaster?.name != + null) Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), + padding: + const EdgeInsets.symmetric( + horizontal: 12, + vertical: 4), decoration: BoxDecoration( color: Colors.blue.shade100, - borderRadius: BorderRadius.circular(20), + borderRadius: + BorderRadius.circular(20), ), child: MyText.bodySmall( item.workCategoryMaster!.name!, @@ -429,48 +469,68 @@ class _DailyTaskPlanningScreenState extends State ), MySpacing.height(4), Row( - crossAxisAlignment: CrossAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, children: [ Expanded( flex: 3, child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, children: [ MySpacing.height(8), MyText.bodySmall( "Completed: $completed / $planned", fontWeight: 600, - color: const Color.fromARGB(221, 0, 0, 0), + color: const Color.fromARGB( + 221, 0, 0, 0), ), ], ), ), MySpacing.width(16), if (progress < 1.0 && - permissionController.hasPermission(Permissions.assignReportTask)) + permissionController + .hasPermission(Permissions + .assignReportTask)) IconButton( icon: const Icon( Icons.person_add_alt_1_rounded, - color: Color.fromARGB(255, 46, 161, 233), + color: Color.fromARGB( + 255, 46, 161, 233), ), onPressed: () { - final pendingTask = (planned - completed).clamp(0, planned).toInt(); + final pendingTask = + (planned - completed) + .clamp(0, planned) + .toInt(); showModalBottomSheet( context: context, isScrollControlled: true, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(16)), + shape: + const RoundedRectangleBorder( + borderRadius: + BorderRadius.vertical( + top: + Radius.circular( + 16)), ), - builder: (context) => AssignTaskBottomSheet( + builder: (context) => + AssignTaskBottomSheet( buildingName: building.name, floorName: floor.floorName, workAreaName: area.areaName, workLocation: area.areaName, - activityName: item.activityMaster?.name ?? "Unknown Activity", + activityName: item + .activityMaster + ?.name ?? + "Unknown Activity", pendingTask: pendingTask, - workItemId: item.id.toString(), - assignmentDate: DateTime.now(), + workItemId: + item.id.toString(), + assignmentDate: + DateTime.now(), ), ); }, @@ -493,7 +553,8 @@ class _DailyTaskPlanningScreenState extends State height: 5, decoration: BoxDecoration( color: Colors.grey[300], - borderRadius: BorderRadius.circular(6), + borderRadius: + BorderRadius.circular(6), ), ), FractionallySizedBox( @@ -503,8 +564,11 @@ class _DailyTaskPlanningScreenState extends State decoration: BoxDecoration( color: progress >= 1.0 ? Colors.green - : (progress >= 0.5 ? Colors.amber : Colors.red), - borderRadius: BorderRadius.circular(6), + : (progress >= 0.5 + ? Colors.amber + : Colors.red), + borderRadius: + BorderRadius.circular(6), ), ), ), @@ -516,7 +580,9 @@ class _DailyTaskPlanningScreenState extends State fontWeight: 500, color: progress >= 1.0 ? Colors.green[700] - : (progress >= 0.5 ? Colors.amber[800] : Colors.red[700]), + : (progress >= 0.5 + ? Colors.amber[800] + : Colors.red[700]), ), ], ),