From d3c663af32f5e39a683bf12896da0312ae5eb88e Mon Sep 17 00:00:00 2001 From: "J. A. Messias" Date: Mon, 9 Dec 2024 09:09:35 -0300 Subject: [PATCH] WIP --- .../histories/data/data_sources/index.dart | 0 lib/features/histories/data/index.dart | 3 + lib/features/histories/data/models/index.dart | 0 .../histories/data/repositories/index.dart | 0 .../histories/domain/entities/index.dart | 0 lib/features/histories/domain/index.dart | 3 + .../histories/domain/respositories/index.dart | 0 .../histories/domain/usecases/index.dart | 0 .../histories/presentation/blocs/index.dart | 0 .../histories/presentation/index.dart | 3 + .../histories/presentation/pages/index.dart | 0 .../pages/prov_schedule_page.dart | 450 ++++++++++++++++++ .../histories/presentation/widgets/index.dart | 0 lib/flutter_flow/nav/nav.dart | 3 +- 14 files changed, 461 insertions(+), 1 deletion(-) create mode 100644 lib/features/histories/data/data_sources/index.dart create mode 100644 lib/features/histories/data/index.dart create mode 100644 lib/features/histories/data/models/index.dart create mode 100644 lib/features/histories/data/repositories/index.dart create mode 100644 lib/features/histories/domain/entities/index.dart create mode 100644 lib/features/histories/domain/index.dart create mode 100644 lib/features/histories/domain/respositories/index.dart create mode 100644 lib/features/histories/domain/usecases/index.dart create mode 100644 lib/features/histories/presentation/blocs/index.dart create mode 100644 lib/features/histories/presentation/index.dart create mode 100644 lib/features/histories/presentation/pages/index.dart create mode 100644 lib/features/histories/presentation/pages/prov_schedule_page.dart create mode 100644 lib/features/histories/presentation/widgets/index.dart diff --git a/lib/features/histories/data/data_sources/index.dart b/lib/features/histories/data/data_sources/index.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/features/histories/data/index.dart b/lib/features/histories/data/index.dart new file mode 100644 index 00000000..186dfac9 --- /dev/null +++ b/lib/features/histories/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/histories/data/models/index.dart b/lib/features/histories/data/models/index.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/features/histories/data/repositories/index.dart b/lib/features/histories/data/repositories/index.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/features/histories/domain/entities/index.dart b/lib/features/histories/domain/entities/index.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/features/histories/domain/index.dart b/lib/features/histories/domain/index.dart new file mode 100644 index 00000000..00b76d59 --- /dev/null +++ b/lib/features/histories/domain/index.dart @@ -0,0 +1,3 @@ +export 'entities/index.dart'; +export 'respositories/index.dart'; +export 'usecases/index.dart'; diff --git a/lib/features/histories/domain/respositories/index.dart b/lib/features/histories/domain/respositories/index.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/features/histories/domain/usecases/index.dart b/lib/features/histories/domain/usecases/index.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/features/histories/presentation/blocs/index.dart b/lib/features/histories/presentation/blocs/index.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/features/histories/presentation/index.dart b/lib/features/histories/presentation/index.dart new file mode 100644 index 00000000..48b6447a --- /dev/null +++ b/lib/features/histories/presentation/index.dart @@ -0,0 +1,3 @@ +export 'blocs/index.dart'; +export 'pages/index.dart'; +export 'widgets/index.dart'; diff --git a/lib/features/histories/presentation/pages/index.dart b/lib/features/histories/presentation/pages/index.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/features/histories/presentation/pages/prov_schedule_page.dart b/lib/features/histories/presentation/pages/prov_schedule_page.dart new file mode 100644 index 00000000..aef6cab5 --- /dev/null +++ b/lib/features/histories/presentation/pages/prov_schedule_page.dart @@ -0,0 +1,450 @@ +import 'package:flutter/material.dart'; +import 'package:hub/backend/api_requests/api_manager.dart'; +import 'package:hub/components/molecular_components/message_opt_modal/opt_modal_widget.dart'; +import 'package:hub/flutter_flow/flutter_flow_model.dart'; +import 'package:hub/flutter_flow/request_manager.dart'; +import 'package:hub/shared/helpers/storage/base_storage.dart'; +import 'package:hub/shared/helpers/storage/storage_helper.dart'; +import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:hub/backend/api_requests/api_calls.dart'; +import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart'; +import 'package:hub/flutter_flow/flutter_flow_icon_button.dart'; +import 'package:hub/flutter_flow/flutter_flow_theme.dart'; +import 'package:hub/flutter_flow/flutter_flow_util.dart'; +import 'package:hub/shared/utils/dialog_util.dart'; +import 'package:hub/shared/utils/limited_text_size.dart'; +import 'package:hub/shared/utils/log_util.dart'; +import 'package:rxdart/rxdart.dart'; + +class ProvisionalScheduleHistoryModel extends FlutterFlowModel { + late final String devUUID; + late final String userUUID; + late final String cliUUID; + + final unfocusNode = FocusNode(); + final _ProvisionalScheduleHistoryManager = FutureRequestManager(); + Future ProvisionalScheduleHistory({ + String? uniqueQueryKey, + bool? overrideCache, + required Future Function() requestFn, + }) => + _ProvisionalScheduleHistoryManager.performRequest( + uniqueQueryKey: uniqueQueryKey, + overrideCache: overrideCache, + requestFn: requestFn, + ); + void clearProvisionalScheduleHistoryCache() => _ProvisionalScheduleHistoryManager.clear(); + void clearProvisionalScheduleHistoryCacheKey(String? uniqueKey) => _ProvisionalScheduleHistoryManager.clearRequest(uniqueKey); + + @override + void initState(BuildContext context) { + initDatabase(); + } + + Future initDatabase() async { + devUUID = (await StorageHelper().get(KeychainStorageKey.devUUID.value)) ?? ''; + userUUID = (await StorageHelper().get(KeychainStorageKey.userUUID.value)) ?? ''; + cliUUID = (await StorageHelper().get(KeychainStorageKey.clientUUID.value)) ?? ''; + } + + @override + void dispose() { + unfocusNode.dispose(); + + clearProvisionalScheduleHistoryCache(); + } + + Future toggleOptionsAction(BuildContext context) async { + await showModalBottomSheet( + isScrollControlled: true, + backgroundColor: Colors.transparent, + useSafeArea: true, + context: context, + builder: (context) { + return GestureDetector( + onTap: () => unfocusNode.canRequestFocus + ? FocusScope.of(context).requestFocus(unfocusNode) + : FocusScope.of(context).unfocus(), + child: Padding( + padding: MediaQuery.viewInsetsOf(context), + child: const OptModalWidget(), + ), + ); + }, + ); + } +} + +// ignore: must_be_immutable +class ProvisionalScheduleHistoryScreen extends StatefulWidget { + late Map opt = { + 'personType': '.*', + 'accessType': '.*', + 'search': '.*', + }; + ProvisionalScheduleHistoryScreen({super.key, required this.opt}); + @override + State createState() => _ProvisionalScheduleHistoryState(opt); +} + +class _ProvisionalScheduleHistoryState extends State { + late ProvisionalScheduleHistoryModel _model; + final BehaviorSubject> selectedTypeSubject; + bool _isSubjectClosed = false; + final scaffoldKey = GlobalKey(); + + late ScrollController _scrollController; + int _pageNumber = 1; + final int _pageSize = 10; + bool _hasData = false; + bool _loading = false; + + String _personType = '.*'; + + late Future _accessFuture; + List _accessWrap = []; + + _ProvisionalScheduleHistoryState(Map opt) : selectedTypeSubject = BehaviorSubject.seeded(opt) { + selectedTypeSubject.listen((value) {}); + } + + @override + void initState() { + super.initState(); + _model = createModel(context, () => ProvisionalScheduleHistoryModel()); + _accessFuture = fetchProvisionalScheduleHistoryService(); + + _scrollController = ScrollController() + ..addListener(() { + if (_scrollController.position.atEdge && _scrollController.position.pixels != 0) { + _loadMoreAccess(); + } + }); + } + + @override + void dispose() { + selectedTypeSubject.close(); + _isSubjectClosed = true; + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final theme = FlutterFlowTheme.of(context); + + return Scaffold( + key: scaffoldKey, + backgroundColor: FlutterFlowTheme.of(context).primaryBackground, + appBar: _appBar(context, theme), + body: _body(context)); + } + + PreferredSizeWidget _appBar(BuildContext context, FlutterFlowTheme theme) { + return AppBar( + backgroundColor: theme.primaryBackground, + automaticallyImplyLeading: false, + leading: _backButton(context, theme), + title: _title(context, theme), + centerTitle: true, + elevation: 0.0, + actions: [_filterButton(context)], + ); + } + + Widget _backButton(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 _title(BuildContext context, FlutterFlowTheme theme) { + return Text( + FFLocalizations.of(context).getText('ch8qymga'), + style: theme.headlineMedium.override( + fontFamily: theme.headlineMediumFamily, + color: theme.primaryText, + fontSize: 16.0, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap().containsKey(theme.headlineMediumFamily), + ), + ); + } + + Widget _filterButton(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: (context) { + return GestureDetector( + onTap: () => Navigator.of(context).pop(), + child: Container( + color: Colors.transparent, + child: GestureDetector( + onTap: () {}, + child: OptModalWidget( + defaultPersonType: selectedTypeSubject.value['personType'] ?? '.*', + ), + ), + ), + ); + }); + + if (selectedFilter != null) { + _updateProvisionalScheduleHistoryAction(selectedFilter); + } + }, + ), + ), + ], + ); + } + + void _updateProvisionalScheduleHistoryAction(Map newType) { + if (!_isSubjectClosed) { + final currentType = selectedTypeSubject.value; + final updatedType = Map.from(currentType); + bool needsUpdate = false; + newType.forEach((key, newValue) { + if (currentType[key] != newValue) { + updatedType[key] = newValue; + needsUpdate = true; + } + }); + if (needsUpdate) { + selectedTypeSubject.add(updatedType); + fetchCardListViewService(updatedType); + safeSetState(() {}); + } + } + } + + Future fetchProvisionalScheduleHistoryService() async { + try { + setState(() => _loading = true); + var response = await PhpGroup.getProvSchedules.call( + _pageNumber.toString() + ); + + final List ProvisionalScheduleHistory = response.jsonBody['agendamento']['value'] ?? []; + + List filteredAccess = ProvisionalScheduleHistory.where((item) { + final personTypeMatches = _personType == '.*' || item["AGP_STATUS"].toString() == _personType; + return personTypeMatches; + }).toList(); + + if (filteredAccess.isNotEmpty) { + setState(() { + _accessWrap.addAll(filteredAccess); + _hasData = true; + _loading = false; + }); + return response; + } + _showNoMoreDataSnackbar(context); + setState(() { + _hasData = false; + _loading = false; + }); + return null; + } catch (e, s) { + await DialogUtil.errorDefault(context); + LogUtil.requestAPIFailed('processRequest', "", "Busca Acesso", e, s); + setState(() { + _hasData = false; + _loading = false; + }); + } + return null; + } + + void _showNoMoreDataSnackbar(BuildContext context) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + FFLocalizations.of(context).getVariableText(ptText: "Não há mais dados.", enText: "No more data."), + style: TextStyle( + color: Colors.white, + fontSize: LimitedFontSizeUtil.getBodyFontSize(context), + ), + ), + duration: const Duration(seconds: 3), + backgroundColor: FlutterFlowTheme.of(context).primary, + ), + ); + } + + Widget _body(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (_hasData == false && _pageNumber <= 1 && _loading == false) + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + Center( + child: Text( + FFLocalizations.of(context) + .getVariableText(ptText: "Nenhum histórico encontrado!", enText: "No history found!"), + )), + ], + ), + ) + else if (_hasData || _pageNumber >= 1) + Expanded(child: _cardListViewOrganismWidget()), + if (_hasData == true && _loading) + Container( + padding: const EdgeInsets.only(top: 15, bottom: 15), + child: Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + FlutterFlowTheme.of(context).primary, + ), + ), + ), + ) + ], + ); + } + + void _loadMoreAccess() { + if (_hasData == true) { + _pageNumber++; + _accessFuture = fetchProvisionalScheduleHistoryService(); + } + } + + void fetchCardListViewService(Map select) { + _personType = select['personType']!; + _accessWrap = []; + _pageNumber = 1; + _accessFuture = fetchProvisionalScheduleHistoryService(); + } + + Widget _cardListViewOrganismWidget() { + return FutureBuilder( + future: _accessFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting && _accessWrap.isEmpty) { + return Center( + child: SizedBox( + width: 50.0, + height: 50.0, + child: SpinKitCircle( + color: FlutterFlowTheme.of(context).primary, + size: 50.0, + ), + ), + ); + } else if (snapshot.hasError) { + return Center( + child: Text(FFLocalizations.of(context) + .getVariableText(ptText: "Falha ao efetuar operação!", enText: "Failed to perform operation!")), + ); + } + + return ListView.builder( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + controller: _scrollController, + itemCount: _accessWrap.length, + itemBuilder: (context, index) { + final ProvisionalScheduleHistoryItem = _accessWrap[index]; + return _ProvisionalScheduleHistoryCardMoleculeWidget(context, ProvisionalScheduleHistoryItem); + }, + ); + }, + ); + } + + Widget _ProvisionalScheduleHistoryCardMoleculeWidget(BuildContext context, dynamic ProvisionalScheduleHistoryItem) { + return CardItemTemplateComponentWidget( + imagePath: + 'https://freaccess.com.br/freaccess/getImage.php?cliID=${_model.cliUUID}&atividade=getFoto&Documento=${ProvisionalScheduleHistoryItem['PES_ID'] ?? ''}&tipo=${ProvisionalScheduleHistoryItem['PES_TIPO'] ?? ''}', + labelsHashMap: Map.from({ + FFLocalizations.of(context).getVariableText( + ptText: 'Nome:', + enText: 'Name:', + ): ProvisionalScheduleHistoryItem['AGP_NOME'] ?? '', + FFLocalizations.of(context).getVariableText( + ptText: 'Acesso:', + enText: 'Access:', + ): ProvisionalScheduleHistoryItem['AGP_DT_VISITA'] ?? '', + FFLocalizations.of(context).getVariableText( + ptText: 'Setor', + enText: 'Sector', + ): ProvisionalScheduleHistoryItem['AGP_OBSERVACAO'] ?? '', + }), + statusHashMap: [ + ProvisionalScheduleHistoryItem['AGP_STATUS'] == 'AT' + ? Map.from({ + FFLocalizations.of(context).getVariableText( + ptText: 'Morador', + enText: 'Resident', + ): FlutterFlowTheme.of(context).alternate2, + }) + : ProvisionalScheduleHistoryItem['AGP_STATUS'] == 'IN' + ? Map.from({ + FFLocalizations.of(context).getVariableText( + ptText: 'Visitante', + enText: 'Visitor', + ): FlutterFlowTheme.of(context).alternate2, + }) + : Map.from({ + FFLocalizations.of(context).getVariableText( + ptText: 'Desconhecido', + enText: 'Unknown', + ): FlutterFlowTheme.of(context).alternate2, + }), + ProvisionalScheduleHistoryItem['AGP_STATUS'] == 'CO' + ? Map.from({ + FFLocalizations.of(context).getVariableText( + ptText: 'Entrada', + enText: 'Entrance', + ): FlutterFlowTheme.of(context).success, + }) + : ProvisionalScheduleHistoryItem['AGP_STATUS'] == 'AA' + ? Map.from({ + FFLocalizations.of(context).getVariableText( + ptText: 'Saída', + enText: 'Exit', + ): FlutterFlowTheme.of(context).error, + }) + : Map.from({ + FFLocalizations.of(context).getVariableText( + ptText: 'Desconhecido', + enText: 'Unknown', + ): FlutterFlowTheme.of(context).warning, + }) + ], + onTapCardItemAction: () async {}); + } + + String imageUrlAtomWidget(String document, String type) { + return valueOrDefault( + "https://freaccess.com.br/freaccess/getImage.php?&cliID=${_model.cliUUID}&atividade=getFoto&Documento=$document&tipo=$type", + "https://storage.googleapis.com/flutterflow-io-6f20.appspot.com/projects/flutter-freaccess-hub-0xgz9q/assets/7ftdetkzc3s0/360_F_64676383_LdbmhiNM6Ypzb3FM4PPuFP9rHe7ri8Ju.jpg", + ); + } +} diff --git a/lib/features/histories/presentation/widgets/index.dart b/lib/features/histories/presentation/widgets/index.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/flutter_flow/nav/nav.dart b/lib/flutter_flow/nav/nav.dart index 25209384..2ad2b25c 100644 --- a/lib/flutter_flow/nav/nav.dart +++ b/lib/flutter_flow/nav/nav.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:hub/backend/schema/util/schema_util.dart'; +import 'package:hub/features/histories/presentation/pages/prov_schedule_page.dart'; import 'package:hub/features/home/index.dart'; import 'package:hub/features/property/index.dart'; import 'package:hub/flutter_flow/flutter_flow_util.dart'; @@ -131,7 +132,7 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) { FFRoute(name: 'petsOnThePropertyPage', path: '/petsOnThePropertyPage', builder: (context, params) => Scaffold(body: const PetsHistoryScreen(isApp: true))), FFRoute(name: 'vehiclesOnThePropertyPage', path: '/vehiclesOnThePropertyPage', builder: (context, params) => const VehicleOnTheProperty()), FFRoute(name: 'receptionPage', path: '/receptionPage', builder: (context, params) => const ReceptionPageWidget()), - FFRoute(name: 'messageHistoryPage', path: '/messageHistoryPage', builder: (context, params) => const MessageHistoryPageWidget()), + FFRoute(name: 'messageHistoryPage', path: '/messageHistoryPage', builder: (context, params) => ProvisionalScheduleHistoryScreen(opt: const {'personType': '.*', 'accessType': '.*', 'search': '.*'})), FFRoute(name: 'registerVisitorPage', path: '/registerVisitorPage', builder: (context, params) => const RegisterVisitorPageWidget()), FFRoute(name: 'scheduleCompleteVisitPage', path: '/scheduleCompleteVisitPage', builder: (context, params) => const ScheduleCompleteVisitPageWidget()), FFRoute(name: 'deliverySchedule', path: '/deliverySchedule', builder: (context, params) => const DeliverySchedule()),