import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:on_field_work/helpers/utils/mixins/ui_mixin.dart'; import 'package:on_field_work/helpers/widgets/my_flex.dart'; import 'package:on_field_work/helpers/widgets/my_flex_item.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/controller/attendance/attendance_screen_controller.dart'; import 'package:on_field_work/controller/permission_controller.dart'; import 'package:on_field_work/controller/project_controller.dart'; import 'package:on_field_work/view/Attendence/regularization_requests_tab.dart'; import 'package:on_field_work/view/Attendence/attendance_logs_tab.dart'; import 'package:on_field_work/view/Attendence/todays_attendance_tab.dart'; import 'package:on_field_work/helpers/widgets/my_refresh_indicator.dart'; import 'package:on_field_work/helpers/widgets/custom_app_bar.dart'; import 'package:on_field_work/helpers/utils/permission_constants.dart'; import 'package:on_field_work/helpers/widgets/pill_tab_bar.dart'; import 'package:on_field_work/model/attendance/attendence_filter_sheet.dart'; class AttendanceScreen extends StatefulWidget { const AttendanceScreen({super.key}); @override State createState() => _AttendanceScreenState(); } class _AttendanceScreenState extends State with SingleTickerProviderStateMixin, UIMixin { final attendanceController = Get.put(AttendanceController()); final permissionController = Get.put(PermissionController()); final projectController = Get.put(ProjectController()); late TabController _tabController; late List> _tabs; bool _tabsInitialized = false; @override void initState() { super.initState(); ever(permissionController.permissionsLoaded, (loaded) { if (loaded == true && !_tabsInitialized) { WidgetsBinding.instance.addPostFrameCallback((_) { _initializeTabs(); setState(() {}); }); } }); // Watch project changes to reload data ever(projectController.selectedProjectId, (projectId) async { if (projectId.isNotEmpty && _tabsInitialized) { await _fetchTabData(attendanceController.selectedTab); } }); // If permissions are already loaded at init if (permissionController.permissionsLoaded.value) { _initializeTabs(); } } void _initializeTabs() async { final allTabs = [ {'label': "Today's", 'value': 'todaysAttendance'}, {'label': "Logs", 'value': 'attendanceLogs'}, {'label': "Regularization", 'value': 'regularizationRequests'}, ]; final hasRegularizationPermission = permissionController.hasPermission(Permissions.regularizeAttendance); _tabs = allTabs.where((tab) { return tab['value'] != 'regularizationRequests' || hasRegularizationPermission; }).toList(); _tabController = TabController(length: _tabs.length, vsync: this); // Keep selectedTab in sync and fetch data on tab change _tabController.addListener(() async { if (!_tabController.indexIsChanging) { final selectedTab = _tabs[_tabController.index]['value']!; attendanceController.selectedTab = selectedTab; await _fetchTabData(selectedTab); } }); _tabsInitialized = true; // Load initial data for default tab final projectId = projectController.selectedProjectId.value; if (projectId.isNotEmpty) { final initialTab = _tabs[_tabController.index]['value']!; attendanceController.selectedTab = initialTab; await _fetchTabData(initialTab); } } Future _fetchTabData(String tab) async { final projectId = projectController.selectedProjectId.value; if (projectId.isEmpty) return; switch (tab) { case 'todaysAttendance': await attendanceController.fetchTodaysAttendance(projectId); break; case 'attendanceLogs': await attendanceController.fetchAttendanceLogs( projectId, dateFrom: attendanceController.startDateAttendance.value, dateTo: attendanceController.endDateAttendance.value, ); break; case 'regularizationRequests': await attendanceController.fetchRegularizationLogs(projectId); break; } } Future _refreshData() async { await _fetchTabData(attendanceController.selectedTab); } Widget _buildFilterSearchRow() { return Padding( padding: MySpacing.xy(8, 8), child: Row( children: [ Expanded( child: SizedBox( height: 35, child: Obx(() { final query = attendanceController.searchQuery.value; return TextField( controller: TextEditingController(text: query) ..selection = TextSelection.collapsed(offset: query.length), onChanged: (value) { attendanceController.searchQuery.value = value; }, decoration: InputDecoration( contentPadding: const EdgeInsets.symmetric(horizontal: 12), prefixIcon: const Icon(Icons.search, size: 20, color: Colors.grey), suffixIcon: query.isNotEmpty ? IconButton( icon: const Icon(Icons.close, size: 18, color: Colors.grey), onPressed: () { attendanceController.searchQuery.value = ''; }, ) : null, hintText: 'Search by name', filled: true, fillColor: Colors.white, border: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: BorderSide(color: Colors.grey.shade300), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: BorderSide(color: Colors.grey.shade300), ), ), ); }), ), ), MySpacing.width(8), Container( height: 35, width: 35, decoration: BoxDecoration( color: Colors.white, border: Border.all(color: Colors.grey.shade300), borderRadius: BorderRadius.circular(5), ), child: IconButton( padding: EdgeInsets.zero, constraints: BoxConstraints(), icon: const Icon(Icons.tune, size: 20, color: Colors.black87), onPressed: () async { final result = await showModalBottomSheet>( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(5)), ), builder: (context) => AttendanceFilterBottomSheet( controller: attendanceController, permissionController: permissionController, selectedTab: _tabs[_tabController.index]['value']!, ), ); if (result != null) { final selectedProjectId = projectController.selectedProjectId.value; final selectedOrgId = result['selectedOrganization'] as String?; if (selectedOrgId != null) { attendanceController.selectedOrganization = attendanceController.organizations .firstWhere((o) => o.id == selectedOrgId); } if (selectedProjectId.isNotEmpty) { await _fetchTabData(attendanceController.selectedTab); } } }, ), ), ], ), ); } Widget _buildNoProjectWidget() { return Center( child: Padding( padding: const EdgeInsets.all(24.0), child: MyText.titleMedium( 'No Records Found', fontWeight: 600, color: Colors.grey[600], ), ), ); } Widget _buildTabBarView() { return TabBarView( controller: _tabController, children: _tabs.map((tab) { switch (tab['value']) { case 'attendanceLogs': return AttendanceLogsTab(controller: attendanceController); case 'regularizationRequests': return RegularizationRequestsTab(controller: attendanceController); case 'todaysAttendance': default: return TodaysAttendanceTab(controller: attendanceController); } }).toList(), ); } @override Widget build(BuildContext context) { final Color appBarColor = contentTheme.primary; if (!_tabsInitialized) { return Scaffold( appBar: CustomAppBar( title: "Attendance", backgroundColor: appBarColor, onBackPressed: () => Get.toNamed('/dashboard'), ), body: const Center(child: CircularProgressIndicator()), ); } return Scaffold( appBar: CustomAppBar( title: "Attendance", backgroundColor: appBarColor, onBackPressed: () => Get.toNamed('/dashboard'), ), body: Stack( children: [ Container( height: 80, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ appBarColor, appBarColor.withOpacity(0.0), ], ), ), ), SafeArea( child: GetBuilder( init: attendanceController, tag: 'attendance_dashboard_controller', builder: (controller) { final selectedProjectId = projectController.selectedProjectId.value; final noProjectSelected = selectedProjectId.isEmpty; return MyRefreshIndicator( onRefresh: _refreshData, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), padding: MySpacing.zero, child: Column( children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: PillTabBar( controller: _tabController, tabs: _tabs.map((e) => e['label']!).toList(), selectedColor: contentTheme.primary, unselectedColor: Colors.grey.shade600, indicatorColor: contentTheme.primary, onTap: (index) async { final selectedTab = _tabs[index]['value']!; attendanceController.selectedTab = selectedTab; await _fetchTabData(selectedTab); }, ), ), _buildFilterSearchRow(), MyFlex( children: [ MyFlexItem( sizes: 'lg-12 md-12 sm-12', child: noProjectSelected ? _buildNoProjectWidget() : SizedBox( height: MediaQuery.of(context).size.height - 200, child: _buildTabBarView(), ), ), ], ), ], ), ), ); }, ), ), ], ), ); } @override void dispose() { _tabController.dispose(); if (Get.isRegistered()) { Get.delete(); } super.dispose(); } }