diff --git a/lib/model/attendance/attendence_filter_sheet.dart b/lib/model/attendance/attendence_filter_sheet.dart index 059f671..277b4b4 100644 --- a/lib/model/attendance/attendence_filter_sheet.dart +++ b/lib/model/attendance/attendence_filter_sheet.dart @@ -2,10 +2,8 @@ import 'package:flutter/material.dart'; import 'package:on_field_work/controller/permission_controller.dart'; import 'package:on_field_work/controller/attendance/attendance_screen_controller.dart'; import 'package:on_field_work/helpers/widgets/my_text.dart'; -import 'package:on_field_work/helpers/utils/permission_constants.dart'; import 'package:on_field_work/helpers/utils/base_bottom_sheet.dart'; import 'package:get/get.dart'; -import 'package:on_field_work/helpers/utils/date_time_utils.dart'; import 'package:on_field_work/helpers/widgets/date_range_picker.dart'; class AttendanceFilterBottomSheet extends StatefulWidget { @@ -27,21 +25,6 @@ class AttendanceFilterBottomSheet extends StatefulWidget { class _AttendanceFilterBottomSheetState extends State { - late String tempSelectedTab; - - @override - void initState() { - super.initState(); - tempSelectedTab = widget.selectedTab; - } - - String getLabelText() { - final start = DateTimeUtils.formatDate( - widget.controller.startDateAttendance.value, 'dd MMM yyyy'); - final end = DateTimeUtils.formatDate( - widget.controller.endDateAttendance.value, 'dd MMM yyyy'); - return "$start - $end"; - } Widget _popupSelector({ required String currentValue, @@ -51,12 +34,8 @@ class _AttendanceFilterBottomSheetState return PopupMenuButton( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), onSelected: onSelected, - itemBuilder: (context) => items - .map((e) => PopupMenuItem( - value: e, - child: MyText(e), - )) - .toList(), + itemBuilder: (context) => + items.map((e) => PopupMenuItem(value: e, child: MyText(e))).toList(), child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( @@ -107,46 +86,11 @@ class _AttendanceFilterBottomSheetState ); } - List buildMainFilters() { - final hasRegularizationPermission = widget.permissionController - .hasPermission(Permissions.regularizeAttendance); - - final viewOptions = [ - {'label': 'Today\'s Attendance', 'value': 'todaysAttendance'}, - {'label': 'Attendance Logs', 'value': 'attendanceLogs'}, - {'label': 'Regularization Requests', 'value': 'regularizationRequests'}, - ]; - - final filteredOptions = viewOptions.where((item) { - return item['value'] != 'regularizationRequests' || - hasRegularizationPermission; - }).toList(); - - final List widgets = [ - Padding( - padding: const EdgeInsets.only(bottom: 4), - child: Align( - alignment: Alignment.centerLeft, - child: MyText.titleSmall("View", fontWeight: 600), - ), - ), - ...filteredOptions.map((item) { - return RadioListTile( - dense: true, - contentPadding: EdgeInsets.zero, - title: MyText.bodyMedium( - item['label']!, - fontWeight: 500, - ), - value: item['value']!, - groupValue: tempSelectedTab, - onChanged: (value) => setState(() => tempSelectedTab = value!), - ); - }), - ]; + List _buildFilters() { + final List widgets = []; + // Organization selector widgets.addAll([ - const Divider(), Padding( padding: const EdgeInsets.only(top: 12, bottom: 12), child: Align( @@ -180,7 +124,8 @@ class _AttendanceFilterBottomSheetState }), ]); - if (tempSelectedTab == 'attendanceLogs') { + // Date range (only for Attendance Logs) + if (widget.selectedTab == 'attendanceLogs') { widgets.addAll([ const Divider(), Padding( @@ -208,24 +153,20 @@ class _AttendanceFilterBottomSheetState @override Widget build(BuildContext context) { return SafeArea( - // ← FIX: avoids hiding under navigation buttons child: BaseBottomSheet( title: "Attendance Filter", submitText: "Apply", onCancel: () => Navigator.pop(context), onSubmit: () => Navigator.pop(context, { - 'selectedTab': tempSelectedTab, 'selectedOrganization': widget.controller.selectedOrganization?.id, }), child: Padding( - padding: - const EdgeInsets.only(bottom: 24), // ← FIX: extra safe padding + padding: const EdgeInsets.only(bottom: 24), child: SingleChildScrollView( - // ← FIX: full scrollable in landscape physics: const BouncingScrollPhysics(), child: Column( crossAxisAlignment: CrossAxisAlignment.start, - children: buildMainFilters(), + children: _buildFilters(), ), ), ), diff --git a/lib/view/Attendence/attendance_logs_tab.dart b/lib/view/Attendence/attendance_logs_tab.dart index e71aabb..49ec168 100644 --- a/lib/view/Attendence/attendance_logs_tab.dart +++ b/lib/view/Attendence/attendance_logs_tab.dart @@ -3,7 +3,6 @@ import 'package:get/get.dart'; import 'package:on_field_work/controller/attendance/attendance_screen_controller.dart'; import 'package:on_field_work/helpers/utils/date_time_utils.dart'; import 'package:on_field_work/helpers/widgets/avatar.dart'; -import 'package:on_field_work/helpers/widgets/my_card.dart'; import 'package:on_field_work/helpers/widgets/my_container.dart'; import 'package:on_field_work/helpers/widgets/my_spacing.dart'; import 'package:on_field_work/helpers/widgets/my_text.dart'; @@ -12,21 +11,14 @@ import 'package:on_field_work/model/attendance/log_details_view.dart'; import 'package:on_field_work/model/attendance/attendence_action_button.dart'; import 'package:on_field_work/helpers/utils/attendance_actions.dart'; -class AttendanceLogsTab extends StatefulWidget { +class AttendanceLogsTab extends StatelessWidget { final AttendanceController controller; const AttendanceLogsTab({super.key, required this.controller}); - @override - State createState() => _AttendanceLogsTabState(); -} - -class _AttendanceLogsTabState extends State { Widget _buildStatusHeader() { return Obx(() { - if (!widget.controller.showPendingOnly.value) { - return const SizedBox.shrink(); - } + if (!controller.showPendingOnly.value) return const SizedBox.shrink(); return Container( width: double.infinity, @@ -46,7 +38,7 @@ class _AttendanceLogsTabState extends State { ), ), InkWell( - onTap: () => widget.controller.showPendingOnly.value = false, + onTap: () => controller.showPendingOnly.value = false, child: const Icon(Icons.close, size: 18, color: Colors.orange), ), ], @@ -55,7 +47,6 @@ class _AttendanceLogsTabState extends State { }); } - /// Return button text priority for sorting inside same date int _getActionPriority(employee) { final text = AttendanceButtonHelper.getButtonText( activity: employee.activity, @@ -77,32 +68,20 @@ class _AttendanceLogsTabState extends State { final isCheckoutAction = text.contains("checkout") || text.contains("check out"); - int priority; - if (isYesterdayCheckIn && isMissingCheckout && isCheckoutAction) { - priority = 0; - } else if (isCheckoutAction) { - priority = 0; - } else if (text.contains("regular")) { - priority = 1; - } else if (text == "requested") { - priority = 2; - } else if (text == "approved") { - priority = 3; - } else if (text == "rejected") { - priority = 4; - } else { - priority = 5; - } - return priority; + if (isYesterdayCheckIn && isMissingCheckout && isCheckoutAction) return 0; + if (isCheckoutAction) return 0; + if (text.contains("regular")) return 1; + if (text == "requested") return 2; + if (text == "approved") return 3; + if (text == "rejected") return 4; + return 5; } @override Widget build(BuildContext context) { return Obx(() { - final allLogs = List.of(widget.controller.filteredLogs); - - // Filter logs if "pending only" - final showPendingOnly = widget.controller.showPendingOnly.value; + final allLogs = List.of(controller.filteredLogs); + final showPendingOnly = controller.showPendingOnly.value; final filteredLogs = showPendingOnly ? allLogs.where((emp) => emp.activity == 1).toList() : allLogs; @@ -116,7 +95,6 @@ class _AttendanceLogsTabState extends State { groupedLogs.putIfAbsent(dateKey, () => []).add(log); } - // Sort dates (latest first) final sortedDates = groupedLogs.keys.toList() ..sort((a, b) { final da = DateTimeUtils.parseDate(a, 'dd MMM yyyy') ?? DateTime(0); @@ -125,175 +103,188 @@ class _AttendanceLogsTabState extends State { }); final dateRangeText = - '${DateTimeUtils.formatDate(widget.controller.startDateAttendance.value, 'dd MMM yyyy')} - ' - '${DateTimeUtils.formatDate(widget.controller.endDateAttendance.value, 'dd MMM yyyy')}'; + '${DateTimeUtils.formatDate(controller.startDateAttendance.value, 'dd MMM yyyy')} - ' + '${DateTimeUtils.formatDate(controller.endDateAttendance.value, 'dd MMM yyyy')}'; - return SingleChildScrollView( - padding: MySpacing.only(bottom: 80), // Added bottom spacing for scroll view - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Header row - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - MyText.titleMedium("Attendance Logs", fontWeight: 600), - widget.controller.isLoading.value - ? SkeletonLoaders.dateSkeletonLoader() - : MyText.bodySmall( - dateRangeText, - fontWeight: 600, - color: Colors.grey[700], - overflow: TextOverflow.ellipsis, - ), - ], - ), - ), - - // Pending-only header - _buildStatusHeader(), - MySpacing.height(8), - - // Content: loader, empty, or logs - if (widget.controller.isLoadingAttendanceLogs.value) - SkeletonLoaders.employeeListSkeletonLoader() - else if (filteredLogs.isEmpty) - SizedBox( - height: 120, - child: Center( - child: Text(showPendingOnly - ? "No Pending Actions Found" - : "No Attendance Logs Found for this Project"), - ), - ) - else - MyCard.bordered( - paddingAll: 8, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - for (final date in sortedDates) ...[ - Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: MyText.bodyMedium(date, fontWeight: 700), + // Sticky header + scrollable list + return Column( + children: [ + // Header Row + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + controller.isLoadingAttendanceLogs.value + ? SkeletonLoaders.dateSkeletonLoader() + : MyText.bodySmall( + dateRangeText, + fontWeight: 600, + color: Colors.grey[700], + overflow: TextOverflow.ellipsis, ), + ], + ), + ), - // Sort employees inside this date by action priority first, then latest entry - for (final emp in (groupedLogs[date]! - ..sort( - (a, b) { - final priorityCompare = _getActionPriority(a) - .compareTo(_getActionPriority(b)); - if (priorityCompare != 0) return priorityCompare; + // Pending-only header + _buildStatusHeader(), - final aTime = a.checkOut ?? a.checkIn ?? DateTime(0); - final bTime = b.checkOut ?? b.checkIn ?? DateTime(0); - return bTime.compareTo( - aTime); - }, - ))) ...[ - MyContainer( - paddingAll: 8, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Avatar( - firstName: emp.firstName, - lastName: emp.lastName, - size: 31, - ), - MySpacing.width(16), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( + // Divider between header and list + const Divider(height: 1), + + // Scrollable attendance logs + Expanded( + child: controller.isLoadingAttendanceLogs.value + ? SkeletonLoaders.employeeListSkeletonLoader() + : filteredLogs.isEmpty + ? Center( + child: Text(showPendingOnly + ? "No Pending Actions Found" + : "No Attendance Logs Found for this Project"), + ) + : ListView.builder( + padding: MySpacing.all(8), + itemCount: sortedDates.length, + itemBuilder: (context, dateIndex) { + final date = sortedDates[dateIndex]; + final employees = groupedLogs[date]! + ..sort((a, b) { + final priorityCompare = _getActionPriority(a) + .compareTo(_getActionPriority(b)); + if (priorityCompare != 0) return priorityCompare; + final aTime = + a.checkOut ?? a.checkIn ?? DateTime(0); + final bTime = + b.checkOut ?? b.checkIn ?? DateTime(0); + return bTime.compareTo(aTime); + }); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: + const EdgeInsets.symmetric(vertical: 8), + child: MyText.bodyMedium(date, fontWeight: 700), + ), + ...employees.map( + (emp) => Column( + children: [ + MyContainer( + paddingAll: 8, + child: Row( + crossAxisAlignment: + CrossAxisAlignment.start, children: [ - Flexible( - child: MyText.bodyMedium( - emp.name, - fontWeight: 600, - overflow: TextOverflow.ellipsis, - ), + Avatar( + firstName: emp.firstName, + lastName: emp.lastName, + size: 31, ), - MySpacing.width(6), - Flexible( - child: MyText.bodySmall( - '(${emp.designation})', - fontWeight: 600, - color: Colors.grey[700], - overflow: TextOverflow.ellipsis, + MySpacing.width(16), + Expanded( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Row( + children: [ + Flexible( + child: MyText.bodyMedium( + emp.name, + fontWeight: 600, + overflow: TextOverflow + .ellipsis, + ), + ), + MySpacing.width(6), + Flexible( + child: MyText.bodySmall( + '(${emp.designation})', + fontWeight: 600, + color: Colors.grey[700], + overflow: TextOverflow + .ellipsis, + ), + ), + ], + ), + MySpacing.height(8), + if (emp.checkIn != null || + emp.checkOut != null) + Row( + children: [ + if (emp.checkIn != + null) ...[ + const Icon( + Icons + .arrow_circle_right, + size: 16, + color: + Colors.green), + MySpacing.width(4), + MyText.bodySmall( + DateTimeUtils + .formatDate( + emp.checkIn!, + 'hh:mm a'), + fontWeight: 600, + ), + MySpacing.width(16), + ], + if (emp.checkOut != + null) ...[ + const Icon( + Icons + .arrow_circle_left, + size: 16, + color: Colors.red), + MySpacing.width(4), + MyText.bodySmall( + DateTimeUtils + .formatDate( + emp.checkOut!, + 'hh:mm a'), + fontWeight: 600, + ), + ], + ], + ), + MySpacing.height(12), + Row( + mainAxisAlignment: + MainAxisAlignment.end, + children: [ + AttendanceActionButton( + employee: emp, + attendanceController: + controller, + ), + MySpacing.width(8), + AttendanceLogViewButton( + employee: emp, + attendanceController: + controller, + ), + ], + ), + ], ), ), ], ), - MySpacing.height(8), - if (emp.checkIn != null || - emp.checkOut != null) - Row( - children: [ - if (emp.checkIn != null) ...[ - const Icon(Icons.arrow_circle_right, - size: 16, color: Colors.green), - MySpacing.width(4), - MyText.bodySmall( - DateTimeUtils.formatDate( - emp.checkIn!, 'hh:mm a'), - fontWeight: 600, - ), - MySpacing.width(16), - ], - if (emp.checkOut != null) ...[ - const Icon(Icons.arrow_circle_left, - size: 16, color: Colors.red), - MySpacing.width(4), - MyText.bodySmall( - DateTimeUtils.formatDate( - emp.checkOut!, 'hh:mm a'), - fontWeight: 600, - ), - ], - ], - ), - MySpacing.height(12), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - AttendanceActionButton( - employee: emp, - attendanceController: widget.controller, - ), - MySpacing.width(8), - AttendanceLogViewButton( - employee: emp, - attendanceController: widget.controller, - ), - ], - ), - ], - ), + ), + ], ), - ], - ), - ), - Divider(color: Colors.grey.withOpacity(0.3)), - ], - ], - // Remove the trailing Divider if we are at the end of the logs - if (sortedDates.isNotEmpty) - // We can use MySpacing.height(8) here if we need to ensure the last divider doesn't show - // But keeping the original structure, the divider is inside the inner loop. - // A clean up would be needed to manage that last divider, but for now, - // the bottom padding handles the visible spacing. - const SizedBox.shrink(), - ], - ), - ), - ], - ), + ), + ], + ); + }, + ), + ), + ], ); }); } -} \ No newline at end of file +} diff --git a/lib/view/Attendence/attendance_screen.dart b/lib/view/Attendence/attendance_screen.dart index 0efd9d7..56f2d16 100644 --- a/lib/view/Attendence/attendance_screen.dart +++ b/lib/view/Attendence/attendance_screen.dart @@ -61,9 +61,9 @@ class _AttendanceScreenState extends State void _initializeTabs() async { final allTabs = [ - {'label': "Today's Attendance", 'value': 'todaysAttendance'}, - {'label': "Attendance Logs", 'value': 'attendanceLogs'}, - {'label': "Regularization Requests", 'value': 'regularizationRequests'}, + {'label': "Today's", 'value': 'todaysAttendance'}, + {'label': "Logs", 'value': 'attendanceLogs'}, + {'label': "Regularization", 'value': 'regularizationRequests'}, ]; final hasRegularizationPermission = @@ -133,8 +133,7 @@ class _AttendanceScreenState extends State final query = attendanceController.searchQuery.value; return TextField( controller: TextEditingController(text: query) - ..selection = - TextSelection.collapsed(offset: query.length), + ..selection = TextSelection.collapsed(offset: query.length), onChanged: (value) { attendanceController.searchQuery.value = value; }, @@ -325,8 +324,9 @@ class _AttendanceScreenState extends State child: noProjectSelected ? _buildNoProjectWidget() : SizedBox( - height: MediaQuery.of(context).size.height - - 200, + height: + MediaQuery.of(context).size.height - + 200, child: _buildTabBarView(), ), ), diff --git a/lib/view/Attendence/regularization_requests_tab.dart b/lib/view/Attendence/regularization_requests_tab.dart index 52eea9d..bd1859d 100644 --- a/lib/view/Attendence/regularization_requests_tab.dart +++ b/lib/view/Attendence/regularization_requests_tab.dart @@ -1,4 +1,3 @@ -// lib/view/attendance/tabs/regularization_requests_tab.dart import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; @@ -19,140 +18,136 @@ class RegularizationRequestsTab extends StatelessWidget { @override Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 4.0), - child: MyText.titleMedium("Regularization Requests", fontWeight: 600), - ), - Obx(() { - final employees = controller.filteredRegularizationLogs; + return Obx(() { + final isLoading = controller.isLoadingRegularizationLogs.value; + final employees = controller.filteredRegularizationLogs; - if (controller.isLoadingRegularizationLogs.value) { - return SkeletonLoaders.employeeListSkeletonLoader(); - } + if (isLoading) { + return SkeletonLoaders.employeeListSkeletonLoader(); + } - if (employees.isEmpty) { - return const SizedBox( - height: 120, - child: Center( - child: - Text("No Regularization Requests Found for this Project"), - ), - ); - } + if (employees.isEmpty) { + return const SizedBox( + height: 120, + child: Center( + child: Text("No Regularization Requests Found for this Project"), + ), + ); + } - return MyCard.bordered( - paddingAll: 8, - child: Column( - children: List.generate(employees.length, (index) { - final employee = employees[index]; - return Column( - children: [ - MyContainer( - paddingAll: 8, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Avatar( - firstName: employee.firstName, - lastName: employee.lastName, - size: 31, - ), - MySpacing.width(16), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Flexible( - child: MyText.bodyMedium( - employee.name, - fontWeight: 600, - overflow: TextOverflow.ellipsis, - ), + return ListView.builder( + itemCount: employees.length, + padding: MySpacing.only(bottom: 80), + itemBuilder: (context, index) { + final employee = employees[index]; // Corrected index + + return Padding( + padding: const EdgeInsets.only(bottom: 12), + child: MyCard.bordered( + paddingAll: 8, + child: Column( + children: [ + MyContainer( + paddingAll: 8, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Avatar( + firstName: employee.firstName, + lastName: employee.lastName, + size: 35, + ), + MySpacing.width(16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Flexible( + child: MyText.bodyMedium( + employee.name, + fontWeight: 600, + overflow: TextOverflow.ellipsis, ), - MySpacing.width(6), - Flexible( - child: MyText.bodySmall( - '(${employee.role})', - fontWeight: 600, - overflow: TextOverflow.ellipsis, - color: Colors.grey[700], - ), - ), - ], - ), - MySpacing.height(8), - if (employee.checkIn != null || - employee.checkOut != null) - Row( - children: [ - if (employee.checkIn != null) ...[ - const Icon(Icons.arrow_circle_right, - size: 16, color: Colors.green), - MySpacing.width(4), - MyText.bodySmall( - DateFormat('hh:mm a') - .format(employee.checkIn!), - fontWeight: 600, - ), - MySpacing.width(16), - ], - if (employee.checkOut != null) ...[ - const Icon(Icons.arrow_circle_left, - size: 16, color: Colors.red), - MySpacing.width(4), - MyText.bodySmall( - DateFormat('hh:mm a') - .format(employee.checkOut!), - fontWeight: 600, - ), - ], - ], ), - MySpacing.height(12), + MySpacing.width(6), + Flexible( + child: MyText.bodySmall( + '(${employee.role})', + fontWeight: 600, + overflow: TextOverflow.ellipsis, + color: Colors.grey[700], + ), + ), + ], + ), + MySpacing.height(8), + if (employee.checkIn != null || + employee.checkOut != null) Row( - mainAxisAlignment: MainAxisAlignment.end, children: [ - RegularizeActionButton( - attendanceController: controller, - log: employee, - uniqueLogKey: employee.employeeId, - action: ButtonActions.approve, - ), - const SizedBox(width: 8), - RegularizeActionButton( - attendanceController: controller, - log: employee, - uniqueLogKey: employee.employeeId, - action: ButtonActions.reject, - ), - const SizedBox(width: 8), - if (employee.checkIn != null) - AttendanceLogViewButton( - employee: employee, - attendanceController: controller, + if (employee.checkIn != null) ...[ + const Icon(Icons.arrow_circle_right, + size: 16, color: Colors.green), + MySpacing.width(4), + MyText.bodySmall( + DateFormat('hh:mm a') + .format(employee.checkIn!), + fontWeight: 600, ), + MySpacing.width(16), + ], + if (employee.checkOut != null) ...[ + const Icon(Icons.arrow_circle_left, + size: 16, color: Colors.red), + MySpacing.width(4), + MyText.bodySmall( + DateFormat('hh:mm a') + .format(employee.checkOut!), + fontWeight: 600, + ), + ], ], ), - ], - ), + MySpacing.height(12), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + RegularizeActionButton( + attendanceController: controller, + log: employee, + uniqueLogKey: employee.employeeId, + action: ButtonActions.approve, + ), + const SizedBox(width: 8), + RegularizeActionButton( + attendanceController: controller, + log: employee, + uniqueLogKey: employee.employeeId, + action: ButtonActions.reject, + ), + const SizedBox(width: 8), + if (employee.checkIn != null) + AttendanceLogViewButton( + employee: employee, + attendanceController: controller, + ), + ], + ), + ], ), - ], - ), + ), + ], ), - if (index != employees.length - 1) - Divider(color: Colors.grey.withOpacity(0.3)), - ], - ); - }), + ), + + ], + ), ), ); - }), - ], - ); + }, + ); + }); } } diff --git a/lib/view/Attendence/todays_attendance_tab.dart b/lib/view/Attendence/todays_attendance_tab.dart index f0bcc0c..3a3aea9 100644 --- a/lib/view/Attendence/todays_attendance_tab.dart +++ b/lib/view/Attendence/todays_attendance_tab.dart @@ -4,7 +4,6 @@ import 'package:on_field_work/controller/attendance/attendance_screen_controller import 'package:on_field_work/helpers/utils/date_time_utils.dart'; import 'package:on_field_work/helpers/widgets/avatar.dart'; import 'package:on_field_work/helpers/widgets/my_card.dart'; -import 'package:on_field_work/helpers/widgets/my_container.dart'; import 'package:on_field_work/helpers/widgets/my_spacing.dart'; import 'package:on_field_work/helpers/widgets/my_text.dart'; import 'package:on_field_work/helpers/widgets/my_custom_skeleton.dart'; @@ -36,11 +35,9 @@ class TodaysAttendanceTab extends StatelessWidget { } return ListView.builder( - itemCount: employees.length + 1, // +1 for header - padding: MySpacing.only( - bottom: 80), // Adjusted padding to add spacing at the bottom + itemCount: employees.length + 1, + padding: MySpacing.only(bottom: 80), itemBuilder: (context, index) { - // --- Header Row --- if (index == 0) { return Padding( padding: const EdgeInsets.only(bottom: 12, top: 4), @@ -59,68 +56,62 @@ class TodaysAttendanceTab extends StatelessWidget { final employee = employees[index - 1]; return Padding( - padding: const EdgeInsets.only(bottom: 12), + padding: const EdgeInsets.only(bottom: 8), child: MyCard.bordered( - paddingAll: 12, + paddingAll: 10, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // 1. Employee Info Row + // --- 1. Employee Info Row (Avatar, Name, Designation ONLY) --- Row( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, children: [ + // Avatar Avatar( firstName: employee.firstName, lastName: employee.lastName, - size: 35, + size: 30, ), - MySpacing.width(16), + MySpacing.width(10), + + // Employee Details (Expanded to use remaining space) Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, children: [ - MyText.titleMedium(employee.name, fontWeight: 600), - MySpacing.height(2), - MyText.bodySmall( + MyText.titleSmall(employee.name, + fontWeight: 600, overflow: TextOverflow.ellipsis), + MyText.labelSmall( employee.designation, fontWeight: 500, color: Colors.grey[600], + overflow: TextOverflow.ellipsis, ), ], ), ), + + // Status Text (Added back for context) + if (employee.checkIn == null) + MyText.bodySmall( + 'Check In Pending', + fontWeight: 600, + color: Colors.red, + ) + else if (employee.checkOut == null) + MyText.bodySmall( + 'Checked In', + fontWeight: 600, + color: Colors.green, + ), ], ), - // Separator - if (employee.checkIn != null || employee.checkOut != null) - const Divider(height: 24), - - // 2. Attendance Time Details Row - if (employee.checkIn != null || employee.checkOut != null) - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - // Check-in Time - _buildLogTime( - icon: Icons.login, - color: Colors.green, - label: 'Check-in', - time: employee.checkIn, - ), - - // Check-out Time - _buildLogTime( - icon: Icons.logout, - color: Colors.red, - label: 'Check-out', - time: employee.checkOut, - ), - ], - ), - - // 3. Action Buttons Row - MySpacing.height(16), + // --- Separator before buttons --- + MySpacing.height(12), + + // --- 2. Action Buttons Row (Below main info) --- Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -145,50 +136,4 @@ class TodaysAttendanceTab extends StatelessWidget { ); }); } - - // Helper function to build a cleaner log time widget - Widget _buildLogTime({ - required IconData icon, - required Color color, - required String label, - required DateTime? time, - }) { - if (time == null) { - return MyContainer( - padding: MySpacing.xy(12, 6), - borderRadiusAll: 5, - color: Colors.grey[100], - child: Row( - children: [ - Icon(icon, size: 16, color: Colors.grey), - MySpacing.width(6), - MyText.bodySmall('$label: **N/A**', - fontWeight: 600, color: Colors.grey), - ], - ), - ); - } - return MyContainer( - padding: MySpacing.xy(12, 6), - borderRadiusAll: 6, - color: color.withOpacity(0.1), - child: Row( - children: [ - Icon(icon, size: 16, color: color), - MySpacing.width(6), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - MyText.labelSmall(label, color: color, fontWeight: 600), - MyText.bodyMedium( - DateTimeUtils.formatDate(time, 'hh:mm a'), - fontWeight: 600, - color: Colors.black87, - ), - ], - ), - ], - ), - ); - } -} +} \ No newline at end of file