import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/access_management/bloc/access_event.dart'; import 'package:syncrow_web/pages/access_management/bloc/access_state.dart'; import 'package:syncrow_web/pages/access_management/booking_system/presentation/model/password_model.dart'; import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; import 'package:syncrow_web/pages/common/hour_picker_dialog.dart'; import 'package:syncrow_web/services/access_mang_api.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/app_enum.dart'; import 'package:syncrow_web/utils/snack_bar.dart'; class AccessBloc extends Bloc { AccessBloc() : super((AccessInitial())) { on(_onFetchTableData); on(selectTime); on(_filterData); on(resetSearch); on(onTabChanged); } String startTime = 'Start Date'; String endTime = 'End Date'; int? effectiveTimeTimeStamp; int? expirationTimeTimeStamp; TextEditingController passwordName = TextEditingController(); TextEditingController emailAuthorizer = TextEditingController(); List filteredData = []; List data = []; Future _onFetchTableData( FetchTableData event, Emitter emit) async { try { final projectUuid = await ProjectManager.getProjectUUID() ?? ''; emit(AccessLoaded()); data = await AccessMangApi().fetchVisitorPassword(projectUuid); filteredData = data; updateTabsCount(); emit(TableLoaded(data)); } catch (e) { emit(FailedState(e.toString())); } } void updateTabsCount() { int toBeEffectiveCount = data .where((item) => item.passwordStatus.value == 'To be effective') .length; int effectiveCount = data.where((item) => item.passwordStatus.value == 'Effective').length; int expiredCount = data.where((item) => item.passwordStatus.value == 'Expired').length; tabs[1] = 'To Be Effective ($toBeEffectiveCount)'; tabs[2] = 'Effective ($effectiveCount)'; tabs[3] = 'Expired ($expiredCount)'; } int selectedIndex = 0; final List tabs = [ 'All', 'To Be Effective (0)', 'Effective (0)', 'Expired' ]; Future selectFilterTap( TabChangedEvent event, Emitter emit) async { try { emit(AccessLoaded()); selectedIndex = event.selectedIndex; emit(AccessInitial()); emit(TableLoaded(data)); } catch (e) { emit(FailedState(e.toString())); return; } } Future selectTime( SelectTime event, Emitter emit, ) async { emit(AccessLoaded()); final DateTime? picked = await showDatePicker( context: event.context, initialDate: DateTime.now(), firstDate: DateTime.now().add(const Duration(days: -5095)), lastDate: DateTime.now().add(const Duration(days: 2095)), builder: (BuildContext context, Widget? child) { return Theme( data: ThemeData.light().copyWith( colorScheme: ColorScheme.light( primary: ColorsManager.blackColor, onPrimary: Colors.white, onSurface: ColorsManager.grayColor, ), textButtonTheme: TextButtonThemeData( style: TextButton.styleFrom( foregroundColor: Colors.blue, ), ), ), child: child!, ); }, ); if (picked != null) { final TimeOfDay? timePicked = await showHourPicker( context: event.context, initialTime: TimeOfDay.now(), ); if (timePicked != null) { final DateTime selectedDateTime = DateTime( picked.year, picked.month, picked.day, timePicked.hour, timePicked.minute, ); final int selectedTimestamp = selectedDateTime.millisecondsSinceEpoch ~/ 1000; if (event.isStart) { if (expirationTimeTimeStamp != null && selectedTimestamp > expirationTimeTimeStamp!) { CustomSnackBar.displaySnackBar( 'Effective Time cannot be later than Expiration Time.'); } else { startTime = selectedDateTime.toString().split('.').first; effectiveTimeTimeStamp = selectedTimestamp; } } else { if (effectiveTimeTimeStamp != null && selectedTimestamp < effectiveTimeTimeStamp!) { CustomSnackBar.displaySnackBar( 'Expiration Time cannot be earlier than Effective Time.'); } else { endTime = selectedDateTime.toString().split('.').first; expirationTimeTimeStamp = selectedTimestamp; } } } } emit(ChangeTimeState()); } Future _filterData( FilterDataEvent event, Emitter emit) async { emit(AccessLoaded()); try { // Convert search text to lower case for case-insensitive search final searchText = event.passwordName?.toLowerCase() ?? ''; final searchEmailText = event.emailAuthorizer?.toLowerCase() ?? ''; filteredData = data.where((item) { bool matchesCriteria = true; // Convert timestamp to DateTime and extract date component DateTime effectiveDate = DateTime.fromMillisecondsSinceEpoch( int.parse(item.effectiveTime.toString()) * 1000) .toUtc() .toLocal(); DateTime invalidDate = DateTime.fromMillisecondsSinceEpoch( int.parse(item.invalidTime.toString()) * 1000) .toUtc() .toLocal(); DateTime effectiveDateAndTime = DateTime( effectiveDate.year, effectiveDate.month, effectiveDate.day, effectiveDate.hour, effectiveDate.minute); DateTime invalidDateAndTime = DateTime( invalidDate.year, invalidDate.month, invalidDate.day, invalidDate.hour, invalidDate.minute); // Filter by password name, making the search case-insensitive if (searchText.isNotEmpty) { final bool matchesName = item.passwordName.toString().toLowerCase().contains(searchText); if (!matchesName) { matchesCriteria = false; } } if (searchEmailText.isNotEmpty) { final bool matchesName = item.authorizerEmail .toString() .toLowerCase() .contains(searchEmailText); if (!matchesName) { matchesCriteria = false; } } // Filter by start date only if (event.startTime != null && event.endTime == null) { DateTime startDateTime = DateTime.fromMillisecondsSinceEpoch(event.startTime! * 1000) .toUtc() .toLocal(); startDateTime = DateTime(startDateTime.year, startDateTime.month, startDateTime.day, startDateTime.hour, startDateTime.minute); if (effectiveDateAndTime.isBefore(startDateTime)) { matchesCriteria = false; } } // Filter by end date only if (event.endTime != null && event.startTime == null) { DateTime startDateTime = DateTime.fromMillisecondsSinceEpoch(event.endTime! * 1000) .toUtc() .toLocal(); startDateTime = DateTime(startDateTime.year, startDateTime.month, startDateTime.day, startDateTime.hour, startDateTime.minute); if (invalidDateAndTime.isAfter(startDateTime)) { matchesCriteria = false; } } // Filter by both start date and end date if (event.startTime != null && event.endTime != null) { DateTime startDateTime = DateTime.fromMillisecondsSinceEpoch(event.startTime! * 1000) .toUtc() .toLocal(); DateTime endDateTime = DateTime.fromMillisecondsSinceEpoch(event.endTime! * 1000) .toUtc() .toLocal(); startDateTime = DateTime(startDateTime.year, startDateTime.month, startDateTime.day, startDateTime.hour, startDateTime.minute); endDateTime = DateTime(endDateTime.year, endDateTime.month, endDateTime.day, endDateTime.hour, endDateTime.minute); if (effectiveDateAndTime.isBefore(startDateTime) || invalidDateAndTime.isAfter(endDateTime)) { matchesCriteria = false; } } // Filter by selected tab index if (event.selectedTabIndex == 1 && item.passwordStatus.value != 'To be effective') { matchesCriteria = false; } else if (event.selectedTabIndex == 2 && item.passwordStatus.value != 'Effective') { matchesCriteria = false; } else if (event.selectedTabIndex == 3 && item.passwordStatus.value != 'Expired') { matchesCriteria = false; } return matchesCriteria; }).toList(); emit(TableLoaded(filteredData)); } catch (e) { emit(FailedState(e.toString())); } } resetSearch(ResetSearch event, Emitter emit) async { emit(AccessLoaded()); startTime = 'Start Time'; endTime = 'End Time'; passwordName.clear(); emailAuthorizer.clear(); selectedIndex = 0; effectiveTimeTimeStamp = null; expirationTimeTimeStamp = null; filteredData = List.from(data); emit(TableLoaded(filteredData)); } String timestampToDate(dynamic timestamp) { DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(int.parse(timestamp) * 1000); return "${dateTime.year}/${dateTime.month.toString().padLeft(2, '0')}/${dateTime.day.toString().padLeft(2, '0')} " " ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}"; } Future onTabChanged( TabChangedEvent event, Emitter emit) async { try { emit(AccessLoaded()); selectedIndex = event.selectedIndex; switch (selectedIndex) { case 0: // All filteredData = data; break; case 1: // To Be Effective filteredData = data .where((item) => item.passwordStatus.value == "To Be Effective") .toList(); break; case 2: // Effective filteredData = data .where((item) => item.passwordStatus.value == "Effective") .toList(); break; case 3: // Expired filteredData = data .where((item) => item.passwordStatus.value == "Expired") .toList(); break; default: filteredData = data; } add(FilterDataEvent( selectedTabIndex: selectedIndex, passwordName: passwordName.text.toLowerCase(), emailAuthorizer: emailAuthorizer.text.toLowerCase(), startTime: effectiveTimeTimeStamp, endTime: expirationTimeTimeStamp)); emit(TableLoaded(filteredData)); } catch (e) { emit(FailedState(e.toString())); } } }