diff --git a/integration_test/app_test.dart b/integration_test/app_test.dart new file mode 100644 index 00000000..28e5df89 --- /dev/null +++ b/integration_test/app_test.dart @@ -0,0 +1,209 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:hub/flutter_flow/random_data_util.dart'; +import 'package:hub/initialization.dart'; +import 'package:hub/main.dart'; +import 'package:hub/shared/helpers/storage/base_storage.dart'; +import 'package:hub/shared/helpers/storage/storage_helper.dart'; +import 'package:integration_test/integration_test.dart'; + +late WidgetTester widget; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group( + 'Initialization', + () { + group('Navigation', () { + setUpAll(() async => await initializeApp().then((_) async => await StorageHelper().set(SecureStorageKey.isLogged.value, 'false'))); + testWidgets('Test Welcome', (WidgetTester tester) async { + widget = tester; + await _testWelcome(); + }); + }); + + group('Terms of Use', () { + // Add tests for Terms of Use here + }); + }, + ); + + group('Authentication', () { + group('Sign in', () { + setUpAll(() async => await initializeApp().then((_) async => await StorageHelper().set(SecureStorageKey.isLogged.value, 'false'))); + testWidgets('Test Sign In', (WidgetTester tester) async { + widget = tester; + await _testSignIn(); + }); + }); + group('Sign up', () { + setUpAll(() async => await initializeApp().then((_) async => await StorageHelper().set(SecureStorageKey.isLogged.value, 'false'))); + testWidgets('Test Sign Up', (WidgetTester tester) async { + widget = tester; + await _testSignUp(); + }); + }); + group('Sign Out', () { + // Add tests for Sign Out here + }); + group('Forgot Password', () { + // setUpAll(() async => await initializeApp().then((_) => StorageUtil().isLogged = false)); + // testWidgets('Test Forgot Password', (WidgetTester tester) async { + // widget = tester; + // await _testForgotPassword(); + // }); + }); + }); + group('Localization', () { + // Add tests for Localization here + }); + group('Networking', () { + // Add tests for Networking here + }); + group('Functionality', () { + // Add tests for Functionality here + }); + group('Usability', () { + // Add tests for Usability here + }); + group('Performance', () { + // Add tests for Performance here + }); + group('Security', () { + // Add tests for Security here + }); + group('Accessibility', () { + // Add tests for Accessibility here + }); + group('Compatibility', () { + // Add tests for Compatibility here + }); + group('Internationalization', () { + // Add tests for Internationalization here + }); +} + +Future _testWelcome() async { + await widget.pumpWidget(const App()); + await widget.pumpAndSettle(); + await _navigateToSignIn(); + await _navigateToSignUp(); + await widget.pumpAndSettle(); + await widget.pumpWidget(const App()); + await widget.pumpAndSettle(); + await _navigateToSignUp(); + await _navigateToSignIn(); + await widget.pumpAndSettle(); +} + +Future _testSignIn() async { + await widget.pumpWidget(const App()); + await _navigateToSignIn(); + await _auth({'emailTextFormField': 'erro@exemplo.com', 'passwordTextFormField': '12345678'}); + await _auth({'emailTextFormField': 'email_app@exemplo.com', 'passwordTextFormField': '12345678'}); +} + +Future _testSignUp() async { + await widget.pumpWidget(const App()); + await _navigateToSignUp(); + + var credentials = {'nameTextFormField': 'app', 'emailTextFormField': 'email_app@exemplo.com', 'passwordTextFormField': '12345678'}; + await _auth(credentials); + + var name = randomString(7, 7, true, true, true); + var email = '$name@example.com'; + var password = '12345678'; + credentials = {'nameTextFormField': name, 'emailTextFormField': email, 'passwordTextFormField': password}; + await _navigateToSignUp(); + await _auth(credentials); + credentials = {'emailTextFormField': email, 'passwordTextFormField': password}; + await _auth(credentials); +} + +Future _testForgotPassword() async { + await widget.pumpWidget(const App()); + await _navigateToSignIn(); + await _recoveryPassword(); + + var addr = randomString(5, 5, true, true, true); + var credentials = {'recoveryTextFormField': '$addr@exemple.com'}; + await _send(credentials); + + await Future.delayed(const Duration(seconds: 2)); + + await _recoveryPassword(); + credentials = {'recoveryTextFormField': 'email_app@exemple.com'}; + await _send(credentials); +} + +Future _recoveryPassword() async { + await widget.pumpAndSettle(); + final Finder forgotPassword = find.byKey(const ValueKey('ForgotPassword')); + if (forgotPassword.evaluate().isNotEmpty) await widget.tap(forgotPassword); + await widget.ensureVisible(forgotPassword); + await widget.pumpAndSettle(); +} + +Future _navigateBackUsingSystemGesture() async => IntegrationTestWidgetsFlutterBinding.instance.keyboard.isLogicalKeyPressed(LogicalKeyboardKey.escape); +Future _navigateToSignUp() async { + await widget.pumpAndSettle(); + final Finder navToSignUp = find.byKey(const ValueKey('toggleSignUpPage')); + if (navToSignUp.evaluate().isNotEmpty) { + await widget.tap(navToSignUp); + await widget.pumpAndSettle(); + } +} + +Future _navigateToSignIn() async { + await widget.pumpAndSettle(); + final Finder navToSignIn = find.byKey(const ValueKey('toggleSignInPage')); + expect(navToSignIn, findsOneWidget); + if (navToSignIn.evaluate().isNotEmpty) { + await widget.tap(navToSignIn); + await widget.pumpAndSettle(); + } +} + +Future _auth(Map credentials) async { + await _enterCredentials(credentials); + await _submit('SubmitButtonWidget'); +} + +Future _send(Map credentials) async { + await _enterCredentials(credentials); + await _submit('SendButtonWidget'); +} + +Future _enterCredentials(Map credentials) async { + await widget.pumpAndSettle(); + for (var entry in credentials.entries) { + final Finder field = find.byKey(ValueKey(entry.key)); + await widget.pumpAndSettle(); + expect(field, findsOneWidget); + await widget.enterText(field, entry.value); + await widget.pumpAndSettle(); + } + await widget.pumpAndSettle(); +} + +Future _submit(String key) async { + await widget.pumpAndSettle(); + final Finder submitButton = find.byKey(ValueKey(key)); + await widget.pumpAndSettle(); + if (submitButton.evaluate().isNotEmpty) { + await widget.tap(submitButton); + await widget.pumpAndSettle(); + } + + final Finder throwExceptionWidget = find.byKey(const ValueKey('ThrowExceptionWidget')); + await widget.pumpAndSettle(); + if (throwExceptionWidget.evaluate().isNotEmpty) { + await widget.ensureVisible(throwExceptionWidget); + await widget.tap(throwExceptionWidget); + await widget.pumpAndSettle(); + } else { + await _navigateBackUsingSystemGesture(); + } +} diff --git a/integration_test/features/provisional_history_test.dart b/integration_test/features/provisional_history_test.dart new file mode 100644 index 00000000..a95362d5 --- /dev/null +++ b/integration_test/features/provisional_history_test.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart'; +import 'package:hub/initialization.dart'; +import 'package:hub/main.dart' as app; +import 'package:hub/shared/helpers/storage/base_storage.dart'; +import 'package:hub/shared/helpers/storage/storage_helper.dart'; +import 'package:integration_test/integration_test.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('ProvisionalHistoryPage Integration Test', () { + setUpAll(() async => await initializeApp().then((_) async { + await StorageHelper().set(SecureStorageKey.isLogged.value, 'true'); + await StorageHelper().set(SecureStorageKey.haveLocal.value, 'true'); + await StorageHelper().set(KeychainStorageKey.devUUID.value, 'b5c3818753e76d85'); + await StorageHelper().set(KeychainStorageKey.userUUID.value, '649c45d7514a28.85876308'); + await StorageHelper().set(KeychainStorageKey.clientUUID.value, '7'); + })); + + testWidgets('Filter Provisional History', (WidgetTester tester) async { + app.main(); + await tester.pumpAndSettle(); + + final Finder menuButton = find.byIcon(Icons.person_search_outlined); + await tester.tap(menuButton); + await tester.pumpAndSettle(); + + final Finder filterButton = find.byIcon(Icons.filter_list); + await tester.tap(filterButton); + await tester.pumpAndSettle(); + + final Finder activeFilterOption = find.text('Active'); + await tester.tap(activeFilterOption); + await tester.pumpAndSettle(); + + final Finder applyFilterButton = find.text('Apply'); + await tester.tap(applyFilterButton); + await tester.pumpAndSettle(); + + expect(find.byType(CardItemTemplateComponentWidget), findsWidgets); + }); + }); +} diff --git a/lib/backend/api_requests/api_calls.dart b/lib/backend/api_requests/api_calls.dart index 1b9a9a9f..06f7bf5b 100644 --- a/lib/backend/api_requests/api_calls.dart +++ b/lib/backend/api_requests/api_calls.dart @@ -63,36 +63,35 @@ class PhpGroup { } class GetProvSchedules { - Future call(final String page) async { - // final String baseUrl = PhpGroup.getBaseUrl(); - // final String devUUID = (await StorageHelper().g(KeychainStorageKey.devUUID.value)) ?? ''; - // final String userUUID = (await StorageHelper().g(KeychainStorageKey.userUUID.value)) ?? ''; - // final String cliID = (await StorageHelper().g(KeychainStorageKey.clientUUID.value)) ?? ''; - // const String atividade = 'getProvSchedules'; - // const String pageSize = '10'; - final String baseUrl = 'http://localhost:3000'; + Future call(final String page, final String status) async { + final String baseUrl = PhpGroup.getBaseUrl(); + final String devUUID = (await StorageHelper().get(KeychainStorageKey.devUUID.value)) ?? ''; + final String userUUID = (await StorageHelper().get(KeychainStorageKey.userUUID.value)) ?? ''; + final String cliID = (await StorageHelper().get(KeychainStorageKey.clientUUID.value)) ?? ''; + const String atividade = 'getAgendamentoProv'; + const String pageSize = '10'; + final bool isFiltered = status != '' && status != '.*'; + // const String baseUrl = 'http://localhost:3000'; return await ApiManager.instance.makeApiCall( callName: 'getProvSchedules', - apiUrl: '$baseUrl/getAgendamentoProv.php', + apiUrl: '$baseUrl/processRequest.php', callType: ApiCallType.POST, headers: {'Content-Type': 'application/x-www-form-urlencoded'}, params: { - "proId": "8", - "status": "AT", - "page": page, - "pageSize": "10" - // 'devUUID': devUUID, - // 'userUUID': userUUID, - // 'cliID': cliID, - // 'atividade': atividade, - // 'page': page, - // 'pageSize': pageSize, + if (isFiltered) 'status': status, + 'devUUID': devUUID, + 'userUUID': userUUID, + 'cliID': cliID, + 'atividade': atividade, + 'page': page, + 'pageSize': pageSize, }, bodyType: BodyType.X_WWW_FORM_URL_ENCODED, returnBody: true, encodeBodyUtf8: false, decodeUtf8: false, cache: false, + isStreamingApi: false, alwaysAllowBody: false, ); } @@ -194,7 +193,6 @@ class GetVehiclesByProperty { static GetLicense getLicense = GetLicense(); } - class GetLicense { Future call() async { final String baseUrl = PhpGroup.getBaseUrl(); @@ -935,25 +933,27 @@ class GetLocalsCall { final String devUUID = await StorageHelper().get(KeychainStorageKey.devUUID.value) ?? ''; final String userUUID = await StorageHelper().get(KeychainStorageKey.userUUID.value) ?? ''; - return await ApiManager.instance.makeApiCall( - callName: 'getLocals', - apiUrl: '$baseUrl/getLocais.php', - callType: ApiCallType.POST, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, - params: { - 'devUUID': devUUID, - 'userUUID': userUUID, - }, - bodyType: BodyType.X_WWW_FORM_URL_ENCODED, - returnBody: true, - encodeBodyUtf8: false, - decodeUtf8: false, - cache: false, - isStreamingApi: false, - alwaysAllowBody: false, - ); + return await ApiManager.instance + .makeApiCall( + callName: 'getLocals', + apiUrl: '$baseUrl/getLocais.php', + callType: ApiCallType.POST, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + params: { + 'devUUID': devUUID, + 'userUUID': userUUID, + }, + bodyType: BodyType.X_WWW_FORM_URL_ENCODED, + returnBody: true, + encodeBodyUtf8: false, + decodeUtf8: false, + cache: false, + isStreamingApi: false, + alwaysAllowBody: false, + ) + .timeout(const Duration(seconds: 30)); } List? locais(dynamic response) => getJsonField( @@ -2639,8 +2639,7 @@ class ApiPagingParams { }); @override - String toString() => - 'PagingParams(nextPageNumber: $nextPageNumber, numItems: $numItems, lastResponse: $lastResponse,)'; + String toString() => 'PagingParams(nextPageNumber: $nextPageNumber, numItems: $numItems, lastResponse: $lastResponse,)'; } String _toEncodable(dynamic item) { diff --git a/lib/components/molecular_components/message_opt_modal/opt_modal_model.dart b/lib/components/molecular_components/message_opt_modal/opt_modal_model.dart deleted file mode 100644 index e2b90125..00000000 --- a/lib/components/molecular_components/message_opt_modal/opt_modal_model.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; - -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/form_field_controller.dart'; -import 'opt_modal_widget.dart' show OptModalWidget; - -class OptModalModel extends FlutterFlowModel { - /// State fields for stateful widgets in this component. - - // State field(s) for TextField widget. - FocusNode? textFieldFocusNode; - TextEditingController? textController; - String? Function(BuildContext, String?)? textControllerValidator; - // State field(s) for Checkbox widget. - bool? checkboxValue1; - // State field(s) for Checkbox widget. - bool? checkboxValue2; - // State field(s) for CheckboxGroup widget. - 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(); - } -} diff --git a/lib/components/molecular_components/opt_modal/opt_modal_model.dart b/lib/components/molecular_components/opt_modal/opt_modal_model.dart deleted file mode 100644 index 1f2e3693..00000000 --- a/lib/components/molecular_components/opt_modal/opt_modal_model.dart +++ /dev/null @@ -1,30 +0,0 @@ -import '/flutter_flow/flutter_flow_util.dart'; -import '/flutter_flow/form_field_controller.dart'; -import 'opt_modal_widget.dart' show OptModalWidget; -import 'package:flutter/material.dart'; - -class OptModalModel extends FlutterFlowModel { - /// State fields for stateful widgets in this component. - - // State field(s) for TextField widget. - FocusNode? textFieldFocusNode; - TextEditingController? textController; - String? Function(BuildContext, String?)? textControllerValidator; - // State field(s) for Checkbox widget. - bool? checkboxValue1; - // State field(s) for Checkbox widget. - bool? checkboxValue2; - // State field(s) for CheckboxGroup widget. - 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(); - } -} diff --git a/lib/components/molecular_components/opt_modal/opt_modal_widget.dart b/lib/components/molecular_components/opt_modal/opt_modal_widget.dart deleted file mode 100644 index a4914b22..00000000 --- a/lib/components/molecular_components/opt_modal/opt_modal_widget.dart +++ /dev/null @@ -1,302 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:hub/components/molecular_components/message_opt_modal/opt_modal_model.dart'; -import 'package:hub/flutter_flow/flutter_flow_model.dart'; -import 'package:hub/flutter_flow/flutter_flow_theme.dart'; -import 'package:hub/flutter_flow/internationalization.dart'; -import 'package:hub/flutter_flow/nav/nav.dart'; - -class OptModalWidget extends StatefulWidget { - final String defaultPersonType; - final String defaultAccessType; - - const OptModalWidget({ - super.key, - this.defaultPersonType = '.*', - this.defaultAccessType = '.*', - }); - - @override - _OptModalWidgetState createState() => _OptModalWidgetState(); -} - -class _OptModalWidgetState extends State { - late OptModalModel _model; - - late Map selected; - final List> personTypeOptions = [ - {'title': 'zok7lu4w', 'value': 'E'}, - {'title': 'oonqk812', 'value': 'O'}, - ]; - final List> accessTypeOptions = [ - {'title': '580z80ct', 'value': '0'}, - {'title': '1nbwqtzs', 'value': '1'}, - ]; - - @override - void setState(VoidCallback callback) { - super.setState(callback); - _model.onUpdate(); - } - - @override - void initState() { - super.initState(); - - _model = createModel(context, () => OptModalModel()); - - _model.textController ??= TextEditingController(); - _model.textFieldFocusNode ??= FocusNode(); - - selected = { - 'personType': widget.defaultPersonType == '.*' ? ['E', 'O'] : [widget.defaultPersonType], - 'accessType': widget.defaultAccessType == '.*' ? ['0', '1'] : [widget.defaultAccessType], - 'search': '.*', - }; - } - - void _applyFilter() { - Map filterResult = { - 'personType': '', - 'accessType': '', - 'search': _model.textController?.text == '' ? '.*' : _model.textController!.text.toLowerCase(), - }; - - if (selected['personType']!.isEmpty) { - filterResult['personType'] = '.*'; - } else if (selected['personType']!.length > 1) { - filterResult['personType'] = '.*'; - } else { - filterResult['personType'] = selected['personType']!.first; - } - - if (selected['accessType']!.isEmpty) { - filterResult['accessType'] = '.*'; - } else if (selected['accessType']!.length > 1) { - filterResult['accessType'] = '.*'; - } else { - filterResult['accessType'] = selected['accessType']!.first; - } - - // Navigator.pop(context, filterResult); - context.pop(filterResult); - } - - Widget _buildCheckboxListTile(String key, List> options) { - return Column( - children: [ - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsetsDirectional.fromSTEB(0.0, 10.0, 0.0, 0.0), - child: Text( - FFLocalizations.of(context).getText('l7tw8b92'), - textAlign: TextAlign.left, // Adiciona esta linha para alinhar o texto à esquerda - style: FlutterFlowTheme.of(context).bodyMedium.override( - fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily, - letterSpacing: 0.0, - useGoogleFonts: GoogleFonts.asMap().containsKey(FlutterFlowTheme.of(context).bodyMediumFamily), - color: FlutterFlowTheme.of(context).primaryText, - ), - ), - ), - ], - ), - ListView.builder( - shrinkWrap: true, - itemCount: options.length, - itemBuilder: (context, index) { - final option = options[index]; - return CheckboxListTile( - title: Text( - FFLocalizations.of(context).getText(option['title']!), - style: FlutterFlowTheme.of(context).bodyMedium.override( - fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily, - letterSpacing: 0.0, - 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: 10, - color: FlutterFlowTheme.of(context).secondaryText, - ), - controlAffinity: ListTileControlAffinity.leading, // Adiciona esta linha - ); - }, - ), - ], - ); - } - - void _updateSelection(String? value, String key) { - setState(() { - if (value == '.') { - selected[key] = []; - } else if (value != null) { - if (selected[key]!.contains(value)) { - selected[key]!.remove(value); - } else { - selected[key]!.add(value); - } - } - }); - } - - @override - Widget build(BuildContext context) { - return SafeArea( - child: Align( - alignment: const AlignmentDirectional(1.0, -1.0), - child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB(0.0, 50.0, 50.0, 0.0), - child: Container( - width: 300.0, - height: 450.0, - 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'), // Filtros - style: FlutterFlowTheme.of(context).headlineMedium.override( - fontFamily: FlutterFlowTheme.of(context).headlineMediumFamily, - color: FlutterFlowTheme.of(context).primaryText, - fontSize: 16.0, - letterSpacing: 0.0, - useGoogleFonts: - GoogleFonts.asMap().containsKey(FlutterFlowTheme.of(context).headlineMediumFamily), - ), - ), - ), - ], - ), - Padding( - padding: const EdgeInsetsDirectional.fromSTEB(8.0, 0.0, 8.0, 0.0), - child: TextFormField( - controller: _model.textController, - focusNode: _model.textFieldFocusNode, - autofocus: false, - obscureText: false, - decoration: InputDecoration( - isDense: true, - labelText: FFLocalizations.of(context).getText( - '0enrtljz' /* Pesquise aqui..... */, - ), - labelStyle: FlutterFlowTheme.of(context).labelMedium.override( - fontFamily: FlutterFlowTheme.of(context).labelMediumFamily, - color: FlutterFlowTheme.of(context).primaryText, - letterSpacing: 0.0, - useGoogleFonts: - GoogleFonts.asMap().containsKey(FlutterFlowTheme.of(context).labelMediumFamily), - ), - hintStyle: FlutterFlowTheme.of(context).labelMedium.override( - fontFamily: FlutterFlowTheme.of(context).labelMediumFamily, - color: FlutterFlowTheme.of(context).primaryText, - letterSpacing: 0.0, - useGoogleFonts: - GoogleFonts.asMap().containsKey(FlutterFlowTheme.of(context).labelMediumFamily), - ), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide( - color: FlutterFlowTheme.of(context).alternate, - width: 1, - ), - borderRadius: BorderRadius.circular(8.0), - ), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: FlutterFlowTheme.of(context).primary, - width: 1, - ), - borderRadius: BorderRadius.circular(8.0), - ), - errorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: FlutterFlowTheme.of(context).error, - width: 1, - ), - borderRadius: BorderRadius.circular(8.0), - ), - focusedErrorBorder: OutlineInputBorder( - borderSide: BorderSide( - color: FlutterFlowTheme.of(context).error, - width: 1, - ), - borderRadius: BorderRadius.circular(8.0), - ), - filled: true, - fillColor: FlutterFlowTheme.of(context).alternate, - suffixIcon: const Icon( - Icons.search_outlined, - ), - ), - style: FlutterFlowTheme.of(context).bodyMedium.override( - fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily, - letterSpacing: 0.0, - useGoogleFonts: - GoogleFonts.asMap().containsKey(FlutterFlowTheme.of(context).bodyMediumFamily), - ), - validator: _model.textControllerValidator.asValidator(context), - ), - ), - SingleChildScrollView( - child: Container( - padding: const EdgeInsets.all(20), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - _buildCheckboxListTile('personType', personTypeOptions), - _buildCheckboxListTile('accessType', accessTypeOptions), - ], - ), - ), - ), - ElevatedButton( - onPressed: _applyFilter, - style: ElevatedButton.styleFrom( - foregroundColor: FlutterFlowTheme.of(context).info, - backgroundColor: FlutterFlowTheme.of(context).primary, - ), - child: Text(FFLocalizations.of(context).getText('88kshkph')), - ), - ], - ), - ), - ), - ), - ), - ); - } -} diff --git a/lib/components/templates_components/card_item_template_component/card_item_template_component_widget.dart b/lib/components/templates_components/card_item_template_component/card_item_template_component_widget.dart index 4814763e..9cba811c 100644 --- a/lib/components/templates_components/card_item_template_component/card_item_template_component_widget.dart +++ b/lib/components/templates_components/card_item_template_component/card_item_template_component_widget.dart @@ -79,7 +79,7 @@ class _CardItemTemplateComponentWidgetState extends State { clearAccessHistoryCache(); } - - 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(), - ), - ); - }, - ); - } } diff --git a/lib/features/history/presentation/blocs/index.dart b/lib/features/history/presentation/blocs/index.dart new file mode 100644 index 00000000..b189300a --- /dev/null +++ b/lib/features/history/presentation/blocs/index.dart @@ -0,0 +1,2 @@ +export 'access_history_bloc.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..2601bb51 --- /dev/null +++ b/lib/features/history/presentation/blocs/provisional_history_bloc.dart @@ -0,0 +1,52 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:hub/shared/helpers/storage/base_storage.dart'; +import 'package:hub/shared/helpers/storage/storage_helper.dart'; + +class ProvisionalHistoryEvent {} + +class LoadProvisionalHistory extends ProvisionalHistoryEvent {} + +class ProvisionalHistoryStateBloc { + final String devUUID; + final String userUUID; + final String cliUUID; + final bool isLoading; + + ProvisionalHistoryStateBloc({ + required this.devUUID, + required this.userUUID, + required this.cliUUID, + this.isLoading = false, + }); + + ProvisionalHistoryStateBloc copyWith({ + String? devUUID, + String? userUUID, + String? cliUUID, + bool? isLoading, + }) { + return ProvisionalHistoryStateBloc( + devUUID: devUUID ?? this.devUUID, + userUUID: userUUID ?? this.userUUID, + cliUUID: cliUUID ?? this.cliUUID, + isLoading: isLoading ?? this.isLoading, + ); + } +} + +class ProvisionalHistoryBloc extends Bloc { + ProvisionalHistoryBloc() : super(ProvisionalHistoryStateBloc(devUUID: '', userUUID: '', cliUUID: '')) { + on(_onLoadProvisionalHistory); + } + + Future _onLoadProvisionalHistory( + LoadProvisionalHistory event, + Emitter emit, + ) async { + emit(state.copyWith(isLoading: true)); + final devUUID = (await StorageHelper().get(KeychainStorageKey.devUUID.value)) ?? ''; + final userUUID = (await StorageHelper().get(KeychainStorageKey.userUUID.value)) ?? ''; + final cliUUID = (await StorageHelper().get(KeychainStorageKey.clientUUID.value)) ?? ''; + emit(state.copyWith(devUUID: devUUID, userUUID: userUUID, cliUUID: cliUUID, isLoading: false)); + } +} diff --git a/lib/features/history/presentation/index.dart b/lib/features/history/presentation/index.dart new file mode 100644 index 00000000..9a3f70b7 --- /dev/null +++ b/lib/features/history/presentation/index.dart @@ -0,0 +1,4 @@ +export 'blocs/index.dart'; + +export 'widgets/index.dart'; +export 'pages/index.dart'; diff --git a/lib/pages/acess_history_page/acess_history_page_widget.dart b/lib/features/history/presentation/pages/acess_history_page_widget.dart similarity index 90% rename from lib/pages/acess_history_page/acess_history_page_widget.dart rename to lib/features/history/presentation/pages/acess_history_page_widget.dart index 1f239f26..81501e99 100644 --- a/lib/pages/acess_history_page/acess_history_page_widget.dart +++ b/lib/features/history/presentation/pages/acess_history_page_widget.dart @@ -4,12 +4,11 @@ import 'package:flutter/material.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/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_icon_button.dart'; import 'package:hub/flutter_flow/flutter_flow_theme.dart'; import 'package:hub/flutter_flow/flutter_flow_util.dart'; -import 'package:hub/pages/acess_history_page/acess_history_page_model.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'; @@ -18,11 +17,8 @@ import 'package:rxdart/rxdart.dart'; @immutable // ignore: must_be_immutable class AccessHistoryScreen extends StatefulWidget { - late Map opt = { - 'personType': '.*', - 'accessType': '.*', - 'search': '.*', - }; + late Map opt = {'personType': '.*'}; + AccessHistoryScreen({super.key, required this.opt}); @override State createState() => _AccessHistoryState(opt); @@ -75,10 +71,11 @@ class _AccessHistoryState extends State { final theme = FlutterFlowTheme.of(context); return Scaffold( - key: scaffoldKey, - backgroundColor: FlutterFlowTheme.of(context).primaryBackground, - appBar: _appBar(context, theme), - body: _body(context)); + key: scaffoldKey, + backgroundColor: FlutterFlowTheme.of(context).primaryBackground, + appBar: _appBar(context, theme), + body: _body(context), + ); } PreferredSizeWidget _appBar(BuildContext context, FlutterFlowTheme theme) { @@ -141,8 +138,21 @@ class _AccessHistoryState extends State { color: Colors.transparent, child: GestureDetector( onTap: () {}, - child: OptModalWidget( - defaultPersonType: selectedTypeSubject.value['personType'] ?? '.*', + child: FilterWidget( + defaultSelections: selectedTypeSubject.value, + filterOptions: { + 'personType': [ + { + 'title': FFLocalizations.of(context).getText('zok7lu4w'), + 'value': 'E', + }, + { + 'title': FFLocalizations.of(context).getText('oonqk812'), + 'value': 'O', + }, + ], + }, + filterTitles: {'personType': ''}, ), ), ), @@ -247,8 +257,7 @@ class _AccessHistoryState extends State { children: [ Center( child: Text( - FFLocalizations.of(context) - .getVariableText(ptText: "Nenhum histórico encontrado!", enText: "No history found!"), + FFLocalizations.of(context).getVariableText(ptText: "Nenhum histórico encontrado!", enText: "No history found!"), )), ], ), @@ -301,8 +310,7 @@ class _AccessHistoryState extends State { ); } else if (snapshot.hasError) { return Center( - child: Text(FFLocalizations.of(context) - .getVariableText(ptText: "Falha ao efetuar operação!", enText: "Failed to perform operation!")), + child: Text(FFLocalizations.of(context).getVariableText(ptText: "Falha ao efetuar operação!", enText: "Failed to perform operation!")), ); } diff --git a/lib/features/history/presentation/pages/index.dart b/lib/features/history/presentation/pages/index.dart new file mode 100644 index 00000000..0b731c13 --- /dev/null +++ b/lib/features/history/presentation/pages/index.dart @@ -0,0 +1,2 @@ +export 'acess_history_page_widget.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..bca19aec --- /dev/null +++ b/lib/features/history/presentation/pages/provisional_history_page.dart @@ -0,0 +1,424 @@ +import 'dart:async'; +import 'dart:developer'; + +import 'package:flutter/material.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/features/history/index.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:hub/shared/utils/snackbar_util.dart'; +import 'package:rxdart/rxdart.dart'; + +@immutable +// ignore: must_be_immutable +class ProvisionalHistoryPage extends StatefulWidget { + Map opt; + ProvisionalHistoryPage({super.key, Map? opt}) : opt = opt ?? const {'AGP_STATUS': '.*'}; + @override + State createState() => ProvisionalHistoryState(opt); +} + +class ProvisionalHistoryState extends State { + final BehaviorSubject> selectedTypeSubject; + late ScrollController _scrollController; + final scaffoldKey = GlobalKey(); + + bool _isSubjectClosed = false; + int _pageNumber = 1; + bool hasData = false; + bool _loading = false; + + String status = '.*'; + + late Future future; + List wrap = []; + + ProvisionalHistoryState(Map opt) : selectedTypeSubject = BehaviorSubject.seeded(opt) { + selectedTypeSubject.listen((value) {}); + } + + @override + void initState() { + super.initState(); + future = fetchHistoryService(); + _scrollController = ScrollController() + ..addListener(() { + if (_scrollController.position.atEdge && _scrollController.position.pixels != 0) { + _loadMore(); + } + }); + } + + @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).getVariableText( + ptText: 'Histórico Provisório', + enText: 'Provisional History', + ), + 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: FilterWidget( + defaultSelections: selectedTypeSubject.value, + filterOptions: { + 'AGP_STATUS': [ + { + 'title': FFLocalizations.of(context).getVariableText( + ptText: 'Ativo', + enText: 'Active', + ), + 'value': 'AT', + }, + { + 'title': FFLocalizations.of(context).getVariableText( + ptText: 'Convidado', + enText: 'Guest', + ), + 'value': 'CO', + }, + { + 'title': FFLocalizations.of(context).getVariableText( + ptText: 'Inativo', + enText: 'Inactive', + ), + 'value': 'IN', + }, + { + 'title': FFLocalizations.of(context).getVariableText( + ptText: 'Aguardando Aprovação', + enText: 'Awaiting Approval', + ), + 'value': 'AA', + }, + ], + }, + filterTitles: {'AGP_STATUS': ''}, + ), + ), + ), + ); + }); + + if (selectedFilter != null) { + _updateHistoryAction(selectedFilter); + } + }, + ), + ), + ], + ); + } + + void _updateHistoryAction(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 fetchHistoryService() async { + try { + setState(() => _loading = true); + var response = await PhpGroup.getProvSchedules(_pageNumber.toString(), status); + + final List history = response.jsonBody['agendamento']['value'] ?? []; + + if (history.isNotEmpty) { + setState(() { + wrap.addAll(history); + hasData = true; + _loading = false; + }); + return response; + } + SnackBarUtil.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; + } + + 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 _loadMore() { + if (hasData == true) { + _pageNumber++; + future = fetchHistoryService(); + } + } + + void fetchCardListViewService(Map select) { + status = select['AGP_STATUS']!; + wrap = []; + _pageNumber = 1; + future = fetchHistoryService(); + } + + Widget _cardListViewOrganismWidget() { + return FutureBuilder( + future: future, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting && wrap.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: wrap.length, + itemBuilder: (context, index) { + final historyItem = wrap[index]; + return _historyCardMoleculeWidget(context, historyItem); + }, + ); + }, + ); + } + + Widget _historyCardMoleculeWidget(BuildContext context, dynamic historyItem) { + return CardItemTemplateComponentWidget( + imagePath: null, + labelsHashMap: _buildLabelsHashMap(context, historyItem), + statusHashMap: _buildStatusHashMap(context, historyItem), + onTapCardItemAction: () async {}, + ); + } + + String _imageUrlAtomWidget(String document, String type) { + return valueOrDefault( + "https://freaccess.com.br/freaccess/getImage.php?&cliID=&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", + ); + } + + Map _buildLabelsHashMap(BuildContext context, dynamic historyItem) { + return { + FFLocalizations.of(context).getVariableText( + ptText: 'Nome:', + enText: 'Name:', + ): historyItem['AGP_NOME'] ?? '', + FFLocalizations.of(context).getVariableText( + ptText: 'Data:', + enText: 'Data:', + ): formatDate(historyItem['AGP_DT_VISITA']), + FFLocalizations.of(context).getVariableText( + ptText: 'Observação:', + enText: 'Observation:', + ): formatObs(historyItem['AGP_OBSERVACAO']), + }; + } + + String formatObs(String? obs) { + if (obs == null || obs.isEmpty) { + return FFLocalizations.of(context).getVariableText( + ptText: 'Não fornecida', + enText: 'No provided', + ); + } + return obs; + } + + String formatDate(String dateString) { + DateTime dateTime = DateTime.parse(dateString); + return "${dateTime.day.toString().padLeft(2, '0')}/${dateTime.month.toString().padLeft(2, '0')}/${dateTime.year} ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}"; + } + + List> _buildStatusHashMap(BuildContext context, dynamic historyItem) { + return [ + { + FFLocalizations.of(context).getVariableText( + ptText: 'Visitante', + enText: 'Visitor', + ): FlutterFlowTheme.of(context).alternate2, + }, + _getStatusMap(context, historyItem['AGP_STATUS']) + ]; + } + + Map _getStatusMap(BuildContext context, String status) { + switch (status) { + case 'AT': + return { + FFLocalizations.of(context).getVariableText( + ptText: 'Ativo', + enText: 'Active', + ): FlutterFlowTheme.of(context).success, + }; + case 'CO': + return { + FFLocalizations.of(context).getVariableText( + ptText: 'Convidado', + enText: 'Called', + ): Colors.blue, + }; + case 'IN': + return { + FFLocalizations.of(context).getVariableText( + ptText: 'Inativo', + enText: 'Inactive', + ): FlutterFlowTheme.of(context).error, + }; + case 'AA': + return { + FFLocalizations.of(context).getVariableText( + ptText: 'Aguardando Aprovação', + enText: 'Awaiting Approval', + ): FlutterFlowTheme.of(context).warning, + }; + default: + return { + FFLocalizations.of(context).getVariableText( + ptText: 'Desconhecido', + enText: 'Unknown', + ): FlutterFlowTheme.of(context).alternate2, + }; + } + } +} diff --git a/lib/components/molecular_components/message_opt_modal/opt_modal_widget.dart b/lib/features/history/presentation/widgets/access_filter_modal.dart similarity index 84% rename from lib/components/molecular_components/message_opt_modal/opt_modal_widget.dart rename to lib/features/history/presentation/widgets/access_filter_modal.dart index bba2ea01..85a5ecbb 100644 --- a/lib/components/molecular_components/message_opt_modal/opt_modal_widget.dart +++ b/lib/features/history/presentation/widgets/access_filter_modal.dart @@ -1,27 +1,53 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'package:hub/components/molecular_components/message_opt_modal/opt_modal_model.dart'; import 'package:hub/flutter_flow/flutter_flow_theme.dart'; import 'package:hub/flutter_flow/flutter_flow_util.dart'; +import 'package:hub/flutter_flow/form_field_controller.dart'; import 'package:hub/flutter_flow/nav/nav.dart'; import 'package:hub/shared/utils/limited_text_size.dart'; -class OptModalWidget extends StatefulWidget { +class AccessFilterModel extends FlutterFlowModel { + /// State fields for stateful widgets in this component. + + // State field(s) for TextField widget. + FocusNode? textFieldFocusNode; + TextEditingController? textController; + String? Function(BuildContext, String?)? textControllerValidator; + // State field(s) for Checkbox widget. + bool? checkboxValue1; + // State field(s) for Checkbox widget. + bool? checkboxValue2; + // State field(s) for CheckboxGroup widget. + 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 AccessFilter extends StatefulWidget { final String defaultPersonType; final String defaultAccessType; - const OptModalWidget({ + const AccessFilter({ super.key, this.defaultPersonType = '.*', this.defaultAccessType = '.*', }); @override - _OptModalWidgetState createState() => _OptModalWidgetState(); + _AccessFilterState createState() => _AccessFilterState(); } -class _OptModalWidgetState extends State { - late OptModalModel _model; +class _AccessFilterState extends State { + late AccessFilterModel _model; late Map selected; final List> personTypeOptions = [ @@ -39,7 +65,7 @@ class _OptModalWidgetState extends State { void initState() { super.initState(); - _model = createModel(context, () => OptModalModel()); + _model = createModel(context, () => AccessFilterModel()); _model.textController ??= TextEditingController(); _model.textFieldFocusNode ??= FocusNode(); @@ -194,8 +220,7 @@ class _OptModalWidgetState extends State { fontSize: LimitedFontSizeUtil.getHeaderFontSize(context), letterSpacing: 0.0, fontWeight: FontWeight.bold, - useGoogleFonts: - GoogleFonts.asMap().containsKey(FlutterFlowTheme.of(context).headlineMediumFamily), + useGoogleFonts: GoogleFonts.asMap().containsKey(FlutterFlowTheme.of(context).headlineMediumFamily), ), ), ), @@ -227,8 +252,7 @@ class _OptModalWidgetState extends State { fontSize: LimitedFontSizeUtil.getInputFontSize(context), letterSpacing: 0.0, fontWeight: FontWeight.bold, - useGoogleFonts: - GoogleFonts.asMap().containsKey(FlutterFlowTheme.of(context).bodyMediumFamily), + 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..d48e5262 --- /dev/null +++ b/lib/features/history/presentation/widgets/index.dart @@ -0,0 +1,2 @@ +export 'access_filter_modal.dart'; +export 'provisional_filter_modal.dart'; diff --git a/lib/features/history/presentation/widgets/provisional_filter_modal.dart b/lib/features/history/presentation/widgets/provisional_filter_modal.dart new file mode 100644 index 00000000..c06612ab --- /dev/null +++ b/lib/features/history/presentation/widgets/provisional_filter_modal.dart @@ -0,0 +1,235 @@ +import 'dart:developer'; + +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 FilterModel 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 FilterWidget extends StatefulWidget { + final Map defaultSelections; + final Map>> filterOptions; + final Map filterTitles; + + const FilterWidget({ + super.key, + required this.defaultSelections, + required this.filterOptions, + required this.filterTitles, + }); + + @override + _FilterWidgetState createState() => _FilterWidgetState(); +} + +class _FilterWidgetState extends State { + late FilterModel _model; + late Map selected; + + @override + void setState(VoidCallback callback) { + super.setState(callback); + _model.onUpdate(); + } + + @override + void initState() { + super.initState(); + + _model = createModel(context, () => FilterModel()); + + _model.textController ??= TextEditingController(); + _model.textFieldFocusNode ??= FocusNode(); + + selected = Map.from(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]!; + }); + setState(() { + // Update the state with the new filter result + selected = filterResult; + }); + 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] = option['value']; + } + } else { + selected[key] = ''; + } + }); + }, + 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: screenWidth, + 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).getVariableText( + ptText: 'Filtros', + enText: 'Filters', + ), + 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).getVariableText( + ptText: 'Aplicar', + enText: 'Apply', + ), + 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/home/presentation/pages/home_page.dart b/lib/features/home/presentation/pages/home_page.dart index 350ae9a2..64442d3d 100644 --- a/lib/features/home/presentation/pages/home_page.dart +++ b/lib/features/home/presentation/pages/home_page.dart @@ -1,16 +1,14 @@ - import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:hub/features/home/presentation/widgets/drawer_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/components/molecules/drawer/index.dart'; import 'package:hub/shared/components/molecules/locals/index.dart'; import 'package:hub/shared/components/molecules/menu/index.dart'; - class HomePageWidget extends StatefulWidget { const HomePageWidget(this.update, {super.key}); final Future Function(BuildContext context)? update; @@ -29,8 +27,8 @@ class _HomePageWidgetState extends State with WidgetsBindingObse WidgetsBinding.instance.addPostFrameCallback((_) async { await LocalsRepositoryImpl().check(context); if (widget.update != null) { - await widget.update!(context); - } + await widget.update!(context); + } }); } @@ -40,7 +38,6 @@ class _HomePageWidgetState extends State with WidgetsBindingObse super.dispose(); } - @override Widget build(BuildContext context) { return Builder( @@ -49,13 +46,13 @@ class _HomePageWidgetState extends State with WidgetsBindingObse // context.read().add(LocalProfileEvent()); // context.read().add(MenuEvent()); LocalsRepositoryImpl.license.add(true); - + return Scaffold( key: scaffoldKey, backgroundColor: FlutterFlowTheme.of(context).primaryBackground, drawerEnableOpenDragGesture: true, drawerDragStartBehavior: DragStartBehavior.start, - drawer: CustomDrawer(), + drawer: DrawerWidget(), appBar: buildAppBar(context), body: buildPage(context), ); diff --git a/lib/shared/components/molecules/drawer/drawer_widget.dart b/lib/features/home/presentation/widgets/drawer_widget.dart similarity index 98% rename from lib/shared/components/molecules/drawer/drawer_widget.dart rename to lib/features/home/presentation/widgets/drawer_widget.dart index 0eac540d..f221d183 100644 --- a/lib/shared/components/molecules/drawer/drawer_widget.dart +++ b/lib/features/home/presentation/widgets/drawer_widget.dart @@ -9,8 +9,8 @@ import 'package:hub/shared/components/molecules/locals/index.dart'; import 'package:hub/shared/components/molecules/menu/index.dart'; import 'package:hub/shared/components/molecules/modules/index.dart'; -class CustomDrawer extends StatelessWidget { - const CustomDrawer({super.key}); +class DrawerWidget extends StatelessWidget { + const DrawerWidget({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/flutter_flow/nav/nav.dart b/lib/flutter_flow/nav/nav.dart index 25209384..8006324a 100644 --- a/lib/flutter_flow/nav/nav.dart +++ b/lib/flutter_flow/nav/nav.dart @@ -3,12 +3,13 @@ import 'dart:developer'; import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hub/backend/schema/util/schema_util.dart'; +import 'package:hub/features/history/index.dart'; import 'package:hub/features/home/index.dart'; import 'package:hub/features/property/index.dart'; import 'package:hub/flutter_flow/flutter_flow_util.dart'; import 'package:hub/flutter_flow/nav/nav.dart'; -import 'package:hub/pages/acess_history_page/acess_history_page_widget.dart'; import 'package:hub/pages/delivery_schedule_page/delivery_schedule_widget.dart'; import 'package:hub/pages/fast_pass_page/fast_pass_page_widget.dart'; import 'package:hub/pages/forgot_password_page/forgot_password_screen.dart'; @@ -32,6 +33,11 @@ import 'package:hub/pages/vehicles_on_the_property/vehicles_on_the_property.dart import 'package:hub/pages/visits_on_the_property/visits_on_the_property_screen.dart'; import 'package:hub/pages/welcome_page/welcome_page_widget.dart'; import 'package:hub/shared/components/molecules/locals/data/repositories/locals_repository_impl.dart'; +import 'package:hub/shared/components/molecules/locals/presentation/blocs/local_profile_bloc.dart'; +import 'package:hub/shared/components/molecules/menu/domain/entities/menu_item.dart'; +import 'package:hub/shared/components/molecules/menu/presentation/blocs/menu_bloc.dart'; +import 'package:hub/shared/components/molecules/menu/presentation/mappers/menu_entry.dart'; +import 'package:hub/shared/components/molecules/menu/presentation/widgets/menu_view/menu_list_view.dart'; import 'package:hub/shared/helpers/storage/base_storage.dart'; import 'package:hub/shared/helpers/storage/storage_helper.dart'; import 'package:hub/shared/utils/dialog_util.dart'; @@ -92,7 +98,26 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) { final bool haveDevUUID = (await StorageHelper().get(KeychainStorageKey.devUUID.value))?.isNotEmpty ?? false; if (isLogged && haveDevUUID && haveUserUUID) { - return haveLocal ? HomePageWidget(LocalsRepositoryImpl().update) : const ReceptionPageWidget(); + return haveLocal + ? MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => MenuBloc( + style: MenuView.list_grid, + item: EnumMenuItem.button, + entries: MenuEntry.getEntriesByType(MenuEntryType.Home), + )..add(MenuEvent()), + ), + BlocProvider( + create: (context) => HomeBloc()..add(HomeEvent()), + ), + BlocProvider( + create: (context) => LocalProfileBloc()..add(LocalProfileEvent()), + ), + ], + child: HomePageWidget(key: UniqueKey(), LocalsRepositoryImpl().update), + ) + : const ReceptionPageWidget(); } else { return const WelcomePageWidget(); } @@ -123,11 +148,30 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) { token: token, ); }), - FFRoute(name: 'homePage', path: '/homePage', builder: (context, params) { - final Future Function(BuildContext context)? update = params.getParam('update', ParamType.Function); - return HomePageWidget(key: UniqueKey(), update); - - }), + FFRoute( + name: 'homePage', + path: '/homePage', + builder: (context, params) { + final Future Function(BuildContext context)? update = params.getParam('update', ParamType.Function); + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (context) => MenuBloc( + style: MenuView.list_grid, + item: EnumMenuItem.button, + entries: MenuEntry.getEntriesByType(MenuEntryType.Home), + )..add(MenuEvent()), + ), + BlocProvider( + create: (context) => HomeBloc()..add(HomeEvent()), + ), + BlocProvider( + create: (context) => LocalProfileBloc()..add(LocalProfileEvent()), + ), + ], + child: HomePageWidget(key: UniqueKey(), update), + ); + }), 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()), @@ -136,7 +180,7 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) { FFRoute(name: 'scheduleCompleteVisitPage', path: '/scheduleCompleteVisitPage', builder: (context, params) => const ScheduleCompleteVisitPageWidget()), FFRoute(name: 'deliverySchedule', path: '/deliverySchedule', builder: (context, params) => const DeliverySchedule()), FFRoute(name: 'provisionalSchedule', path: '/provisionalSchedule', builder: (context, params) => const ProvisionalSchedule()), - FFRoute(name: 'fastPassPage', path: '/fastPassPage', builder: (context, params) => /*const*/ FastPassPageWidget()), + FFRoute(name: 'fastPassPage', path: '/fastPassPage', builder: (context, params) => FastPassPageWidget()), FFRoute(name: 'preferencesSettings', path: '/preferencesSettings', builder: (context, params) => PreferencesPageWidget()), FFRoute(name: 'aboutProperty', path: '/aboutProperty', builder: (context, params) => AboutPropertyPage()), FFRoute(name: 'residentsOnThePropertyPage', path: '/residentsOnThePropertyPage', builder: (context, params) => ResidentsOnTheProperty()), @@ -146,6 +190,7 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) { name: 'acessHistoryPage', path: '/acessHistoryPage', builder: (context, params) => AccessHistoryScreen(opt: const {'personType': '.*', 'accessType': '.*', 'search': '.*'})), + FFRoute(name: 'provisionalHistoryPage', path: '/provisionalHistoryPage', builder: (context, params) => ProvisionalHistoryPage()), FFRoute(name: 'liberationHistory', path: '/liberationHistory', builder: (context, params) => const LiberationHistoryWidget()), FFRoute(name: 'signInPage', path: '/signInPage', builder: (context, params) => const SignInPageWidget()), FFRoute(name: 'signUpPage', path: '/signUpPage', builder: (context, params) => const SignUpPageWidget()), diff --git a/lib/main.dart b/lib/main.dart index 7ada3253..00c6e724 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,25 +5,18 @@ import 'dart:io'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:hub/backend/notifications/notification_service.dart'; import 'package:hub/flutter_flow/flutter_flow_theme.dart'; import 'package:hub/flutter_flow/internationalization.dart'; import 'package:hub/flutter_flow/nav/nav.dart'; -import 'package:hub/shared/components/molecules/locals/index.dart'; import 'package:hub/shared/helpers/storage/base_storage.dart'; import 'package:hub/shared/helpers/storage/storage_helper.dart'; import 'package:hub/shared/services/deeplink/deep_link_service.dart'; import 'package:responsive_framework/responsive_framework.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:hub/shared/extensions/string_extensions.dart'; -import 'backend/notifications/firebase_messaging_service.dart'; -import 'features/home/index.dart'; import 'initialization.dart'; -import 'shared/components/molecules/menu/index.dart'; -import 'shared/components/molecules/modules/index.dart'; final GlobalKey navigatorKey = GlobalKey(); @@ -60,7 +53,7 @@ class App extends StatefulWidget { static _AppState of(BuildContext context) => context.findAncestorStateOfType<_AppState>()!; } -class _AppState extends State { +class _AppState extends State { Locale? _locale = FFLocalizations.getStoredLocale(); ThemeMode _themeMode = FlutterFlowTheme.themeMode; late AppStateNotifier _appStateNotifier; @@ -169,7 +162,7 @@ class _AppState extends State { @override void initState() { super.initState(); - + FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true); _appStateNotifier = AppStateNotifier.instance; _router = createRouter(_appStateNotifier); @@ -187,38 +180,19 @@ class _AppState extends State { super.dispose(); } - @override Widget build(BuildContext context) { - return MultiBlocProvider( - providers: [ - BlocProvider( - create: (context) => MenuBloc( - style: MenuView.list_grid, - item: EnumMenuItem.button, - entries: MenuEntry.getEntriesByType(MenuEntryType.Home), - )..add(MenuEvent()), - ), - BlocProvider( - create: (context) => HomeBloc()..add(HomeEvent()), - ), - BlocProvider( - create: (context) => LocalProfileBloc()..add(LocalProfileEvent()), - ), - ], - child: MaterialApp.router( - key: navigatorKey, - title: 'FRE ACCESS HUB', - builder: builder, - localizationsDelegates: localizationsDelegates, - locale: _locale, - supportedLocales: supportedLocales, - theme: _theme, - darkTheme: _darkTheme, - themeMode: _themeMode, - routerConfig: _router, - ), + return MaterialApp.router( + key: navigatorKey, + title: 'FRE ACCESS HUB', + builder: builder, + localizationsDelegates: localizationsDelegates, + locale: _locale, + supportedLocales: supportedLocales, + theme: _theme, + darkTheme: _darkTheme, + themeMode: _themeMode, + routerConfig: _router, ); } - } diff --git a/lib/shared/components/molecules/drawer/index.dart b/lib/shared/components/molecules/drawer/index.dart deleted file mode 100644 index bbb9bc10..00000000 --- a/lib/shared/components/molecules/drawer/index.dart +++ /dev/null @@ -1 +0,0 @@ -export 'drawer_widget.dart'; diff --git a/lib/shared/components/molecules/menu/presentation/mappers/menu_entry.dart b/lib/shared/components/molecules/menu/presentation/mappers/menu_entry.dart index 02b59717..a206d279 100644 --- a/lib/shared/components/molecules/menu/presentation/mappers/menu_entry.dart +++ b/lib/shared/components/molecules/menu/presentation/mappers/menu_entry.dart @@ -61,6 +61,16 @@ class MenuEntry implements BaseModule { route: '/scheduleCompleteVisitPage', types: [MenuEntryType.Home, MenuEntryType.Drawer], ), + MenuEntry( + key: 'FRE-HUB-PROVISIONAL-HISTORY', + icon: Icons.person_search_outlined, + name: FFLocalizations.of(navigatorKey.currentContext!).getVariableText( + ptText: 'Consultar Agendas', + enText: 'Provisional History', + ), + route: '/provisionalHistoryPage', + types: [MenuEntryType.Home, MenuEntryType.Drawer], + ), MenuEntry( key: 'FRE-HUB-RESIDENTS', icon: Icons.groups, @@ -144,10 +154,7 @@ class MenuEntry implements BaseModule { MenuEntry( key: 'FRE-HUB-PETS', icon: Icons.pets, - name: FFLocalizations.of(navigatorKey.currentContext!).getVariableText( - ptText: 'Cadastrar Pets', - enText: 'Pets Register' - ), + name: FFLocalizations.of(navigatorKey.currentContext!).getVariableText(ptText: 'Cadastrar Pets', enText: 'Pets Register'), route: '/petsPage', types: [MenuEntryType.Home, MenuEntryType.Drawer], ), @@ -162,14 +169,14 @@ class MenuEntry implements BaseModule { types: [MenuEntryType.Home, MenuEntryType.Drawer], ), MenuEntry( - key: 'FRE-HUB-LIBERATIONS', - icon: Icons.how_to_reg_outlined, - name: FFLocalizations.of(navigatorKey.currentContext!).getVariableText( - ptText: 'Consultar Liberações', - enText: 'Liberations History', - ), - route: '/liberationHistory', - types: [MenuEntryType.Home, MenuEntryType.Drawer], + key: 'FRE-HUB-LIBERATIONS', + icon: Icons.how_to_reg_outlined, + name: FFLocalizations.of(navigatorKey.currentContext!).getVariableText( + ptText: 'Consultar Liberações', + enText: 'Liberations History', + ), + route: '/liberationHistory', + types: [MenuEntryType.Home, MenuEntryType.Drawer], ), MenuEntry( key: 'FRE-HUB-MESSAGES', @@ -191,7 +198,6 @@ class MenuEntry implements BaseModule { route: '/aboutProperty', types: [MenuEntryType.Home, MenuEntryType.Drawer], ), - MenuEntry( key: 'FRE-HUB-PEOPLE', icon: Icons.groups, @@ -227,7 +233,4 @@ class MenuEntry implements BaseModule { static List getEntriesByType(MenuEntryType type) { return entries.where((entry) => entry.types.contains(type)).toList(); } - } - - diff --git a/lib/shared/components/molecules/modules/domain/entities/license.dart b/lib/shared/components/molecules/modules/domain/entities/license.dart index d643e322..bd90d50d 100644 --- a/lib/shared/components/molecules/modules/domain/entities/license.dart +++ b/lib/shared/components/molecules/modules/domain/entities/license.dart @@ -1,12 +1,9 @@ - - import 'package:hub/shared/extensions/string_extensions.dart'; import 'package:hub/shared/helpers/storage/base_storage.dart'; import 'package:hub/shared/helpers/storage/storage_helper.dart'; import 'module.dart'; - enum LicenseKeys { messages('FRE-HUB-MESSAGES'), liberations('FRE-HUB-LIBERATIONS'), @@ -20,6 +17,7 @@ enum LicenseKeys { completeSchedule('FRE-HUB-COMPLETE-SCHEDULE'), providerSchedule('FRE-HUB-AGE-PROV-PREST'), deliverySchedule('FRE-HUB-AGE-PROV-DELIVERY'), + provisionalHistory('FRE-HUB-PROVISIONAL-HISTORY'), property('FRE-HUB-ABOUT-PROPERTY'), fastPass('FRE-HUB-FASTPASS'), visitors('FRE-HUB-VISITORS'), @@ -29,14 +27,10 @@ enum LicenseKeys { settings('FRE-HUB-SETTINGS'), logout('FRE-HUB-LOGOUT'); - final String value; const LicenseKeys(this.value); } - - - class License { final List modules; @@ -44,10 +38,9 @@ class License { this.modules, ); - static Future _processWithoutModule(LicenseKeys key) async { - switch(key) { - case LicenseKeys.reservations: + switch (key) { + case LicenseKeys.reservations: return await _precessWpp(); case LicenseKeys.orders: return await _precessWpp(); @@ -68,171 +61,188 @@ class License { static Future _precessWpp() async { final bool whatsapp = await StorageHelper().get(KeychainStorageKey.whatsapp.value).then((v) => v.toBoolean()); - if (whatsapp) return ModuleStatus.active.key; - else return ModuleStatus.inactive.key; + if (whatsapp) + return ModuleStatus.active.key; + else + return ModuleStatus.inactive.key; } + static Future _processProvisional() async { final bool provisional = await StorageHelper().get(KeychainStorageKey.provisional.value).then((v) => v.toBoolean()); - if (provisional) return ModuleStatus.active.key; - else return ModuleStatus.inactive.key; + if (provisional) + return ModuleStatus.active.key; + else + return ModuleStatus.inactive.key; } static Future _processPets() async { final bool pets = await StorageHelper().get(KeychainStorageKey.pets.value).then((v) => v.toBoolean()); - if (pets) return ModuleStatus.active.key; - else return ModuleStatus.inactive.key; + if (pets) + return ModuleStatus.active.key; + else + return ModuleStatus.inactive.key; } - static getKeyByModule(String s) { + static getKeyByModule(String s) {} - } - /// Returns a [License] object with the modules and their status. /// @param isNewVersion: Indica que é sistema novo que possui modularização. static Future getLicense(bool isNewVersionWithModule) async { return License([ - Module( - key: LicenseKeys.messages.value, - display: ModuleStatus.active.key, - expirationDate: '', - startDate: '', - quantity: 0, - ), - Module( - key: LicenseKeys.liberations.value, - display: ModuleStatus.active.key, - expirationDate: '', - startDate: '', - quantity: 0, - ), - Module( - key: LicenseKeys.reservations.value, - display: await _processWithoutModule(LicenseKeys.reservations), - expirationDate: '', - startDate: '', - quantity: 0, - ), - Module( - key: LicenseKeys.access.value, - display: ModuleStatus.active.key, - expirationDate: '', - startDate: '', - quantity: 0, - ), - Module( - key: LicenseKeys.openedVisits.value, - display: isNewVersionWithModule ? ModuleStatus.active.key : ModuleStatus.inactive.key, - expirationDate: '', - startDate: '', - quantity: 0, - ), - Module( - key: LicenseKeys.vehicles.value, - display: isNewVersionWithModule ? ModuleStatus.active.key : ModuleStatus.inactive.key, - expirationDate: '', - startDate: '', - quantity: 0, - ), - Module( - key: LicenseKeys.residents.value, - display: isNewVersionWithModule ? ModuleStatus.active.key : ModuleStatus.inactive.key, - expirationDate: '', - startDate: '', - quantity: 0, - ), - Module( - key: LicenseKeys.pets.value, - display: await _processWithoutModule(LicenseKeys.pets), - expirationDate: '', - startDate: '', - quantity: 0, - ), - Module( - key: LicenseKeys.orders.value, - display: await _processWithoutModule(LicenseKeys.orders), - expirationDate: '', - startDate: '', - quantity: 0, - ), - Module( - key: LicenseKeys.completeSchedule.value, - display: ModuleStatus.active.key, - expirationDate: '', - startDate: '', - quantity: 0, - ), - Module( - key: LicenseKeys.providerSchedule.value, - display: await _processWithoutModule(LicenseKeys.providerSchedule), - expirationDate: '', - startDate: '', - quantity: 0, - ), - Module( - key: LicenseKeys.deliverySchedule.value, - display: await _processWithoutModule(LicenseKeys.deliverySchedule), - expirationDate: '', - startDate: '', - quantity: 0, - ), - Module( - key: LicenseKeys.fastPass.value, - display: await _processWithoutModule(LicenseKeys.fastPass), - expirationDate: '', - startDate: '', - quantity: 0, - ), - Module( - key: LicenseKeys.qrCode.value, - display: ModuleStatus.active.key, - expirationDate: '', - startDate: '', - quantity: 0, - ), - Module( - key: LicenseKeys.visitors.value, - display: ModuleStatus.active.key, - expirationDate: '', - startDate: '', - quantity: 0, - ), - Module( - key: LicenseKeys.property.value, - display: isNewVersionWithModule ? ModuleStatus.active.key : ModuleStatus.inactive.key, - expirationDate: '', - startDate: '', - quantity: 0, - ), - Module( - key: LicenseKeys.people.value, - display: isNewVersionWithModule ? ModuleStatus.inactive.key : ModuleStatus.active.key, - expirationDate: '', - startDate: '', - quantity: 0, - ), - Module( - key: LicenseKeys.petsHistory.value, - display: await _processWithoutModule(LicenseKeys.pets), - expirationDate: '', - startDate: '', - quantity: 0, - ), - Module( - key: LicenseKeys.settings.value, - display: ModuleStatus.active.key, - expirationDate: '', - startDate: '', - quantity: 0, - ), - Module( - key: LicenseKeys.logout.value, - display: ModuleStatus.active.key, - expirationDate: '', - startDate: '', - quantity: 0, - ), - + Module( + key: LicenseKeys.messages.value, + display: ModuleStatus.active.key, + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.provisionalHistory.value, + display: ModuleStatus.inactive.key, + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.liberations.value, + display: ModuleStatus.active.key, + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.reservations.value, + display: await _processWithoutModule(LicenseKeys.reservations), + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.access.value, + display: ModuleStatus.active.key, + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.access.value, + display: ModuleStatus.active.key, + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.openedVisits.value, + display: isNewVersionWithModule ? ModuleStatus.active.key : ModuleStatus.inactive.key, + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.vehicles.value, + display: isNewVersionWithModule ? ModuleStatus.active.key : ModuleStatus.inactive.key, + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.residents.value, + display: isNewVersionWithModule ? ModuleStatus.active.key : ModuleStatus.inactive.key, + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.pets.value, + display: await _processWithoutModule(LicenseKeys.pets), + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.orders.value, + display: await _processWithoutModule(LicenseKeys.orders), + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.completeSchedule.value, + display: ModuleStatus.active.key, + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.providerSchedule.value, + display: await _processWithoutModule(LicenseKeys.providerSchedule), + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.deliverySchedule.value, + display: await _processWithoutModule(LicenseKeys.deliverySchedule), + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.fastPass.value, + display: await _processWithoutModule(LicenseKeys.fastPass), + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.qrCode.value, + display: ModuleStatus.active.key, + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.visitors.value, + display: ModuleStatus.active.key, + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.property.value, + display: isNewVersionWithModule ? ModuleStatus.active.key : ModuleStatus.inactive.key, + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.people.value, + display: isNewVersionWithModule ? ModuleStatus.inactive.key : ModuleStatus.active.key, + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.petsHistory.value, + display: await _processWithoutModule(LicenseKeys.pets), + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.settings.value, + display: ModuleStatus.active.key, + expirationDate: '', + startDate: '', + quantity: 0, + ), + Module( + key: LicenseKeys.logout.value, + display: ModuleStatus.active.key, + expirationDate: '', + startDate: '', + quantity: 0, + ), ]); } } - diff --git a/lib/shared/components/templates/history/history_page_template.dart b/lib/shared/components/templates/history/history_page_template.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/shared/components/templates/home/home_page_template.dart b/lib/shared/components/templates/home/home_page_template.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/shared/components/templates/schedule/schedule_page_template.dart b/lib/shared/components/templates/schedule/schedule_page_template.dart new file mode 100644 index 00000000..e69de29b diff --git a/lib/shared/utils/snackbar_util.dart b/lib/shared/utils/snackbar_util.dart index 21c43566..1b8ae0d0 100644 --- a/lib/shared/utils/snackbar_util.dart +++ b/lib/shared/utils/snackbar_util.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:hub/flutter_flow/internationalization.dart'; import 'package:hub/shared/utils/limited_text_size.dart'; import '../../flutter_flow/flutter_flow_theme.dart'; @@ -22,4 +23,20 @@ class SnackBarUtil { ), ); } + + static 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, + ), + ); + } } diff --git a/pubspec.lock b/pubspec.lock index 3dc25f42..db0188e7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -386,10 +386,10 @@ packages: dependency: transitive description: name: file - sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "7.0.1" + version: "7.0.0" file_picker: dependency: "direct main" description: @@ -555,6 +555,11 @@ packages: url: "https://pub.dev" source: hosted version: "3.4.1" + flutter_driver: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" flutter_inappwebview: dependency: "direct main" description: @@ -786,6 +791,11 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" + fuchsia_remote_debug_protocol: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" glob: dependency: transitive description: @@ -938,6 +948,11 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.1+1" + integration_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" intl: dependency: "direct main" description: @@ -1138,6 +1153,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + mockito: + dependency: "direct dev" + description: + name: mockito + sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917" + url: "https://pub.dev" + source: hosted + version: "5.4.4" nested: dependency: transitive description: @@ -1310,10 +1333,10 @@ packages: dependency: transitive description: name: platform - sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" url: "https://pub.dev" source: hosted - version: "3.1.6" + version: "3.1.5" plugin_platform_interface: dependency: "direct main" description: @@ -1330,6 +1353,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" + process: + dependency: transitive + description: + name: process + sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" + url: "https://pub.dev" + source: hosted + version: "5.0.2" provider: dependency: "direct main" description: @@ -1607,6 +1638,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + sync_http: + dependency: transitive + description: + name: sync_http + sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" + url: "https://pub.dev" + source: hosted + version: "0.3.1" synchronized: dependency: "direct main" description: @@ -1799,6 +1838,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" + webdriver: + dependency: transitive + description: + name: webdriver + sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e" + url: "https://pub.dev" + source: hosted + version: "3.0.3" webview_flutter: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 9d2540c4..0448cb01 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -114,11 +114,10 @@ dev_dependencies: flutter_lints: ^5.0.0 image: ^4.3.0 lints: ^5.0.0 - # build_runner: ^2.4.13 - # mockito: ^5.4.4 - # integration_test: - # sdk: flutter - + # build_runner: ^2.4.13 + mockito: ^5.4.4 + integration_test: + sdk: flutter flutter_test: sdk: flutter build_runner: ^2.4.13 diff --git a/test/integration_test/app_test.dart b/test/integration_test/app_test.dart deleted file mode 100644 index f5a03e8c..00000000 --- a/test/integration_test/app_test.dart +++ /dev/null @@ -1,210 +0,0 @@ -// import 'package:flutter/material.dart'; -// import 'package:flutter/services.dart'; -// import 'package:flutter_test/flutter_test.dart'; -// import 'package:hub/flutter_flow/random_data_util.dart'; -// import 'package:hub/main.dart'; -// import 'package:hub/shared/helpers/storage/base_storage.dart'; -// import 'package:hub/shared/helpers/storage/storage_helper.dart'; -// import 'package:integration_test/integration_test.dart'; - -// late WidgetTester widget; - -// void main() { -// IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - -// group('Initialization', () { -// group('Navigation', () { -// setUpAll(() async => -// await initializeApp().then((_) async => await StorageHelper().s(SecureStorageKey.isLogged.value, 'false'))); -// testWidgets('Test Welcome', (WidgetTester tester) async { -// widget = tester; -// await _testWelcome(); -// }); -// }); - -// group('Terms of Use', () {}); -// }); -// group('Authentication', () { -// group('Sign in', () { -// setUpAll(() async => -// await initializeApp().then((_) async => await StorageHelper().s(SecureStorageKey.isLogged.value, 'false'))); -// testWidgets('Test Sign In', (WidgetTester tester) async { -// widget = tester; -// await _testSignIn(); -// }); -// }); -// group('Sign up', () { -// setUpAll(() async => -// await initializeApp().then((_) async => await StorageHelper().s(SecureStorageKey.isLogged.value, 'false'))); -// testWidgets('Test Sign Up', (WidgetTester tester) async { -// widget = tester; -// await _testSignUp(); -// }); -// }); -// group('Sign Out', () { -// // Add tests for Sign Out here -// }); -// group('Forgot Password', () { -// // setUpAll(() async => await initializeApp().then((_) => StorageUtil().isLogged = false)); -// // testWidgets('Test Forgot Password', (WidgetTester tester) async { -// // widget = tester; -// // await _testForgotPassword(); -// // }); -// }); -// }); -// group('Localization', () { -// // Add tests for Localization here -// }); -// group('Networking', () { -// // Add tests for Networking here -// }); -// group('Functionality', () { -// // Add tests for Functionality here -// }); -// group('Usability', () { -// // Add tests for Usability here -// }); -// group('Performance', () { -// // Add tests for Performance here -// }); -// group('Security', () { -// // Add tests for Security here -// }); -// group('Accessibility', () { -// // Add tests for Accessibility here -// }); -// group('Compatibility', () { -// // Add tests for Compatibility here -// }); -// group('Internationalization', () { -// // Add tests for Internationalization here -// }); -// } - -// Future _testWelcome() async { -// await widget.pumpWidget(const App()); -// await widget.pumpAndSettle(); -// await _navigateToSignIn(); -// await _navigateToSignUp(); -// await widget.pumpAndSettle(); -// await widget.pumpWidget(const App()); -// await widget.pumpAndSettle(); -// await _navigateToSignUp(); -// await _navigateToSignIn(); -// await widget.pumpAndSettle(); -// } - -// Future _testSignIn() async { -// await widget.pumpWidget(const App()); -// await _navigateToSignIn(); -// await _auth({'emailTextFormField': 'erro@exemplo.com', 'passwordTextFormField': '12345678'}); -// await _auth({'emailTextFormField': 'email_app@exemplo.com', 'passwordTextFormField': '12345678'}); -// } - -// Future _testSignUp() async { -// await widget.pumpWidget(const App()); -// await _navigateToSignUp(); - -// var credentials = { -// 'nameTextFormField': 'app', -// 'emailTextFormField': 'email_app@exemplo.com', -// 'passwordTextFormField': '12345678' -// }; -// await _auth(credentials); - -// var name = randomString(7, 7, true, true, true); -// var email = '$name@example.com'; -// var password = '12345678'; -// credentials = {'nameTextFormField': name, 'emailTextFormField': email, 'passwordTextFormField': password}; -// await _navigateToSignUp(); -// await _auth(credentials); -// credentials = {'emailTextFormField': email, 'passwordTextFormField': password}; -// await _auth(credentials); -// } - -// Future _testForgotPassword() async { -// await widget.pumpWidget(const App()); -// await _navigateToSignIn(); -// await _recoveryPassword(); - -// var addr = randomString(5, 5, true, true, true); -// var credentials = {'recoveryTextFormField': '$addr@exemple.com'}; -// await _send(credentials); - -// await Future.delayed(const Duration(seconds: 2)); - -// await _recoveryPassword(); -// credentials = {'recoveryTextFormField': 'email_app@exemple.com'}; -// await _send(credentials); -// } - -// Future _recoveryPassword() async { -// await widget.pumpAndSettle(); -// final Finder forgotPassword = find.byKey(const ValueKey('ForgotPassword')); -// if (forgotPassword.evaluate().isNotEmpty) await widget.tap(forgotPassword); -// await widget.ensureVisible(forgotPassword); -// await widget.pumpAndSettle(); -// } - -// Future _navigateBackUsingSystemGesture() async => -// IntegrationTestWidgetsFlutterBinding.instance.keyboard.isLogicalKeyPressed(LogicalKeyboardKey.escape); -// Future _navigateToSignUp() async { -// await widget.pumpAndSettle(); -// final Finder navToSignUp = find.byKey(const ValueKey('toggleSignUpPage')); -// if (navToSignUp.evaluate().isNotEmpty) { -// await widget.tap(navToSignUp); -// await widget.pumpAndSettle(); -// } -// } - -// Future _navigateToSignIn() async { -// await widget.pumpAndSettle(); -// final Finder navToSignIn = find.byKey(const ValueKey('toggleSignInPage')); -// expect(navToSignIn, findsOneWidget); -// if (navToSignIn.evaluate().isNotEmpty) { -// await widget.tap(navToSignIn); -// await widget.pumpAndSettle(); -// } -// } - -// Future _auth(Map credentials) async { -// await _enterCredentials(credentials); -// await _submit('SubmitButtonWidget'); -// } - -// Future _send(Map credentials) async { -// await _enterCredentials(credentials); -// await _submit('SendButtonWidget'); -// } - -// Future _enterCredentials(Map credentials) async { -// await widget.pumpAndSettle(); -// for (var entry in credentials.entries) { -// final Finder field = find.byKey(ValueKey(entry.key)); -// await widget.pumpAndSettle(); -// expect(field, findsOneWidget); -// await widget.enterText(field, entry.value); -// await widget.pumpAndSettle(); -// } -// await widget.pumpAndSettle(); -// } - -// Future _submit(String key) async { -// await widget.pumpAndSettle(); -// final Finder submitButton = find.byKey(ValueKey(key)); -// await widget.pumpAndSettle(); -// if (submitButton.evaluate().isNotEmpty) { -// await widget.tap(submitButton); -// await widget.pumpAndSettle(); -// } - -// final Finder throwExceptionWidget = find.byKey(const ValueKey('ThrowExceptionWidget')); -// await widget.pumpAndSettle(); -// if (throwExceptionWidget.evaluate().isNotEmpty) { -// await widget.ensureVisible(throwExceptionWidget); -// await widget.tap(throwExceptionWidget); -// await widget.pumpAndSettle(); -// } else { -// await _navigateBackUsingSystemGesture(); -// } -// }