From 13f07a237c6ab19b7863b343dfdfd058ee3595ca Mon Sep 17 00:00:00 2001 From: "J. A. Messias" Date: Mon, 9 Dec 2024 16:24:27 -0300 Subject: [PATCH] WIP --- .../history/data/data_sources/index.dart | 0 lib/features/history/data/index.dart | 3 + lib/features/history/data/models/index.dart | 0 .../history/data/repositories/index.dart | 0 .../history/domain/entities/index.dart | 0 lib/features/history/domain/index.dart | 3 + .../history/domain/respositories/index.dart | 0 .../history/domain/usecases/index.dart | 0 lib/features/history/index.dart | 3 + .../blocs/history_bloc_template.dart | 117 +++++++++ .../history/presentation/blocs/index.dart | 2 + .../blocs/provisional_history_bloc.dart | 0 lib/features/history/presentation/index.dart | 3 + .../pages/access_history_page.dart | 106 ++++++++ .../pages/history_page_template.dart | 211 ++++++++++++++++ .../history/presentation/pages/index.dart | 2 + .../pages/provisional_history_page.dart | 105 ++++++++ .../presentation/widgets/filter_modal.dart | 229 ++++++++++++++++++ .../history/presentation/widgets/index.dart | 1 + 19 files changed, 785 insertions(+) create mode 100644 lib/features/history/data/data_sources/index.dart create mode 100644 lib/features/history/data/index.dart create mode 100644 lib/features/history/data/models/index.dart create mode 100644 lib/features/history/data/repositories/index.dart create mode 100644 lib/features/history/domain/entities/index.dart create mode 100644 lib/features/history/domain/index.dart create mode 100644 lib/features/history/domain/respositories/index.dart create mode 100644 lib/features/history/domain/usecases/index.dart create mode 100644 lib/features/history/index.dart create mode 100644 lib/features/history/presentation/blocs/history_bloc_template.dart create mode 100644 lib/features/history/presentation/blocs/index.dart create mode 100644 lib/features/history/presentation/blocs/provisional_history_bloc.dart create mode 100644 lib/features/history/presentation/index.dart create mode 100644 lib/features/history/presentation/pages/access_history_page.dart create mode 100644 lib/features/history/presentation/pages/history_page_template.dart create mode 100644 lib/features/history/presentation/pages/index.dart create mode 100644 lib/features/history/presentation/pages/provisional_history_page.dart create mode 100644 lib/features/history/presentation/widgets/filter_modal.dart create mode 100644 lib/features/history/presentation/widgets/index.dart diff --git a/lib/features/history/data/data_sources/index.dart b/lib/features/history/data/data_sources/index.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/features/history/data/index.dart b/lib/features/history/data/index.dart new file mode 100644 index 00000000..186dfac9 --- /dev/null +++ b/lib/features/history/data/index.dart @@ -0,0 +1,3 @@ +export 'data_sources/index.dart'; +export 'repositories/index.dart'; +export 'models/index.dart'; diff --git a/lib/features/history/data/models/index.dart b/lib/features/history/data/models/index.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/features/history/data/repositories/index.dart b/lib/features/history/data/repositories/index.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/features/history/domain/entities/index.dart b/lib/features/history/domain/entities/index.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/features/history/domain/index.dart b/lib/features/history/domain/index.dart new file mode 100644 index 00000000..00b76d59 --- /dev/null +++ b/lib/features/history/domain/index.dart @@ -0,0 +1,3 @@ +export 'entities/index.dart'; +export 'respositories/index.dart'; +export 'usecases/index.dart'; diff --git a/lib/features/history/domain/respositories/index.dart b/lib/features/history/domain/respositories/index.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/features/history/domain/usecases/index.dart b/lib/features/history/domain/usecases/index.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/features/history/index.dart b/lib/features/history/index.dart new file mode 100644 index 00000000..aa560630 --- /dev/null +++ b/lib/features/history/index.dart @@ -0,0 +1,3 @@ +export 'data/index.dart'; +export 'domain/index.dart'; +export 'presentation/index.dart'; diff --git a/lib/features/history/presentation/blocs/history_bloc_template.dart b/lib/features/history/presentation/blocs/history_bloc_template.dart new file mode 100644 index 00000000..60dfe00f --- /dev/null +++ b/lib/features/history/presentation/blocs/history_bloc_template.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:hub/backend/api_requests/api_manager.dart'; +import 'package:hub/flutter_flow/nav/nav.dart'; +import 'package:hub/shared/utils/dialog_util.dart'; +import 'package:hub/shared/utils/log_util.dart'; + +class HistoryBloc extends Bloc { + final ScrollController scrollController = ScrollController(); + final Future Function(int pageSize, int pageNumber, Map opt) fetchHistoryService; + final bool Function(dynamic item, Map opt)? filterLogic; + + HistoryBloc(Map opt, this.fetchHistoryService, {this.filterLogic}) : super(HistoryState(opt: opt)) { + scrollController.addListener(_onScroll); + on(_onUpdateHistory); + on(_onFetchHistory); + add(FetchHistoryEvent()); + } + + void _onScroll() { + if (scrollController.position.atEdge && scrollController.position.pixels != 0) { + add(FetchHistoryEvent()); + } + } + + Future _onUpdateHistory(UpdateHistoryEvent event, Emitter emit) async { + emit(state.copyWith(opt: event.newOpt, historyWrap: [], pageNumber: 1)); + add(FetchHistoryEvent()); + } + + Future _onFetchHistory(FetchHistoryEvent event, Emitter emit) async { + if (state.loading) return; + emit(state.copyWith(loading: true)); + try { + var response = await fetchHistoryService(state.pageSize, state.pageNumber, state.opt); + + final List history = response.jsonBody['acessos'] ?? []; + List filteredHistory = history.where((item) { + if (filterLogic != null) { + return filterLogic!(item, state.opt); + } else { + return true; // Default to no filtering if no logic is provided + } + }).toList(); + + if (filteredHistory.isNotEmpty) { + emit(state.copyWith( + historyWrap: [...state.historyWrap, ...filteredHistory], + hasData: true, + loading: false, + pageNumber: state.pageNumber + 1, + )); + } else { + emit(state.copyWith(hasData: false, loading: false)); + } + } catch (e, s) { + await DialogUtil.errorDefault(navigatorKey.currentContext!); + LogUtil.requestAPIFailed('processRequest', "", "Fetch History", e, s); + emit(state.copyWith(hasData: false, loading: false, error: e.toString())); + } + } + + @override +Future close() { + scrollController.dispose(); + return super.close(); + } +} + +class HistoryState { + final Map opt; + final List historyWrap; + final bool hasData; + final bool loading; + final int pageNumber; + final int pageSize; + final String? error; + + HistoryState({ + required this.opt, + this.historyWrap = const [], + this.hasData = false, + this.loading = false, + this.pageNumber = 1, + this.pageSize = 10, + this.error, + }); + + HistoryState copyWith({ + Map? opt, + List? historyWrap, + bool? hasData, + bool? loading, + int? pageNumber, + int? pageSize, + String? error, + }) { + return HistoryState( + opt: opt ?? this.opt, + historyWrap: historyWrap ?? this.historyWrap, + hasData: hasData ?? this.hasData, + loading: loading ?? this.loading, + pageNumber: pageNumber ?? this.pageNumber, + pageSize: pageSize ?? this.pageSize, + error: error ?? this.error, + ); + } +} + +abstract class HistoryEvent {} + +class UpdateHistoryEvent extends HistoryEvent { + final Map newOpt; + UpdateHistoryEvent(this.newOpt); +} + +class FetchHistoryEvent extends HistoryEvent {} \ No newline at end of file diff --git a/lib/features/history/presentation/blocs/index.dart b/lib/features/history/presentation/blocs/index.dart new file mode 100644 index 00000000..83ccc77f --- /dev/null +++ b/lib/features/history/presentation/blocs/index.dart @@ -0,0 +1,2 @@ +export 'history_bloc_template.dart'; +export 'provisional_history_bloc.dart'; diff --git a/lib/features/history/presentation/blocs/provisional_history_bloc.dart b/lib/features/history/presentation/blocs/provisional_history_bloc.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/features/history/presentation/index.dart b/lib/features/history/presentation/index.dart new file mode 100644 index 00000000..48b6447a --- /dev/null +++ b/lib/features/history/presentation/index.dart @@ -0,0 +1,3 @@ +export 'blocs/index.dart'; +export 'pages/index.dart'; +export 'widgets/index.dart'; diff --git a/lib/features/history/presentation/pages/access_history_page.dart b/lib/features/history/presentation/pages/access_history_page.dart new file mode 100644 index 00000000..ac031632 --- /dev/null +++ b/lib/features/history/presentation/pages/access_history_page.dart @@ -0,0 +1,106 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:hub/backend/api_requests/api_calls.dart'; +import 'package:hub/components/molecular_components/message_opt_modal/opt_modal_widget.dart'; +import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart'; +import 'package:hub/features/history/index.dart'; +import 'package:hub/flutter_flow/flutter_flow_theme.dart'; +import 'package:hub/flutter_flow/internationalization.dart'; +import 'package:hub/shared/helpers/storage/base_storage.dart'; +import 'package:hub/shared/helpers/storage/storage_helper.dart'; + +class AcessCubit extends Cubit { + AcessCubit() : super('Provisional'); + + Future fetchCliUUID() async { + final cliUUID = await StorageHelper().get(KeychainStorageKey.clientUUID.value); + emit(cliUUID); + } + + Future fetchHistoryService(int pageSize, int pageNumber, Map opt) async { + return await PhpGroup.getAccessCall.call( + pageSize: pageSize.toString(), + pageNumber: pageNumber.toString(), + pesTipo: opt['personType'] != 'E' && opt['personType'] != 'O' ? 'T' : opt['personType'], + ); + } + + bool filterLogic(dynamic item, Map opt) { + final personTypeMatches = opt['personType'] == '.*' || item["PES_TIPO"].toString() == opt['personType']; + return personTypeMatches; + } +} + +class AccessHistoryPage extends StatelessWidget { + final Map opt = { + 'personType': '.*', + 'accessType': '.*', + 'search': '.*', + }; + + final String appBarTitle = 'Histórico Provisório'; + final String noHistoryMessage = 'Nenhum histórico encontrado!'; + final String errorMessage = 'Falha ao realizar operação!'; + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => AcessCubit()..fetchCliUUID(), + child: HistoryPageTemplate( + opt: opt, + fetchHistoryService: (pageSize, pageNumber, opt) => context.read().fetchHistoryService(pageSize, pageNumber, opt), + cardBuilder: cardBuilder, + appBarTitle: appBarTitle, + noHistoryMessage: noHistoryMessage, + errorMessage: errorMessage, + isLeading: true, + isFilter: true, + filterLogic: (item, opt) => context.read().filterLogic(item, opt), + ), + ); + } + + Widget cardBuilder(BuildContext context, dynamic historyItem) { + final cliUUID = context.watch().state; + final String imagePath = 'https://freaccess.com.br/freaccess/getImage.php?cliID=${cliUUID}&atividade=getFoto&Documento=${historyItem['PES_ID'] ?? ''}&tipo=${historyItem['PES_TIPO'] ?? ''}'; + final Map labelsHashMap = { + FFLocalizations.of(context).getVariableText(ptText: 'Nome:', enText: 'Name:'): historyItem['PES_NOME'] ?? '', + FFLocalizations.of(context).getVariableText(ptText: 'Acesso:', enText: 'Access:'): historyItem['ACE_DATAHORA'] ?? '', + FFLocalizations.of(context).getVariableText(ptText: 'Setor', enText: 'Sector'): historyItem['SET_DESCRICAO'] ?? '', + }; + + final statusHashMap = [ + _getPersonTypeStatus(context, historyItem['PES_TIPO']), + _getAccessTypeStatus(context, historyItem['ACE_TIPO']), + ]; + + return CardItemTemplateComponentWidget( + imagePath: imagePath, + labelsHashMap: labelsHashMap, + statusHashMap: statusHashMap, + onTapCardItemAction: () async {}, + ); + } + + Map _getPersonTypeStatus(BuildContext context, String? personType) { + switch (personType) { + case 'O': + return {FFLocalizations.of(context).getVariableText(ptText: 'Morador', enText: 'Resident'): FlutterFlowTheme.of(context).alternate2}; + case 'E': + return {FFLocalizations.of(context).getVariableText(ptText: 'Visitante', enText: 'Visitor'): FlutterFlowTheme.of(context).alternate2}; + default: + return {FFLocalizations.of(context).getVariableText(ptText: 'Desconhecido', enText: 'Unknown'): FlutterFlowTheme.of(context).alternate2}; + } + } + + Map _getAccessTypeStatus(BuildContext context, String? accessType) { + switch (accessType) { + case '0': + return {FFLocalizations.of(context).getVariableText(ptText: 'Entrada', enText: 'Entrance'): FlutterFlowTheme.of(context).success}; + case '1': + return {FFLocalizations.of(context).getVariableText(ptText: 'Saída', enText: 'Exit'): FlutterFlowTheme.of(context).error}; + default: + return {FFLocalizations.of(context).getVariableText(ptText: 'Desconhecido', enText: 'Unknown'): FlutterFlowTheme.of(context).warning}; + } + } +} \ No newline at end of file diff --git a/lib/features/history/presentation/pages/history_page_template.dart b/lib/features/history/presentation/pages/history_page_template.dart new file mode 100644 index 00000000..884d6bc9 --- /dev/null +++ b/lib/features/history/presentation/pages/history_page_template.dart @@ -0,0 +1,211 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:hub/features/history/index.dart'; +import 'package:hub/flutter_flow/internationalization.dart'; +import 'package:hub/flutter_flow/nav/nav.dart'; +import 'package:hub/backend/api_requests/api_manager.dart'; +import 'package:hub/features/history/presentation/widgets/filter_modal.dart'; +import 'package:hub/flutter_flow/flutter_flow_theme.dart'; +import 'package:hub/shared/utils/dialog_util.dart'; +import 'package:hub/shared/utils/log_util.dart'; +import 'package:hub/flutter_flow/flutter_flow_icon_button.dart'; + +class HistoryPageTemplate extends StatelessWidget { + final Map opt; + final Future Function(int pageSize, int pageNumber, Map opt) fetchHistoryService; + final Widget Function(BuildContext context, dynamic historyItem) cardBuilder; + final String appBarTitle; + final String noHistoryMessage; + final String errorMessage; + final bool isLeading; + final bool isFilter; + final bool Function(dynamic item, Map opt)? filterLogic; + + const HistoryPageTemplate({ + Key? key, + required this.opt, + required this.fetchHistoryService, + required this.cardBuilder, + this.appBarTitle = 'History', + this.noHistoryMessage = 'No history found!', + this.errorMessage = 'Failed to perform operation!', + this.isLeading = true, + this.isFilter = false, + this.filterLogic, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => HistoryBloc(opt, fetchHistoryService, filterLogic: filterLogic), + child: HistoryPageView( + cardBuilder: cardBuilder, + appBarTitle: appBarTitle, + noHistoryMessage: noHistoryMessage, + errorMessage: errorMessage, + isLeading: isLeading, + isFilter: isFilter, + ), + ); + } +} + +class HistoryPageView extends StatelessWidget { + final Widget Function(BuildContext context, dynamic historyItem) cardBuilder; + final String appBarTitle; + final String noHistoryMessage; + final String errorMessage; + final bool isLeading; + final bool isFilter; + + const HistoryPageView({ + Key? key, + required this.cardBuilder, + required this.appBarTitle, + required this.noHistoryMessage, + required this.errorMessage, + this.isLeading = false, + this.isFilter = false, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = FlutterFlowTheme.of(context); + return Scaffold( + key: GlobalKey(), + backgroundColor: theme.primaryBackground, + appBar: _buildAppBar(context, theme), + body: _buildBody(context), + ); + } + + PreferredSizeWidget _buildAppBar(BuildContext context, FlutterFlowTheme theme) { + return AppBar( + backgroundColor: theme.primaryBackground, + automaticallyImplyLeading: false, + leading: isLeading ? _buildBackButton(context, theme) : null, + title: _buildTitle(context, theme), + centerTitle: true, + elevation: 0.0, + actions: [ + if (isFilter) _buildFilterButton(context), + ], + ); + } + + Widget _buildBackButton(BuildContext context, FlutterFlowTheme theme) { + return FlutterFlowIconButton( + borderColor: Colors.transparent, + borderRadius: 30.0, + borderWidth: 1.0, + buttonSize: 60.0, + icon: Icon( + Icons.keyboard_arrow_left, + color: theme.primaryText, + size: 30.0, + ), + onPressed: () => Navigator.of(context).pop(), + ); + } + + Widget _buildTitle(BuildContext context, FlutterFlowTheme theme) { + return Text( + appBarTitle, + style: theme.headlineMedium.override( + fontFamily: theme.headlineMediumFamily, + color: theme.primaryText, + fontSize: 16.0, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap().containsKey(theme.headlineMediumFamily), + ), + ); + } + + Widget _buildFilterButton(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 10, 0), + child: IconButton( + icon: const Icon(Icons.filter_list), + onPressed: () async { + final Map? selectedFilter = await showModalBottomSheet>( + isScrollControlled: true, + backgroundColor: Colors.transparent, + context: context, + builder: (BuildContext bottomSheetContext) { + return GestureDetector( + onTap: () => Navigator.of(bottomSheetContext).pop(), + child: Container( + color: Colors.transparent, + child: GestureDetector( + onTap: () {}, + child: FilterModalWidget( + defaultSelections: { + 'personType': context.read().state.opt['personType'] == '.*' ? ['E', 'O'] : [context.read().state.opt['personType']], + 'search': '.*', + }, + filterOptions: { + 'personType': [ + {'title': FFLocalizations.of(context).getVariableText(ptText: 'Visitante', enText: 'Visitor'), 'value': 'E',}, + {'title': FFLocalizations.of(context).getVariableText(ptText: 'Morador', enText: 'Resident'), 'value': 'O',}, + + ], + }, + filterTitles: { + 'personType': 'Person Type', + }, + ), + ), + ), + ); + }, + ); + + if (selectedFilter != null) { + context.read().add(UpdateHistoryEvent(selectedFilter)); + } + }, + ), + ), + ], + ); + } + + Widget _buildBody(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state.loading && state.historyWrap.isEmpty) { + return Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + FlutterFlowTheme.of(context).primary, + ), + ), + ); + } else if (state.error != null) { + return Center( + child: Text(errorMessage), + ); + } else if (state.historyWrap.isEmpty) { + return Center( + child: Text(noHistoryMessage), + ); + } else { + return ListView.builder( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + controller: context.read().scrollController, + itemCount: state.historyWrap.length, + itemBuilder: (context, index) { + final historyItem = state.historyWrap[index]; + return cardBuilder(context, historyItem); + }, + ); + } + }, + ); + } +} \ No newline at end of file diff --git a/lib/features/history/presentation/pages/index.dart b/lib/features/history/presentation/pages/index.dart new file mode 100644 index 00000000..5b174ec8 --- /dev/null +++ b/lib/features/history/presentation/pages/index.dart @@ -0,0 +1,2 @@ +export 'history_page_template.dart'; +export 'provisional_history_page.dart'; diff --git a/lib/features/history/presentation/pages/provisional_history_page.dart b/lib/features/history/presentation/pages/provisional_history_page.dart new file mode 100644 index 00000000..9df73420 --- /dev/null +++ b/lib/features/history/presentation/pages/provisional_history_page.dart @@ -0,0 +1,105 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:hub/backend/api_requests/api_calls.dart'; +import 'package:hub/components/molecular_components/message_opt_modal/opt_modal_widget.dart'; +import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart'; +import 'package:hub/features/history/index.dart'; +import 'package:hub/flutter_flow/flutter_flow_theme.dart'; +import 'package:hub/flutter_flow/internationalization.dart'; +import 'package:hub/shared/helpers/storage/base_storage.dart'; +import 'package:hub/shared/helpers/storage/storage_helper.dart'; + +class ProvisionalCubit extends Cubit { + ProvisionalCubit() : super('Provisional'); + + Future fetchCliUUID() async { + final cliUUID = await StorageHelper().get(KeychainStorageKey.clientUUID.value); + emit(cliUUID); + } + + Future fetchHistoryService(int pageSize, int pageNumber, Map status) async { + return await PhpGroup.getProvSchedules(pageNumber.toString(), ''); + } + + bool filterLogic(dynamic item, Map opt) { + final personTypeMatches = opt['personType'] == '.*' || item["PES_TIPO"].toString() == opt['personType']; + return personTypeMatches; + } +} + +class ProvisionalHistoryPage extends StatelessWidget { + final Map opt = { + 'personType': '.*', + 'accessType': '.*', + 'search': '.*', + }; + + final String appBarTitle = 'Histórico Provisório'; + final String noHistoryMessage = 'Nenhum histórico encontrado!'; + final String errorMessage = 'Falha ao realizar operação!'; + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => ProvisionalCubit()..fetchCliUUID(), + child: HistoryPageTemplate( + opt: opt, + fetchHistoryService: (pageSize, pageNumber, opt) => context.read().fetchHistoryService(pageSize, pageNumber, opt), + cardBuilder: cardBuilder, + appBarTitle: appBarTitle, + noHistoryMessage: noHistoryMessage, + errorMessage: errorMessage, + isLeading: true, + isFilter: false, + filterLogic: (item, opt) => context.read().filterLogic(item, opt), + ), + ); + } + + Widget cardBuilder(BuildContext context, dynamic historyItem) { + log('historyItem: $historyItem'); + final cliUUID = context.watch().state; + final String imagePath = 'https://freaccess.com.br/freaccess/getImage.php?cliID=${cliUUID}&atividade=getFoto&Documento=${historyItem['PES_ID'] ?? ''}&tipo=${historyItem['PES_TIPO'] ?? ''}'; + final Map labelsHashMap = { + FFLocalizations.of(context).getVariableText(ptText: 'Nome:', enText: 'Name:'): historyItem['PES_NOME'] ?? '', + FFLocalizations.of(context).getVariableText(ptText: 'Acesso:', enText: 'Access:'): historyItem['ACE_DATAHORA'] ?? '', + FFLocalizations.of(context).getVariableText(ptText: 'Setor', enText: 'Sector'): historyItem['SET_DESCRICAO'] ?? '', + }; + + final statusHashMap = [ + _getPersonTypeStatus(context, historyItem['PES_TIPO']), + _getAccessTypeStatus(context, historyItem['ACE_TIPO']), + ]; + + return CardItemTemplateComponentWidget( + imagePath: imagePath, + labelsHashMap: labelsHashMap, + statusHashMap: statusHashMap, + onTapCardItemAction: () async {}, + ); + } + + Map _getPersonTypeStatus(BuildContext context, String? personType) { + switch (personType) { + case 'O': + return {FFLocalizations.of(context).getVariableText(ptText: 'Morador', enText: 'Resident'): FlutterFlowTheme.of(context).alternate2}; + case 'E': + return {FFLocalizations.of(context).getVariableText(ptText: 'Visitante', enText: 'Visitor'): FlutterFlowTheme.of(context).alternate2}; + default: + return {FFLocalizations.of(context).getVariableText(ptText: 'Desconhecido', enText: 'Unknown'): FlutterFlowTheme.of(context).alternate2}; + } + } + + Map _getAccessTypeStatus(BuildContext context, String? accessType) { + switch (accessType) { + case '0': + return {FFLocalizations.of(context).getVariableText(ptText: 'Entrada', enText: 'Entrance'): FlutterFlowTheme.of(context).success}; + case '1': + return {FFLocalizations.of(context).getVariableText(ptText: 'Saída', enText: 'Exit'): FlutterFlowTheme.of(context).error}; + default: + return {FFLocalizations.of(context).getVariableText(ptText: 'Desconhecido', enText: 'Unknown'): FlutterFlowTheme.of(context).warning}; + } + } +} \ No newline at end of file diff --git a/lib/features/history/presentation/widgets/filter_modal.dart b/lib/features/history/presentation/widgets/filter_modal.dart new file mode 100644 index 00000000..c44a545b --- /dev/null +++ b/lib/features/history/presentation/widgets/filter_modal.dart @@ -0,0 +1,229 @@ +import 'package:flutter/material.dart'; + +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/form_field_controller.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:hub/flutter_flow/flutter_flow_theme.dart'; +import 'package:hub/flutter_flow/nav/nav.dart'; +import 'package:hub/shared/utils/limited_text_size.dart'; + + + +class FilterModal extends FlutterFlowModel { + FocusNode? textFieldFocusNode; + TextEditingController? textController; + String? Function(BuildContext, String?)? textControllerValidator; + bool? checkboxValue1; + bool? checkboxValue2; + FormFieldController>? checkboxGroupValueController; + List? get checkboxGroupValues => checkboxGroupValueController?.value; + set checkboxGroupValues(List? v) => checkboxGroupValueController?.value = v; + + @override + void initState(BuildContext context) {} + + @override + void dispose() { + textFieldFocusNode?.dispose(); + textController?.dispose(); + } +} + +class FilterModalWidget extends StatefulWidget { + final Map defaultSelections; + final Map>> filterOptions; + final Map filterTitles; + + const FilterModalWidget({ + super.key, + required this.defaultSelections, + required this.filterOptions, + required this.filterTitles, + }); + + @override + _FilterModalWidgetState createState() => _FilterModalWidgetState(); +} + +class _FilterModalWidgetState extends State { + late FilterModal _model; + late Map selected; + + @override + void setState(VoidCallback callback) { + super.setState(callback); + _model.onUpdate(); + } + + @override + void initState() { + super.initState(); + + _model = createModel(context, () => FilterModal()); + + _model.textController ??= TextEditingController(); + _model.textFieldFocusNode ??= FocusNode(); + + selected = widget.defaultSelections; + } + + void _applyFilter() { + Map filterResult = { + 'search': _model.textController?.text == '' ? '.*' : _model.textController!.text.toLowerCase(), + }; + + widget.filterOptions.forEach((key, options) { + filterResult[key] = selected[key]!.isEmpty || selected[key]!.length > 1 + ? '.*' + : selected[key]!.first; + }); + + context.pop(filterResult); + } + + Widget _buildCheckboxListTile(String key, List> options, double fontsize) { + double limitedInputFontSize = LimitedFontSizeUtil.getInputFontSize(context); + return Column( + children: [ + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsetsDirectional.fromSTEB(0.0, 3.0, 0.0, 0.0), + child: Text( + widget.filterTitles[key]!, + textAlign: TextAlign.left, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily, + fontSize: limitedInputFontSize, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap().containsKey(FlutterFlowTheme.of(context).bodyMediumFamily), + color: FlutterFlowTheme.of(context).primaryText, + ), + ), + ), + ], + ), + ListView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: options.length, + itemBuilder: (context, index) { + final option = options[index]; + return CheckboxListTile( + title: Text( + option['title']!, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily, + letterSpacing: 0.0, + fontSize: limitedInputFontSize, + useGoogleFonts: GoogleFonts.asMap().containsKey(FlutterFlowTheme.of(context).bodyMediumFamily), + color: FlutterFlowTheme.of(context).primaryText, + ), + ), + dense: true, + value: selected[key]!.contains(option['value']), + onChanged: (bool? value) { + setState(() { + if (value == true) { + if (!selected[key]!.contains(option['value'])) { + selected[key]!.add(option['value']); + } + } else { + selected[key]!.remove(option['value']); + } + }); + }, + activeColor: FlutterFlowTheme.of(context).primary, + checkColor: FlutterFlowTheme.of(context).info, + checkboxShape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(100), + ), + enableFeedback: true, + side: BorderSide( + width: 5, + color: FlutterFlowTheme.of(context).secondaryText, + ), + controlAffinity: ListTileControlAffinity.leading, + ); + }, + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + double screenWidth = MediaQuery.of(context).size.width; + + return Center( + child: Container( + width: screenWidth - (screenWidth * 0.35), + height: 250, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context).primaryBackground, + borderRadius: BorderRadius.circular(24.0), + ), + child: Padding( + padding: const EdgeInsets.all(4.0), + child: Column( + children: [ + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsetsDirectional.fromSTEB(10.0, 10.0, 0.0, 10.0), + child: Text( + FFLocalizations.of(context).getText('yfj9pd6k'), + style: FlutterFlowTheme.of(context).headlineMedium.override( + fontFamily: FlutterFlowTheme.of(context).headlineMediumFamily, + color: FlutterFlowTheme.of(context).primaryText, + fontSize: LimitedFontSizeUtil.getHeaderFontSize(context), + letterSpacing: 0.0, + fontWeight: FontWeight.bold, + useGoogleFonts: + GoogleFonts.asMap().containsKey(FlutterFlowTheme.of(context).headlineMediumFamily), + ), + ), + ), + ], + ), + Expanded( + child: SingleChildScrollView( + child: Container( + padding: const EdgeInsets.all(10), + child: Column( + mainAxisSize: MainAxisSize.min, + children: widget.filterOptions.keys.map((key) { + return _buildCheckboxListTile(key, widget.filterOptions[key]!, 14); + }).toList(), + ), + ), + ), + ), + ElevatedButton( + onPressed: _applyFilter, + style: ElevatedButton.styleFrom( + foregroundColor: FlutterFlowTheme.of(context).info, + backgroundColor: FlutterFlowTheme.of(context).primary, + ), + child: Text(FFLocalizations.of(context).getText('88kshkph'), + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily, + color: FlutterFlowTheme.of(context).info, + fontSize: LimitedFontSizeUtil.getInputFontSize(context), + letterSpacing: 0.0, + fontWeight: FontWeight.bold, + useGoogleFonts: + GoogleFonts.asMap().containsKey(FlutterFlowTheme.of(context).bodyMediumFamily), + )), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/features/history/presentation/widgets/index.dart b/lib/features/history/presentation/widgets/index.dart new file mode 100644 index 00000000..2191ab0c --- /dev/null +++ b/lib/features/history/presentation/widgets/index.dart @@ -0,0 +1 @@ +export 'filter_modal.dart';