diff --git a/.tool-versions b/.tool-versions index fc05c635..3e6df7d4 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -gradle 7.6.1 +gradle 8.10.2 diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..3bfc4b44 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,29 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "name": "flutter-freaccesss-hub", + "request": "launch", + "type": "dart", + "args": [ + "--no-enable-impeller" + ] + }, + { + "name": "flutter-freaccesss-hub (profile mode)", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "flutter-freaccesss-hub (release mode)", + "request": "launch", + "type": "dart", + "flutterMode": "release" + } + ] +} \ No newline at end of file diff --git a/integration_test/app_test.dart b/integration_test/app_test.dart index 6267453a..0d832c65 100644 --- a/integration_test/app_test.dart +++ b/integration_test/app_test.dart @@ -7,12 +7,11 @@ import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:go_router/go_router.dart'; +import 'package:hub/components/atomic_components/shared_components_atoms/submit_button.dart'; import 'package:hub/components/molecular_components/throw_exception/throw_exception_widget.dart'; import 'package:hub/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart'; import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart'; -import 'package:hub/components/templates_components/forgot_password_template_component/forgot_password_template_component_widget.dart'; +import 'package:hub/components/templates_components/details_component/details_component_widget.dart'; import 'package:hub/features/backend/api_requests/index.dart'; import 'package:hub/features/local/index.dart'; import 'package:hub/features/menu/index.dart'; @@ -22,9 +21,9 @@ import 'package:hub/features/notification/index.dart'; import 'package:hub/features/storage/index.dart'; import 'package:hub/flutter_flow/index.dart' as ff; import 'package:hub/flutter_flow/index.dart'; -import 'package:hub/initialization.dart'; import 'package:hub/main.dart'; -import 'package:hub/pages/forgot_password_page/forgot_password_screen.dart'; +import 'package:hub/pages/vehicles_on_the_property/vehicles_on_the_property.dart'; +import 'package:integration_test/integration_test.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:flutter_web_plugins/url_strategy.dart'; import 'package:patrol/patrol.dart'; @@ -33,7 +32,6 @@ import 'app_test.dart'; import 'fuzzer/fuzzer.dart'; import 'package:patrol_finders/patrol_finders.dart'; -import 'package:integration_test/integration_test.dart'; export 'package:flutter_test/flutter_test.dart'; export 'package:patrol/patrol.dart'; @@ -62,6 +60,7 @@ part 'storage_test.dart'; part 'utils_test.dart'; part 'welcome_test.dart'; +part 'vehicle_test.dart'; late PatrolTester $; @@ -89,5 +88,9 @@ void main() { LocalsTest.setLocal(); LocalsTest.unlinkLocal(); - LocalsTest.attachLocal(); + + VehicleTest.vehiclePage(); + VehicleTest.historyScreen(); + VehicleTest.registerScreen(); + VehicleTest.updateScreen(); } diff --git a/integration_test/auth_test.dart b/integration_test/auth_test.dart index 4045cfc3..185ef039 100644 --- a/integration_test/auth_test.dart +++ b/integration_test/auth_test.dart @@ -36,7 +36,7 @@ class AuthenticationTest { Map concat( String username, String domain, String password) { return { - 'emailTextFormField': '${username}@${domain}.test', + 'emailTextFormField': '$username@$domain.test', 'passwordTextFormField': password, }; } @@ -144,7 +144,7 @@ class AuthenticationTest { String name, String username, String domain, String password) { return { 'nameTextFormField': name, - 'emailTextFormField': '${username}@${domain}.test', + 'emailTextFormField': '$username@$domain.test', 'passwordTextFormField': password, }; } diff --git a/integration_test/vehicle_test.dart b/integration_test/vehicle_test.dart new file mode 100644 index 00000000..c3c5c0fb --- /dev/null +++ b/integration_test/vehicle_test.dart @@ -0,0 +1,320 @@ +part of 'app_test.dart'; + +class VehicleTest { + static Future _initVehicleModule() async { + final vehicleParam = { + 'display': 'VISIVEL', + 'expirationDate': '', + 'startDate': '', + 'quantity': 0, + }; + final vehicleManagerParam = { + 'display': 'VISIVEL', + 'expirationDate': '', + 'startDate': '', + 'quantity': 0, + }; + await LicenseRepositoryImpl() + .setModule(LicenseKeys.vehicles.value, vehicleParam); + await LicenseRepositoryImpl() + .setModule(LicenseKeys.vehiclesManager.value, vehicleManagerParam); + } + + static Future vehiclePage() async { + patrolWidgetTest( + 'Vehicle Page', + (PatrolTester tester) async { + $ = tester; + $.tester.printToConsole('Vehicle Page'); + final PatrolFinder throwsException = $(Dialog).$(ThrowExceptionWidget); + + await _loggedWithMultiLocalsAccount(); + await _initVehicleModule(); + await $.pumpAndSettle(); + await $.pumpWidgetAndSettle(const App()); + + ff.navigatorKey.currentContext!.go('/vehiclesOnThePropertyPage'); + + final String title = MenuEntry.entries // + .where((entry) => entry.key == 'FRE-HUB-VEHICLES') // + .map((entry) => entry.name) + .first; + + final PatrolFinder appBar = await $(AppBar) // + .waitUntilExists(); + final PatrolFinder titleAppBar = await appBar // + .$(title) + .waitUntilVisible(); + expect(titleAppBar, findsOneWidget); + + final PatrolFinder tab1 = await $(#TabView_Tab1) // + .waitUntilExists(); + final PatrolFinder tab2 = await $(#TabView_Tab2) // + .waitUntilExists(); + + await tab2.tap(); + await Future.delayed(const Duration(milliseconds: 500)); + await tab1.tap(); + + final PatrolFinder listViewFinder = await $(VehicleHistoryScreen) // + .$(ListView) + .waitUntilVisible(); + + expect(listViewFinder, findsOneWidget); + + final PatrolFinder entriesFinder = await $(listViewFinder) + .$(CardItemTemplateComponentWidget) + .waitUntilVisible(); + + expect(entriesFinder, findsWidgets); + final int entriesCount = entriesFinder.evaluate().length; + await $.pumpAndSettle(); + + if (entriesCount > 0) + for (int i = 0; i < entriesCount; i++) { + await $(entriesFinder.at(i)).scrollTo(); + + await $(entriesFinder.at(i)) + .waitUntilVisible(timeout: const Duration(seconds: 1)) + .tap( + settleTimeout: const Duration(seconds: 1), + settlePolicy: SettlePolicy.noSettle, + ); + + await $.pumpAndSettle(duration: Duration(milliseconds: 500)); + final PatrolFinder detailsFinder = + await $(DetailsComponentWidget).waitUntilVisible(); + expect(detailsFinder, findsOneWidget); + + await _navigateBackUsingSystemGesture(); + + // await $.native.pressBack().then((_) => $.pumpAndSettle()); + } + }, + ); + patrolWidgetTest( + 'License', + (PatrolTester tester) async { + $ = tester; + $.tester.printToConsole('Vehicle Page'); + + await _loggedWithMultiLocalsAccount(); + await _initVehicleModule(); + await $.pumpAndSettle(); + + await $.pumpWidgetAndSettle(const App()); + + ff.navigatorKey.currentContext!.go('/vehiclesOnThePropertyPage'); + final String title = MenuEntry.entries // + .where((entry) => entry.key == 'FRE-HUB-VEHICLES') // + .map((entry) => entry.name) + .first; + + final PatrolFinder appBar = await $(AppBar) // + .waitUntilExists(); + final PatrolFinder titleAppBar = await appBar // + .$(title) + .waitUntilVisible(); + expect(titleAppBar, findsOneWidget); + + final PatrolFinder tab1 = await $(#TabView_Tab1) // + .waitUntilExists(); + final PatrolFinder tab2 = await $(#TabView_Tab2) // + .waitUntilExists(); + + await tab2.tap(); + await Future.delayed(const Duration(milliseconds: 500)); + await tab1.tap(); + + final PatrolFinder listViewFinder = await $(VehicleHistoryScreen) // + .$(ListView) + .waitUntilVisible(); + + expect(listViewFinder, findsOneWidget); + + final PatrolFinder entriesFinder = await $(listViewFinder) + .$(CardItemTemplateComponentWidget) + .waitUntilVisible(); + + expect(entriesFinder, findsWidgets); + await $.pumpAndSettle(); + await Future.delayed(const Duration(milliseconds: 1000)); + }, + ); + } + + static Future historyScreen() async { + patrolWidgetTest( + 'historyScreen', + (PatrolTester tester) async { + $ = tester; + $.tester.printToConsole('Vehicle Page'); + final PatrolFinder throwsException = $(Dialog).$(ThrowExceptionWidget); + + await _loggedWithMultiLocalsAccount(); + await _initVehicleModule(); + await $.pumpAndSettle(); + await $.pumpWidgetAndSettle(const App()); + + ff.navigatorKey.currentContext!.go('/vehiclesOnThePropertyPage'); + + final String title = MenuEntry.entries // + .where((entry) => entry.key == 'FRE-HUB-VEHICLES') // + .map((entry) => entry.name) + .first; + + final PatrolFinder appBar = await $(AppBar) // + .waitUntilExists(); + final PatrolFinder titleAppBar = await appBar // + .$(title) + .waitUntilVisible(); + expect(titleAppBar, findsOneWidget); + + final PatrolFinder listViewFinder = await $(VehicleHistoryScreen) // + .$(ListView) + .waitUntilVisible(); + + expect(listViewFinder, findsOneWidget); + + final PatrolFinder entriesFinder = await $(listViewFinder) + .$(CardItemTemplateComponentWidget) + .waitUntilVisible(); + + expect(entriesFinder, findsWidgets); + await $.pumpAndSettle(); + await $(entriesFinder.first) + .waitUntilVisible(timeout: const Duration(seconds: 1)) + .tap( + settleTimeout: const Duration(seconds: 1), + settlePolicy: SettlePolicy.noSettle, + ); + + await $.pumpAndSettle(duration: Duration(milliseconds: 500)); + final PatrolFinder detailsFinder = + await $(DetailsComponentWidget).waitUntilVisible(); + expect(detailsFinder, findsOneWidget); + + await _navigateBackUsingSystemGesture(); + + /// Iterable Test + // final int entriesCount = entriesFinder.evaluate().length; + // for (int i = 0; i < entriesCount; i++) { + // // await $(entriesFinder.at(i)).scrollTo(); + + // await $(entriesFinder.at(i)) + // .waitUntilVisible(timeout: const Duration(seconds: 1)) + // .tap( + // settleTimeout: const Duration(seconds: 1), + // settlePolicy: SettlePolicy.noSettle, + // ); + + // await $.pumpAndSettle(duration: Duration(milliseconds: 500)); + // final PatrolFinder detailsFinder = + // await $(DetailsComponentWidget).waitUntilVisible(); + // expect(detailsFinder, findsOneWidget); + + // await _navigateBackUsingSystemGesture(); + + // // await $.native.pressBack().then((_) => $.pumpAndSettle()); + // } + }, + ); + } + + static Future registerScreen() async { + patrolWidgetTest( + 'registerScreen', + (PatrolTester tester) async { + $ = tester; + $.tester.printToConsole('Vehicle Register Page'); + + await _loggedWithMultiLocalsAccount(); + await _initVehicleModule(); + await $.pumpAndSettle(); + await $.pumpWidgetAndSettle(const App()); + + ff.navigatorKey.currentContext!.go('/vehiclesOnThePropertyPage'); + + final PatrolFinder tab2 = await $(#TabView_Tab2) // + .waitUntilExists(); + + await tab2.tap(); + + final PatrolFinder licensePlateField = + await $(TextField).at(0).waitUntilVisible(); + final PatrolFinder modelField = + await $(TextField).at(1).waitUntilVisible(); + final PatrolFinder colorField = + await $(TextField).at(2).waitUntilVisible(); + final PatrolFinder submitButton = + await $(SubmitButtonUtil).waitUntilVisible(); + + await licensePlateField.enterText('ABC1234'); + await modelField.enterText('Voyage'); + await colorField.enterText('Black'); + await submitButton.tap(); + + await $.pumpAndSettle(); + final PatrolFinder successDialog = await $(Dialog).waitUntilVisible(); + expect(successDialog, findsOneWidget); + }, + ); + } + + static Future updateScreen() async { + patrolWidgetTest( + 'updateScreen', + (PatrolTester tester) async { + $ = tester; + $.tester.printToConsole('Vehicle Update Page'); + + await _loggedWithMultiLocalsAccount(); + await _initVehicleModule(); + await $.pumpAndSettle(); + await $.pumpWidgetAndSettle(const App()); + + ff.navigatorKey.currentContext!.go('/vehiclesOnThePropertyPage'); + + final PatrolFinder tab1 = await $(#TabView_Tab1) // + .waitUntilExists(); + + await tab1.tap(); + + final PatrolFinder listViewFinder = await $(VehicleHistoryScreen) // + .$(ListView) + .waitUntilVisible(); + + expect(listViewFinder, findsOneWidget); + + final PatrolFinder entriesFinder = await $(listViewFinder) + .$(CardItemTemplateComponentWidget) + .waitUntilVisible(); + + expect(entriesFinder, findsWidgets); + await $(entriesFinder.at(0)).tap(); + + final PatrolFinder editButton = + await $(FFButtonWidget).$('Edit').waitUntilVisible(); + await editButton.tap(); + + final PatrolFinder licensePlateField = + await $(TextField).at(0).waitUntilVisible(); + final PatrolFinder modelField = + await $(TextField).at(1).waitUntilVisible(); + final PatrolFinder colorField = + await $(TextField).at(2).waitUntilVisible(); + final PatrolFinder submitButton = + await $(SubmitButtonUtil).waitUntilVisible(); + + await licensePlateField.enterText('XYZ5678'); + await modelField.enterText('Fiesta'); + await colorField.enterText('Red'); + await submitButton.tap(); + + await $.pumpAndSettle(); + final PatrolFinder successDialog = await $(Dialog).waitUntilVisible(); + expect(successDialog, findsOneWidget); + }, + ); + } +} diff --git a/lib/components/atomic_components/shared_components_atoms/custom_input.dart b/lib/components/atomic_components/shared_components_atoms/custom_input.dart index cac159f4..cb02aba7 100644 --- a/lib/components/atomic_components/shared_components_atoms/custom_input.dart +++ b/lib/components/atomic_components/shared_components_atoms/custom_input.dart @@ -5,6 +5,15 @@ import 'package:google_fonts/google_fonts.dart'; import 'package:hub/flutter_flow/flutter_flow_theme.dart'; import 'package:hub/shared/utils/limited_text_size.dart'; +class UpperCaseTextFormatter extends TextInputFormatter { + @override + TextEditingValue formatEditUpdate( + TextEditingValue oldValue, TextEditingValue newValue) { + return TextEditingValue( + text: newValue.text.toUpperCase(), selection: newValue.selection); + } +} + // ignore: must_be_immutable class CustomInputUtil extends StatefulWidget { final TextEditingController? controller; @@ -20,22 +29,25 @@ class CustomInputUtil extends StatefulWidget { final String? Function(String?)? validator; final bool haveMaxLength; final void Function(String)? onChanged; + final List? inputFormatters; - CustomInputUtil( - {super.key, - this.controller, - required this.labelText, - required this.hintText, - required this.suffixIcon, - this.autoFocus = false, - required this.focusNode, - this.onChanged, - this.textInputAction = TextInputAction.next, - this.keyboardType = TextInputType.text, - this.maxLength = 80, - this.validator, - this.obscureText, - required this.haveMaxLength}); + CustomInputUtil({ + super.key, + this.controller, + required this.labelText, + required this.hintText, + required this.suffixIcon, + this.autoFocus = false, + required this.focusNode, + this.onChanged, + this.textInputAction = TextInputAction.next, + this.keyboardType = TextInputType.text, + this.maxLength = 80, + this.validator, + this.obscureText, + this.inputFormatters, + required this.haveMaxLength, + }); @override State createState() => _CustomInputUtilState(); @@ -152,6 +164,7 @@ class _CustomInputUtilState extends State { keyboardType: widget.keyboardType, inputFormatters: [ LengthLimitingTextInputFormatter(widget.maxLength), + if (widget.inputFormatters != null) ...widget.inputFormatters! ], ), ], diff --git a/lib/components/atomic_components/shared_components_atoms/tabview.dart b/lib/components/atomic_components/shared_components_atoms/tabview.dart index 8a62cbf3..30348b8d 100644 --- a/lib/components/atomic_components/shared_components_atoms/tabview.dart +++ b/lib/components/atomic_components/shared_components_atoms/tabview.dart @@ -10,7 +10,7 @@ class TabViewUtil extends StatelessWidget { String labelTab1; String labelTab2; final TabController controller; - final Function(bool) onEditingChanged; + final Function([bool]) onEditingChanged; Widget widget1; Widget widget2; @@ -49,15 +49,17 @@ class TabViewUtil extends StatelessWidget { padding: const EdgeInsets.all(4.0), tabs: [ Tab( + key: ValueKey('TabView_Tab1'), text: labelTab1, ), Tab( + key: ValueKey('TabView_Tab2'), text: labelTab2, ), ], controller: controller, onTap: (i) async { - if (i == 1) onEditingChanged(false); + onEditingChanged(); [() async {}, () async {}][i](); }, ), diff --git a/lib/components/atomic_components/shared_components_atoms/toast.dart b/lib/components/atomic_components/shared_components_atoms/toast.dart index 1be2a15e..30cc5332 100644 --- a/lib/components/atomic_components/shared_components_atoms/toast.dart +++ b/lib/components/atomic_components/shared_components_atoms/toast.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; -import 'package:hub/shared/utils/limited_text_size.dart'; class ToastUtil { static void showToast({ diff --git a/lib/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart b/lib/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart index b832653e..6502afbc 100644 --- a/lib/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart +++ b/lib/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart @@ -208,6 +208,7 @@ class _BottomArrowLinkedLocalsComponentWidgetState return CardItemTemplateComponentWidget( key: ValueKey(local['CLI_NOME']), imagePath: _imagePath(local), + icon: null, labelsHashMap: _labelsHashMap(local), statusHashMap: [_statusHashMap(local)], onTapCardItemAction: () async { 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 9c96f384..c9034798 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 @@ -1,4 +1,5 @@ import 'dart:collection'; +import 'dart:developer'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; @@ -12,18 +13,74 @@ import 'card_item_template_component_model.dart'; export 'card_item_template_component_model.dart'; +class FreCardIcon extends StatelessWidget { + final double height; + final double width; + final Icon icon; + + const FreCardIcon({ + super.key, + required this.height, + required this.width, + required this.icon, + }); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: height, + width: width, + child: icon, + ); + } +} + +class FreCardPin extends StatelessWidget { + final double height; + final double width; + final Color color; + + const FreCardPin({ + super.key, + required this.height, + required this.width, + required this.color, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + height: height, + width: width, + decoration: BoxDecoration( + color: color, + shape: BoxShape.circle, + ), + ), + ); + } +} + class CardItemTemplateComponentWidget extends StatefulWidget { const CardItemTemplateComponentWidget({ super.key, required this.labelsHashMap, required this.statusHashMap, - required this.imagePath, + this.imagePath, + this.icon, + this.pin, + this.itemWidthFactor = 0.25, required this.onTapCardItemAction, }); final Map? labelsHashMap; final List?> statusHashMap; final String? imagePath; + final FreCardIcon? icon; + final FreCardPin? pin; + final double itemWidthFactor; final Future Function()? onTapCardItemAction; @override @@ -124,9 +181,24 @@ class _CardItemTemplateComponentWidgetState ); } + Widget _generateIcon() { + return Column( + children: [ + widget.icon!, + ], + ); + } + + Widget _generatePin() { + return widget.pin!; + } + List _generateStatus() { double limitedBodyTextSize = LimitedFontSizeUtil.getBodyFontSize(context); + return statusLinkedHashMap.expand((statusLinked) { + log('statusHashMap: ${statusLinked.length}'); + return statusLinked.entries.map((entry) { final text = entry.key; final color = entry.value; @@ -135,7 +207,7 @@ class _CardItemTemplateComponentWidgetState message: text, child: Container( padding: const EdgeInsets.all(5), - width: MediaQuery.of(context).size.width * 0.25, + width: MediaQuery.of(context).size.width * widget.itemWidthFactor, decoration: BoxDecoration( color: color, borderRadius: BorderRadius.circular(5), @@ -161,38 +233,50 @@ class _CardItemTemplateComponentWidgetState return LayoutBuilder( builder: (context, constraints) { if (constraints.maxWidth > 360) { - return Row( + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - ..._generateLabels(), - SizedBox(height: 3), - Wrap( - spacing: 8, - runSpacing: 4, - children: _generateStatus(), + Row( + mainAxisAlignment: MainAxisAlignment.end, + mainAxisSize: MainAxisSize.max, + children: [if (widget.pin != null) _generatePin()]), + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + ..._generateLabels(), + Wrap( + spacing: 8, + runSpacing: 4, + children: _generateStatus(), + ), + ] + .addToEnd(const SizedBox(height: 5)) + .divide(const SizedBox(height: 3)) + .addToStart(const SizedBox(height: 5)), ), - ] - .addToEnd(const SizedBox(height: 5)) - .divide(const SizedBox(height: 1)) - .addToStart(const SizedBox(height: 5)), - ), + ), + if (widget.icon != null) _generateIcon(), + if (widget.imagePath != null) _generateImage() + ] + .addToEnd(const SizedBox(width: 10)) + .addToStart(const SizedBox(width: 10)), ), - if (widget.imagePath != null) _generateImage(), - ] - .addToEnd(const SizedBox(width: 10)) - .addToStart(const SizedBox(width: 10)), + ].addToStart(SizedBox(height: 5)).addToEnd(SizedBox(height: 5)), ); } else { return Column( mainAxisSize: MainAxisSize.min, children: [ if (widget.imagePath != null) _generateImage(), + if (widget.icon != null) _generateIcon(), Container( padding: const EdgeInsets.all(8), child: Column( diff --git a/lib/components/templates_components/details_component/details_component_widget.dart b/lib/components/templates_components/details_component/details_component_widget.dart index 6a4f5cec..67a21e19 100644 --- a/lib/components/templates_components/details_component/details_component_widget.dart +++ b/lib/components/templates_components/details_component/details_component_widget.dart @@ -3,6 +3,7 @@ import 'dart:collection'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart'; import 'package:hub/components/templates_components/details_component/details_component_model.dart'; import 'package:hub/flutter_flow/flutter_flow_theme.dart'; import 'package:hub/flutter_flow/flutter_flow_util.dart'; @@ -10,10 +11,11 @@ import 'package:hub/shared/utils/limited_text_size.dart'; class DetailsComponentWidget extends StatefulWidget { const DetailsComponentWidget({ - Key? key, + super.key, required this.labelsHashMap, required this.statusHashMap, this.imagePath, + this.icon, this.onTapCardItemAction, required this.buttons, }); @@ -23,6 +25,7 @@ class DetailsComponentWidget extends StatefulWidget { final String? imagePath; final Future Function()? onTapCardItemAction; final List? buttons; + final FreCardIcon? icon; @override State createState() => _DetailsComponentWidgetState(); @@ -95,6 +98,16 @@ class _DetailsComponentWidgetState extends State { useOldImageOnUrlChange: true, ), ), + if (widget.icon != null && widget.icon != '') + Container( + width: MediaQuery.of(context).size.width * 0.3, + height: MediaQuery.of(context).size.width * 0.3, + clipBehavior: Clip.antiAlias, + decoration: const BoxDecoration( + shape: BoxShape.circle, + ), + child: widget.icon!, + ), SizedBox(height: MediaQuery.of(context).size.height * 0.03), Row( children: statusLinkedHashMap.expand((linkedHashMap) { @@ -109,6 +122,7 @@ class _DetailsComponentWidgetState extends State { autofocus: false, canRequestFocus: false, readOnly: true, + initialValue: item.key, obscureText: false, decoration: InputDecoration( isDense: true, @@ -120,7 +134,7 @@ class _DetailsComponentWidgetState extends State { ), filled: true, fillColor: item.value, - labelText: item.key, + // labelText: item.key, labelStyle: FlutterFlowTheme.of(context) .labelMedium .override( @@ -156,13 +170,16 @@ class _DetailsComponentWidgetState extends State { color: FlutterFlowTheme.of(context).info, ), ), - style: FlutterFlowTheme.of(context).bodyMedium.override( - fontFamily: - FlutterFlowTheme.of(context).bodyMediumFamily, + style: FlutterFlowTheme.of(context) + .labelMedium + .override( + fontFamily: FlutterFlowTheme.of(context) + .labelMediumFamily, + fontWeight: FontWeight.bold, color: FlutterFlowTheme.of(context).info, letterSpacing: 0.0, useGoogleFonts: GoogleFonts.asMap().containsKey( - FlutterFlowTheme.of(context).bodyMediumFamily, + FlutterFlowTheme.of(context).labelMediumFamily, ), fontSize: limitedBodyFontSize, ), @@ -188,7 +205,7 @@ class _DetailsComponentWidgetState extends State { // return Text('key: $key, value: $value'); return TextFormField( readOnly: true, - initialValue: '$value', + initialValue: value, style: FlutterFlowTheme.of(context).bodyMedium.override( fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily, diff --git a/lib/components/templates_components/liberation_history_item_details_template_component/liberation_history_item_details_template_component_model.dart b/lib/components/templates_components/liberation_history_item_details_template_component/liberation_history_item_details_template_component_model.dart index 0fefa607..98d1c915 100644 --- a/lib/components/templates_components/liberation_history_item_details_template_component/liberation_history_item_details_template_component_model.dart +++ b/lib/components/templates_components/liberation_history_item_details_template_component/liberation_history_item_details_template_component_model.dart @@ -32,7 +32,6 @@ class LiberationHistoryItemDetailsTemplateComponentModel (await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? ''; cliUUID = (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; - ; } @override diff --git a/lib/components/templates_components/provisional_schedule_template/provisional_schedule_template_model.dart b/lib/components/templates_components/provisional_schedule_template/provisional_schedule_template_model.dart index c1e82767..2985cbba 100644 --- a/lib/components/templates_components/provisional_schedule_template/provisional_schedule_template_model.dart +++ b/lib/components/templates_components/provisional_schedule_template/provisional_schedule_template_model.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:flutter/material.dart'; import 'package:hub/components/templates_components/provisional_schedule_template/provisional_shcedule_template_widget.dart'; import 'package:hub/features/backend/index.dart'; diff --git a/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_model.dart b/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_model.dart index acff7931..c3d0ee0b 100644 --- a/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_model.dart +++ b/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_model.dart @@ -140,7 +140,6 @@ class RegisiterVistorTemplateComponentModel (await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? ''; cliUUID = (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; - ; } @override diff --git a/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_widget.dart b/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_widget.dart index 4a9c5ebe..b6c71aaf 100644 --- a/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_widget.dart +++ b/lib/components/templates_components/regisiter_vistor_template_component/regisiter_vistor_template_component_widget.dart @@ -35,7 +35,7 @@ class RegisiterVistorTemplateComponentWidget extends StatefulWidget { class _RegisiterVistorTemplateComponentWidgetState extends State { late RegisiterVistorTemplateComponentModel _model; - bool _isLoading = false; + final bool _isLoading = false; final scaffoldKey = GlobalKey(); bool _isVisitorRegistered = false; diff --git a/lib/components/templates_components/view_visit_detail/view_visit_detail_model.dart b/lib/components/templates_components/view_visit_detail/view_visit_detail_model.dart index d115ada3..b7628924 100644 --- a/lib/components/templates_components/view_visit_detail/view_visit_detail_model.dart +++ b/lib/components/templates_components/view_visit_detail/view_visit_detail_model.dart @@ -41,7 +41,6 @@ class ViewVisitDetailModel extends FlutterFlowModel { (await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? ''; cliUUID = (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; - ; } @override diff --git a/lib/components/templates_components/visitor_search_modal_template_component/visitor_search_modal_template_component_model.dart b/lib/components/templates_components/visitor_search_modal_template_component/visitor_search_modal_template_component_model.dart index 17cc8baf..34b0bfc2 100644 --- a/lib/components/templates_components/visitor_search_modal_template_component/visitor_search_modal_template_component_model.dart +++ b/lib/components/templates_components/visitor_search_modal_template_component/visitor_search_modal_template_component_model.dart @@ -43,7 +43,6 @@ class VisitorSearchModalTemplateComponentModel (await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? ''; cliUUID = (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; - ; } @override diff --git a/lib/core/index.dart b/lib/core/index.dart deleted file mode 100644 index 8b137891..00000000 --- a/lib/core/index.dart +++ /dev/null @@ -1 +0,0 @@ - diff --git a/lib/core/meta/anotations.dart b/lib/core/meta/anotations.dart deleted file mode 100644 index af721eaa..00000000 --- a/lib/core/meta/anotations.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:meta/meta.dart'; - -class DeadCode { - final String? desc; - - const DeadCode([this.desc = '']); -} diff --git a/lib/core/meta/index.dart b/lib/core/meta/index.dart deleted file mode 100644 index 166d574c..00000000 --- a/lib/core/meta/index.dart +++ /dev/null @@ -1 +0,0 @@ -export 'anotations.dart'; diff --git a/lib/features/backend/api_requests/api_calls.dart b/lib/features/backend/api_requests/api_calls.dart index 72d1a8a2..fd77fd3c 100644 --- a/lib/features/backend/api_requests/api_calls.dart +++ b/lib/features/backend/api_requests/api_calls.dart @@ -66,8 +66,186 @@ class PhpGroup extends Api { static GetResidentsByProperty getResidentsByProperty = GetResidentsByProperty(); static GetOpenedVisits getOpenedVisits = GetOpenedVisits(); + @override GetLicense getLicense = GetLicense(); static GetProvSchedules getProvSchedules = GetProvSchedules(); + static RegisterVehicle registerVehicle = RegisterVehicle(); + static UpdateVehicle updateVehicle = UpdateVehicle(); + static DeleteVehicle deleteVehicle = DeleteVehicle(); + static CancelDeleteVehicle cancelDelete = CancelDeleteVehicle(); +} + +class CancelDeleteVehicle { + Future call({ + required final int vehicleId, + required final String licensePlate, + required final String model, + required final String color, + }) async { + final String baseUrl = PhpGroup.getBaseUrl(); + final String devUUID = + (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; + final String userUUID = + (await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? ''; + final String cliID = + (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; + const String atividade = 'cancelDeleteVehicleRequest'; + + return await ApiManager.instance.makeApiCall( + callName: atividade, + apiUrl: '$baseUrl/processRequest.php', + callType: ApiCallType.POST, + headers: {'Content-Type': 'application/x-www-form-urlencoded'}, + params: { + 'devUUID': devUUID, + 'userUUID': userUUID, + 'cliID': cliID, + 'atividade': atividade, + 'vehicleId': vehicleId, + 'licensePlate': licensePlate, + 'model': model, + 'color': color + }, + bodyType: BodyType.X_WWW_FORM_URL_ENCODED, + returnBody: true, + encodeBodyUtf8: false, + decodeUtf8: false, + cache: false, + isStreamingApi: false, + alwaysAllowBody: false, + ); + } +} + +class DeleteVehicle { + Future call({ + required final int vehicleId, + required final String licensePlate, + required final String model, + required final String color, + }) async { + final String baseUrl = PhpGroup.getBaseUrl(); + final String devUUID = + (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; + final String userUUID = + (await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? ''; + final String cliID = + (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; + const String atividade = 'deleteVehicle'; + + return await ApiManager.instance.makeApiCall( + callName: 'deleteVehicle', + apiUrl: '$baseUrl/processRequest.php', + callType: ApiCallType.POST, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + params: { + 'devUUID': devUUID, + 'userUUID': userUUID, + 'cliID': cliID, + 'atividade': atividade, + 'vehicleId': vehicleId, + 'licensePlate': licensePlate, + 'model': model, + 'color': color + }, + bodyType: BodyType.X_WWW_FORM_URL_ENCODED, + returnBody: true, + encodeBodyUtf8: false, + decodeUtf8: false, + cache: false, + isStreamingApi: false, + alwaysAllowBody: false, + ); + } +} + +class RegisterVehicle { + Future call({ + final String? licensePlate, + final String? color, + final String? model, + }) async { + final String baseUrl = PhpGroup.getBaseUrl(); + + final String devUUID = + (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; + final String userUUID = + (await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? ''; + final String cliID = + (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; + const String atividade = 'insertVehicle'; + + return await ApiManager.instance.makeApiCall( + callName: 'registerVehicle', + apiUrl: '$baseUrl/processRequest.php', + callType: ApiCallType.POST, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + params: { + 'devUUID': devUUID, + 'userUUID': userUUID, + 'cliID': cliID, + 'atividade': atividade, + 'licensePlate': licensePlate, + 'color': color, + 'model': model, + }, + bodyType: BodyType.X_WWW_FORM_URL_ENCODED, + returnBody: true, + encodeBodyUtf8: false, + decodeUtf8: false, + cache: false, + isStreamingApi: false, + alwaysAllowBody: false, + ); + } +} + +class UpdateVehicle { + Future call({ + required final int vehicleId, + final String? licensePlate, + final String? color, + final String? model, + }) async { + final String baseUrl = PhpGroup.getBaseUrl(); + final String devUUID = + (await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? ''; + final String userUUID = + (await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? ''; + final String cliID = + (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; + const String atividade = 'updateVehicleToInsertRequest'; + + return await ApiManager.instance.makeApiCall( + callName: 'updateVehicle', + apiUrl: '$baseUrl/processRequest.php', + callType: ApiCallType.POST, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + params: { + 'devUUID': devUUID, + 'userUUID': userUUID, + 'cliID': cliID, + 'atividade': atividade, + 'licensePlate': licensePlate, + 'color': color, + 'model': model, + 'vehicleId': vehicleId + }, + bodyType: BodyType.X_WWW_FORM_URL_ENCODED, + returnBody: true, + encodeBodyUtf8: false, + decodeUtf8: false, + cache: false, + isStreamingApi: false, + alwaysAllowBody: false, + ); + } } class GetProvSchedules { diff --git a/lib/features/backend/api_requests/api_manager.dart b/lib/features/backend/api_requests/api_manager.dart index 72087153..d12c03dd 100644 --- a/lib/features/backend/api_requests/api_manager.dart +++ b/lib/features/backend/api_requests/api_manager.dart @@ -496,11 +496,11 @@ class ApiManager { result = ApiCallResponse(null, {}, -1, exception: e); } - log('API Call: $callName'); - log('URL: $apiUrl'); - log('Headers: $headers'); - log('Params: $params'); - log('Response: ${result.jsonBody}'); + print('API Call: $callName'); + print('URL: $apiUrl'); + print('Headers: $headers'); + print('Params: $params'); + print('Response: ${result.jsonBody}'); return result; } } diff --git a/lib/features/backend/schema/structs/device_struct.dart b/lib/features/backend/schema/structs/device_struct.dart index db8a8896..0bd580ec 100644 --- a/lib/features/backend/schema/structs/device_struct.dart +++ b/lib/features/backend/schema/structs/device_struct.dart @@ -3,8 +3,6 @@ import 'package:hub/features/backend/index.dart'; import 'package:hub/flutter_flow/nav/nav.dart'; -import 'index.dart'; - class DeviceStruct extends BaseStruct { DeviceStruct({ String? devUUID, diff --git a/lib/features/history/presentation/pages/acess_history_page_widget.dart b/lib/features/history/presentation/pages/acess_history_page_widget.dart index 16708425..bb6a8891 100644 --- a/lib/features/history/presentation/pages/acess_history_page_widget.dart +++ b/lib/features/history/presentation/pages/acess_history_page_widget.dart @@ -46,12 +46,15 @@ class _AccessHistoryState extends State { selectedTypeSubject.listen((value) {}); } + + @override void initState() { super.initState(); _model = createModel(context, () => AcessHistoryPageModel()); _accessFuture = fetchAccessHistoryService(); + _scrollController = ScrollController() ..addListener(() { if (_scrollController.position.atEdge && diff --git a/lib/features/history/presentation/widgets/provisional_filter_modal.dart b/lib/features/history/presentation/widgets/provisional_filter_modal.dart index 44d0f538..5928e05d 100644 --- a/lib/features/history/presentation/widgets/provisional_filter_modal.dart +++ b/lib/features/history/presentation/widgets/provisional_filter_modal.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:flutter/material.dart'; import '/flutter_flow/flutter_flow_util.dart'; diff --git a/lib/features/home/presentation/pages/about_system.dart b/lib/features/home/presentation/pages/about_system.dart index 7255388f..2e99ba46 100644 --- a/lib/features/home/presentation/pages/about_system.dart +++ b/lib/features/home/presentation/pages/about_system.dart @@ -15,6 +15,8 @@ import 'package:package_info_plus/package_info_plus.dart'; import 'package:url_launcher/url_launcher.dart'; class AboutSystemPage extends StatefulWidget { + const AboutSystemPage({super.key}); + @override _AboutSystemPageState createState() => _AboutSystemPageState(); } diff --git a/lib/features/local/data/data_sources/locals_local_data_source.dart b/lib/features/local/data/data_sources/locals_local_data_source.dart index e259f2c3..c63aec38 100644 --- a/lib/features/local/data/data_sources/locals_local_data_source.dart +++ b/lib/features/local/data/data_sources/locals_local_data_source.dart @@ -58,7 +58,7 @@ class LocalsLocalDataSourceImpl implements LocalsLocalDataSource { }, conflictAlgorithm: ConflictAlgorithm.replace, ); - } catch (e, s) {} + } catch (e) {} } @override diff --git a/lib/features/local/data/repositories/locals_repository_impl.dart b/lib/features/local/data/repositories/locals_repository_impl.dart index d171256e..a7245383 100644 --- a/lib/features/local/data/repositories/locals_repository_impl.dart +++ b/lib/features/local/data/repositories/locals_repository_impl.dart @@ -80,7 +80,6 @@ class LocalsRepositoryImpl implements LocalsRepository { } } else { log('_handleLocal -> Local selected'); - return true; } diff --git a/lib/features/local/presentation/widgets/local_profile/local_profile_widget.dart b/lib/features/local/presentation/widgets/local_profile/local_profile_widget.dart index 3cabc3e7..4215d501 100644 --- a/lib/features/local/presentation/widgets/local_profile/local_profile_widget.dart +++ b/lib/features/local/presentation/widgets/local_profile/local_profile_widget.dart @@ -255,7 +255,7 @@ class _LocalProfileComponentWidgetState ), Tooltip( message: - valueOrDefault(' ' + state.ownerName, ''), + valueOrDefault(' ${state.ownerName}', ''), child: Text( valueOrDefault( state.ownerName.length > 30 diff --git a/lib/features/local/utils/local_util.dart b/lib/features/local/utils/local_util.dart index 8de35b63..783e7fa7 100644 --- a/lib/features/local/utils/local_util.dart +++ b/lib/features/local/utils/local_util.dart @@ -115,6 +115,9 @@ class LocalUtil { .set(LocalsStorageKey.whatsapp.key, jsonBody['whatsapp'] ?? false); await StorageHelper().set( LocalsStorageKey.provisional.key, jsonBody['provisional'] ?? false); + await StorageHelper().set(LocalsStorageKey.vehicleAutoApproval.key, + jsonBody['vehicleAutoApproval'] ?? false); + await StorageHelper().set( LocalsStorageKey.pets.key, jsonBody['pet'] ?? false, @@ -141,6 +144,13 @@ class LocalUtil { jsonBody['petAmountRegister']?.toString().isEmpty ?? true ? '0' : jsonBody['petAmountRegister'].toString()); + + await StorageHelper().set( + LocalsStorageKey.vehicleAmountRegister.key, + jsonBody['vehicleAmountRegister']?.toString().isEmpty ?? true + ? '0' + : jsonBody['vehicleAmountRegister'].toString()); + await StorageHelper().set(ProfileStorageKey.userName.key, jsonBody['visitado']['VDO_NOME'] ?? ''); await StorageHelper().set(ProfileStorageKey.userEmail.key, diff --git a/lib/features/menu/data/data_sources/menu_local_data_source.dart b/lib/features/menu/data/data_sources/menu_local_data_source.dart index 796378cb..ad8502f8 100644 --- a/lib/features/menu/data/data_sources/menu_local_data_source.dart +++ b/lib/features/menu/data/data_sources/menu_local_data_source.dart @@ -17,10 +17,6 @@ abstract class MenuLocalDataSource { Future handleMenu(EnumMenuItem item, EnumDisplay display, MenuEntry opt, List entries); - - Future processStartDate(String startDate, MenuEntry entry); - - Future processExpirationDate(String expirationDate, MenuEntry entry); } class MenuLocalDataSourceImpl implements MenuLocalDataSource { @@ -92,30 +88,4 @@ class MenuLocalDataSourceImpl implements MenuLocalDataSource { log('Error processing display for module ${opt.key}: $e'); } } - - @override - Future processStartDate(String startDate, MenuEntry opt) async { - try { - if (startDate.isEmpty) return true; - final start = DateTime.tryParse(startDate); - if (start == null) return false; - return DateTime.now().isAfter(start); - } catch (e) { - log('Error processing start date for module ${opt.key}: $e'); - } - return false; - } - - @override - Future processExpirationDate( - String expirationDate, MenuEntry opt) async { - try { - if (expirationDate.isEmpty) return false; - final expiration = DateTime.tryParse(expirationDate); - return expiration != null && DateTime.now().isAfter(expiration); - } catch (e) { - log('Error processing expiration date for module ${opt.key}: $e'); - } - return false; - } } diff --git a/lib/features/menu/data/repositories/menu_repository_impl.dart b/lib/features/menu/data/repositories/menu_repository_impl.dart index 96eec795..0a73d855 100644 --- a/lib/features/menu/data/repositories/menu_repository_impl.dart +++ b/lib/features/menu/data/repositories/menu_repository_impl.dart @@ -4,6 +4,7 @@ import 'package:hub/features/menu/index.dart'; import 'package:hub/features/module/index.dart'; import 'package:hub/features/storage/index.dart'; import 'package:hub/flutter_flow/custom_functions.dart'; +import 'package:hub/shared/utils/datetime_util.dart'; class MenuRepositoryImpl implements MenuRepository { final MenuLocalDataSource menuDataSource = MenuLocalDataSourceImpl(); @@ -25,10 +26,9 @@ class MenuRepositoryImpl implements MenuRepository { final display = EnumDisplay.fromString(licenseMap['display']); final startDate = licenseMap['startDate'] ?? ''; final expirationDate = licenseMap['expirationDate'] ?? ''; - final isStarted = - await menuDataSource.processStartDate(startDate, entry); + final isStarted = await DateTimeUtil.processStartDate(startDate); final isExpired = - await menuDataSource.processExpirationDate(expirationDate, entry); + await DateTimeUtil.processExpirationDate(expirationDate); if (isStarted && !isExpired) { await menuDataSource.handleMenu(menuItem, display, entry, entries); } diff --git a/lib/features/menu/presentation/mappers/menu_entry.dart b/lib/features/menu/presentation/mappers/menu_entry.dart index 7e1a4b00..3398b0ae 100644 --- a/lib/features/menu/presentation/mappers/menu_entry.dart +++ b/lib/features/menu/presentation/mappers/menu_entry.dart @@ -7,9 +7,13 @@ import 'package:material_symbols_icons/symbols.dart'; enum MenuEntryType { Home, Drawer, Property } class MenuEntry implements BaseModule { + @override final String key; + @override final IconData icon; + @override final String name; + @override final String route; final List types; @@ -72,6 +76,16 @@ class MenuEntry implements BaseModule { route: '/residentsOnThePropertyPage', types: [MenuEntryType.Property], ), + // MenuEntry( + // key: 'FRE-HUB-VEHICLES-MANAGER', + // icon: Icons.directions_car, + // name: FFLocalizations.of(navigatorKey.currentContext!).getVariableText( + // ptText: 'Veículos', + // enText: 'Vehicles', + // ), + // route: '/vehiclesOnThePropertyPage', + // types: [MenuEntryType.Property], + // ), MenuEntry( key: 'FRE-HUB-VEHICLES', icon: Icons.directions_car, diff --git a/lib/features/menu/presentation/widgets/menu_item/menu_item_button.dart b/lib/features/menu/presentation/widgets/menu_item/menu_item_button.dart index c8f81e31..d5142c21 100644 --- a/lib/features/menu/presentation/widgets/menu_item/menu_item_button.dart +++ b/lib/features/menu/presentation/widgets/menu_item/menu_item_button.dart @@ -76,16 +76,15 @@ class _MenuButtonWidgetState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (widget.icon != null) - Container( - alignment: Alignment.topLeft, - padding: const EdgeInsets.only(left: 8.0), - child: Icon( - widget.icon, - size: 24.0, - color: FlutterFlowTheme.of(context).primaryText, - ), + Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.only(left: 8.0), + child: Icon( + widget.icon, + size: 24.0, + color: FlutterFlowTheme.of(context).primaryText, ), + ), Flexible( child: Padding( padding: const EdgeInsets.only(left: 10.0), diff --git a/lib/features/module/data/data_sources/license_local_data_source.dart b/lib/features/module/data/data_sources/license_local_data_source.dart index cc377225..4cbe143f 100644 --- a/lib/features/module/data/data_sources/license_local_data_source.dart +++ b/lib/features/module/data/data_sources/license_local_data_source.dart @@ -94,7 +94,7 @@ class LicenseLocalDataSourceImpl implements LicenseLocalDataSource { Future isNewVersion() async { final String? reponse = await StorageHelper().get(LocalsStorageKey.isNewVersion.key); - final bool isNewVersion = reponse.toBoolean(); + final bool isNewVersion = reponse.toBoolean; return isNewVersion; } diff --git a/lib/features/module/data/models/license_model.dart b/lib/features/module/data/models/license_model.dart index 48e215cc..df9e8f87 100644 --- a/lib/features/module/data/models/license_model.dart +++ b/lib/features/module/data/models/license_model.dart @@ -1,4 +1,3 @@ -import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'module_model.dart'; diff --git a/lib/features/module/data/models/module_model.dart b/lib/features/module/data/models/module_model.dart index e373edc3..17a2f23b 100644 --- a/lib/features/module/data/models/module_model.dart +++ b/lib/features/module/data/models/module_model.dart @@ -1,4 +1,3 @@ -import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'module_model.freezed.dart'; diff --git a/lib/features/module/domain/entities/base_module.dart b/lib/features/module/domain/entities/base_module.dart index 1cfbeef0..9e8759aa 100644 --- a/lib/features/module/domain/entities/base_module.dart +++ b/lib/features/module/domain/entities/base_module.dart @@ -1,6 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:hub/flutter_flow/internationalization.dart'; -import 'package:hub/flutter_flow/nav/nav.dart'; abstract class BaseModule { String get key; diff --git a/lib/features/module/domain/entities/license.dart b/lib/features/module/domain/entities/license.dart index 17a70650..dde5fee0 100644 --- a/lib/features/module/domain/entities/license.dart +++ b/lib/features/module/domain/entities/license.dart @@ -10,6 +10,7 @@ enum LicenseKeys { access('FRE-HUB-ACCESS'), openedVisits('FRE-HUB-OPENED-VISITS'), vehicles('FRE-HUB-VEHICLES'), + vehiclesManager('FRE-HUB-VEHICLES-MANAGER'), residents('FRE-HUB-RESIDENTS'), about('FRE-HUB-ABOUT-SYSTEM'), pets('FRE-HUB-PETS'), @@ -62,7 +63,7 @@ class License { static Future _precessWpp() async { final bool whatsapp = await StorageHelper() .get(LocalsStorageKey.whatsapp.key) - .then((v) => v.toBoolean()); + .then((v) => v.toBoolean); if (whatsapp) return ModuleStatus.active.key; else @@ -72,7 +73,7 @@ class License { static Future _processProvisional() async { final bool provisional = await StorageHelper() .get(LocalsStorageKey.provisional.key) - .then((v) => v.toBoolean()); + .then((v) => v.toBoolean); if (provisional) return ModuleStatus.active.key; else @@ -82,7 +83,7 @@ class License { static Future _processPets() async { final bool pets = await StorageHelper() .get(LocalsStorageKey.pets.key) - .then((v) => v.toBoolean()); + .then((v) => v.toBoolean); if (pets) return ModuleStatus.active.key; else @@ -155,6 +156,13 @@ class License { startDate: '', quantity: 0, ), + Module( + key: LicenseKeys.vehicles.value, + display: ModuleStatus.inactive.key, + expirationDate: '', + startDate: '', + quantity: 0, + ), Module( key: LicenseKeys.residents.value, display: isNewVersionWithModule diff --git a/lib/features/notification/deep_link_service.dart b/lib/features/notification/deep_link_service.dart index 4f6ba58d..77792519 100644 --- a/lib/features/notification/deep_link_service.dart +++ b/lib/features/notification/deep_link_service.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:developer'; import 'package:app_links/app_links.dart'; import 'package:flutter/material.dart'; import 'package:hub/features/storage/index.dart'; diff --git a/lib/features/profile/data/data_sources/profile_local_data_source.dart b/lib/features/profile/data/data_sources/profile_local_data_source.dart index 8b0f9d1e..b1ee1258 100644 --- a/lib/features/profile/data/data_sources/profile_local_data_source.dart +++ b/lib/features/profile/data/data_sources/profile_local_data_source.dart @@ -1,8 +1,6 @@ import 'dart:developer'; -import 'package:hub/features/storage/constants/profile_constants.dart'; import 'package:hub/features/storage/index.dart'; -import 'package:hub/shared/constants/index.dart'; import 'package:sqflite/sqflite.dart'; diff --git a/lib/features/storage/enums/database_storage_key.dart b/lib/features/storage/enums/database_storage_key.dart index f4028646..92442313 100644 --- a/lib/features/storage/enums/database_storage_key.dart +++ b/lib/features/storage/enums/database_storage_key.dart @@ -34,7 +34,9 @@ enum LocalsStorageKey implements DatabaseStorageKey { panic('fre_panic'), person('fre_person'), requestOSNotification('fre_requestOSnotification'), - isNewVersion('fre_isNewVersion'); + isNewVersion('fre_isNewVersion'), + vehicleAutoApproval('fre_vehicleAutoApproval'), + vehicleAmountRegister('fre_vehicleAmountRegister'); final String key; diff --git a/lib/features/storage/repositories/storage_repository_impl.dart b/lib/features/storage/repositories/storage_repository_impl.dart index 30f3fd0d..1649b05e 100644 --- a/lib/features/storage/repositories/storage_repository_impl.dart +++ b/lib/features/storage/repositories/storage_repository_impl.dart @@ -1,7 +1,6 @@ import 'dart:developer'; import 'package:flutter/cupertino.dart'; -import 'package:hub/features/profile/data/data_sources/profile_local_data_source.dart'; import 'package:hub/features/storage/index.dart'; class StorageHelper implements StorageRepository { diff --git a/lib/flutter_flow/flutter_flow_util.dart b/lib/flutter_flow/flutter_flow_util.dart index bcb34fe5..1aa2f180 100644 --- a/lib/flutter_flow/flutter_flow_util.dart +++ b/lib/flutter_flow/flutter_flow_util.dart @@ -529,7 +529,7 @@ void setAppLanguage(BuildContext context, String language) => void setDarkModeSetting(BuildContext context, ThemeMode themeMode) => App.of(context).setThemeMode(themeMode); -void showSnackbar( +void showSnackbarMessenger( BuildContext context, String message, bool error, { @@ -537,38 +537,47 @@ void showSnackbar( int duration = 4, }) { ScaffoldMessenger.of(context).hideCurrentSnackBar(); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Row( - children: [ - if (loading) - Padding( - padding: const EdgeInsetsDirectional.only(end: 10.0), - child: SizedBox( - height: 20, - width: 20, - child: CircularProgressIndicator( - color: FlutterFlowTheme.of(context).info, - ), + ScaffoldMessenger.of(context) + .showSnackBar(showSnackbar(context, message, error)); +} + +SnackBar showSnackbar( + BuildContext context, + String message, + bool error, { + bool loading = false, + int duration = 4, +}) { + return SnackBar( + content: Row( + children: [ + if (loading) + Padding( + padding: const EdgeInsetsDirectional.only(end: 10.0), + child: SizedBox( + height: 20, + width: 20, + child: CircularProgressIndicator( + color: FlutterFlowTheme.of(context).info, ), ), - Text( - message, - style: TextStyle( - color: FlutterFlowTheme.of(context).info, - fontSize: LimitedFontSizeUtil.getBodyFontSize(context), - ), ), - ], - ), - duration: Duration(seconds: duration), - backgroundColor: error - ? FlutterFlowTheme.of(context).error - : FlutterFlowTheme.of(context).success, - behavior: SnackBarBehavior.floating, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(30), - ), + Text( + message, + style: TextStyle( + color: FlutterFlowTheme.of(context).info, + fontSize: LimitedFontSizeUtil.getBodyFontSize(context), + ), + ), + ], + ), + duration: Duration(seconds: duration), + backgroundColor: error + ? FlutterFlowTheme.of(context).error + : FlutterFlowTheme.of(context).success, + behavior: SnackBarBehavior.floating, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(30), ), ); } diff --git a/lib/flutter_flow/flutter_flow_widgets.dart b/lib/flutter_flow/flutter_flow_widgets.dart index 6845d783..122432eb 100644 --- a/lib/flutter_flow/flutter_flow_widgets.dart +++ b/lib/flutter_flow/flutter_flow_widgets.dart @@ -1,6 +1,5 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:flutter/material.dart'; -import 'package:auto_size_text/auto_size_text.dart'; class FFButtonOptions { const FFButtonOptions({ diff --git a/lib/flutter_flow/nav/nav.dart b/lib/flutter_flow/nav/nav.dart index 600a43ad..6da3a666 100644 --- a/lib/flutter_flow/nav/nav.dart +++ b/lib/flutter_flow/nav/nav.dart @@ -196,7 +196,7 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) { FFRoute( name: 'vehiclesOnThePropertyPage', path: '/vehiclesOnThePropertyPage', - builder: (context, params) => const VehicleOnTheProperty()), + builder: (context, params) => const VehiclePage()), FFRoute( name: 'receptionPage', path: '/receptionPage', diff --git a/lib/initialization.dart b/lib/initialization.dart index 04aa29c6..1d50273e 100644 --- a/lib/initialization.dart +++ b/lib/initialization.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:app_tracking_transparency/app_tracking_transparency.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; @@ -58,6 +56,8 @@ Future _initializeSystemSettings() async { if (kDebugMode) { //kDebugMode print('Debug mode'); + } else { + print('Release mode'); bool unsentReports = await FirebaseCrashlytics.instance.checkForUnsentReports(); if (unsentReports) { @@ -68,9 +68,6 @@ Future _initializeSystemSettings() async { // Não existem relatórios não enviados print('Todos os relatórios de falhas foram enviados.'); } - } else { - print('Release mode'); - await crashlyticsInstance.setCrashlyticsCollectionEnabled(true); // if (crashlyticsInstance.isCrashlyticsCollectionEnabled) { FlutterError.onError = await crashlyticsInstance.recordFlutterError; diff --git a/lib/main.dart b/lib/main.dart index 623ac049..d02e48c7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -14,7 +14,6 @@ import 'package:hub/features/storage/index.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/utils/test_util.dart'; import 'package:responsive_framework/responsive_framework.dart'; import 'initialization.dart'; diff --git a/lib/pages/liberation_history/liberation_history_widget.dart b/lib/pages/liberation_history/liberation_history_widget.dart index f69abdee..d4f4ce88 100644 --- a/lib/pages/liberation_history/liberation_history_widget.dart +++ b/lib/pages/liberation_history/liberation_history_widget.dart @@ -299,7 +299,7 @@ class _LiberationHistoryWidgetState extends State { ) .then((message) { if (message != null || message != '') { - showSnackbar( + showSnackbarMessenger( context, FFLocalizations.of(context).getVariableText( enText: 'Successfully resolved visit', @@ -308,7 +308,7 @@ class _LiberationHistoryWidgetState extends State { false, ); } else { - showSnackbar(context, message, true); + showSnackbarMessenger(context, message, true); } }).whenComplete(() { safeSetState(() { diff --git a/lib/pages/pets_page/pets_page_model.dart b/lib/pages/pets_page/pets_page_model.dart index e7de27fe..6671c468 100644 --- a/lib/pages/pets_page/pets_page_model.dart +++ b/lib/pages/pets_page/pets_page_model.dart @@ -474,7 +474,7 @@ class PetsPageModel extends FlutterFlowModel { context.pop(value); if (value == false) { - showSnackbar( + showSnackbarMessenger( context, FFLocalizations.of(context).getVariableText( ptText: 'Erro ao excluir pet', @@ -483,7 +483,7 @@ class PetsPageModel extends FlutterFlowModel { true, ); } else if (value == true) { - showSnackbar( + showSnackbarMessenger( context, FFLocalizations.of(context).getVariableText( enText: 'Success deleting pet', @@ -494,7 +494,7 @@ class PetsPageModel extends FlutterFlowModel { } }).catchError((err, stack) { context.pop(); - showSnackbar( + showSnackbarMessenger( context, FFLocalizations.of(context).getVariableText( enText: 'Error deleting pet', diff --git a/lib/pages/pets_page/pets_page_widget.dart b/lib/pages/pets_page/pets_page_widget.dart index 8816c468..0fb09390 100644 --- a/lib/pages/pets_page/pets_page_widget.dart +++ b/lib/pages/pets_page/pets_page_widget.dart @@ -86,9 +86,9 @@ class _PetsPageWidgetState extends State ); } - void onEditingChanged(bool value) { + void onEditingChanged([bool? value]) { setState(() { - _model.handleEditingChanged(value); + _model.handleEditingChanged(value!); }); } diff --git a/lib/pages/reception_page/reception_page_widget.dart b/lib/pages/reception_page/reception_page_widget.dart index e7d5718e..4fc8c064 100644 --- a/lib/pages/reception_page/reception_page_widget.dart +++ b/lib/pages/reception_page/reception_page_widget.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:awesome_notifications/awesome_notifications.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; diff --git a/lib/pages/residents_on_the_property/residents_on_the_property_screen.dart b/lib/pages/residents_on_the_property/residents_on_the_property_screen.dart index 8f63d8a2..64b61acf 100644 --- a/lib/pages/residents_on_the_property/residents_on_the_property_screen.dart +++ b/lib/pages/residents_on_the_property/residents_on_the_property_screen.dart @@ -29,7 +29,7 @@ class _ResidentsOnThePropertyState extends State late final ResidentsOnThePropertyModel model; late Future _future; - List _wrap = []; + final List _wrap = []; @override void initState() { diff --git a/lib/pages/schedule_complete_visit_page/schedule_complete_visit_page_model.dart b/lib/pages/schedule_complete_visit_page/schedule_complete_visit_page_model.dart index f7248d93..f6c80364 100644 --- a/lib/pages/schedule_complete_visit_page/schedule_complete_visit_page_model.dart +++ b/lib/pages/schedule_complete_visit_page/schedule_complete_visit_page_model.dart @@ -472,7 +472,7 @@ class ScheduleCompleteVisitPageModel context.pop(value); if (value == false) { - showSnackbar( + showSnackbarMessenger( context, FFLocalizations.of(context).getVariableText( enText: 'Error blocking visit', @@ -481,7 +481,7 @@ class ScheduleCompleteVisitPageModel true, ); } else if (value == true) { - showSnackbar( + showSnackbarMessenger( context, FFLocalizations.of(context).getVariableText( enText: 'Success canceling visit', @@ -492,7 +492,7 @@ class ScheduleCompleteVisitPageModel } }).catchError((err, stack) { context.pop(); - showSnackbar( + showSnackbarMessenger( context, FFLocalizations.of(context).getVariableText( enText: 'Error blocking visit', diff --git a/lib/pages/vehicles_on_the_property/index.dart b/lib/pages/vehicles_on_the_property/index.dart new file mode 100644 index 00000000..f159a317 --- /dev/null +++ b/lib/pages/vehicles_on_the_property/index.dart @@ -0,0 +1,2 @@ +export 'vehicles_on_the_property.dart'; +export 'vehicle_model.dart'; diff --git a/lib/pages/vehicles_on_the_property/vehicle_history_screen.dart b/lib/pages/vehicles_on_the_property/vehicle_history_screen.dart new file mode 100644 index 00000000..1e90cc84 --- /dev/null +++ b/lib/pages/vehicles_on_the_property/vehicle_history_screen.dart @@ -0,0 +1,240 @@ +part of 'vehicles_on_the_property.dart'; + +class VehicleHistoryScreen extends StatefulWidget { + final VehicleModel model; + const VehicleHistoryScreen(this.model, {super.key}); + + @override + State createState() => _VehicleHistoryScreenState(); +} + +class _VehicleHistoryScreenState extends State + with Pageable { + final apiCall = PhpGroup.getVehiclesByProperty; + int count = 0; + final PagingController _pagingController = + PagingController(firstPageKey: 1); + bool _isSnackble = true; + + @override + void initState() { + super.initState(); + _pagingController.addPageRequestListener( + (int pageKey) => fetchPage( + dataProvider: () async { + final newItems = await apiCall.call(pageKey.toString()); + if (newItems.jsonBody == null) return (false, null); + final List vehicles = + (newItems.jsonBody['vehicles'] as List?) ?? []; + + safeSetState(() { + count = newItems.jsonBody['total_rows'] ?? 0; + }); + + return (vehicles.isNotEmpty, vehicles); + }, + onDataUnavailable: (vehicles) { + setState(() {}); + final bool isFirst = pageKey == 2; + if (!isFirst && _isSnackble) showNoMoreDataSnackBar(context); + + _pagingController.appendLastPage(vehicles); + }, + onDataAvailable: (vehicles) { + setState(() {}); + _pagingController.appendPage(vehicles, pageKey + 1); + }, + onFetchError: (e, s) { + DialogUtil.errorDefault(context); + LogUtil.requestAPIFailed( + "proccessRequest.php", "", "Consulta de Veículo", e, s); + setState(() {}); + }, + ), + ); + _pagingController.addStatusListener(_showError); + } + + @override + void dispose() { + _pagingController.dispose(); + super.dispose(); + } + + Future _showError(PagingStatus status) async { + if (status == PagingStatus.subsequentPageError) { + final message = FFLocalizations.of(context).getVariableText( + enText: 'Something went wrong while fetching a new page.', + ptText: 'Algo deu errado ao buscar uma nova página.', + ); + final retry = FFLocalizations.of(context).getVariableText( + enText: 'Retry', + ptText: 'Recarregar', + ); + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(message), + action: SnackBarAction( + label: retry, + onPressed: () => _pagingController.retryLastFailedRequest(), + ), + ), + ); + } + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + _buildHeader(context), + _buildBody(context), + ], + ); + } + + Widget _buildHeader(BuildContext context) { + final bodyFontSize = LimitedFontSizeUtil.getBodyFontSize(context); + return SizedBox( + height: 30, + child: Center( + child: Text( + (widget.model.amountRegister == '0' || + widget.model.amountRegister == null) + ? '' + : "${FFLocalizations.of(context).getVariableText(ptText: "Quantidade de Veículos: ", enText: "Amount of Vehicles: ")}${widget.model.amountRegister}/$count", + textAlign: TextAlign.right, + style: TextStyle( + fontFamily: 'Nunito', + fontSize: bodyFontSize, + ), + ), + ), + ); + } + + Expanded _buildBody(BuildContext context) { + final noDataFound = FFLocalizations.of(context).getVariableText( + ptText: "Nenhum veículo encontrado!", + enText: "No vehicle found", + ); + return buildPaginatedListView( + noDataFound, + _pagingController, + _generateItems, + ); + } + + Widget _generateItems( + BuildContext context, + dynamic item, + int index, + ) { + log('item: $item'); + final bool? isOwner = item['isOwnerVehicle']; + final IconData iconData = + isOwner == true ? Symbols.garage : Symbols.directions_car; + + final FreCardIcon? cardIcon = isOwner != null + ? FreCardIcon( + height: 50, + width: 100, + icon: Icon(iconData, size: 80, opticalSize: 10), + ) + : null; + + final String? tag = item['tag']; + final bool containTag = tag.isNotNullAndEmpty; + final Map labelsHashMap = + _generateLabelsHashMap(context, item, tag, containTag); + + final List> statusHashMapList = + _generateStatusHashMapList(item); + + Future onTapCardItemAction() async { + await _handleCardItemTap(context, cardIcon, item); + } + + final statusLinkedHashMap = statusHashMapList + .map((map) => LinkedHashMap.from(map)) + .toList(); + final length = statusLinkedHashMap.expand((e) => [e.length]); + final double itemWidthFactor = length == 1 ? 0.25 : 0.50; + + return CardItemTemplateComponentWidget( + icon: cardIcon, + labelsHashMap: labelsHashMap, + statusHashMap: statusHashMapList, + onTapCardItemAction: onTapCardItemAction, + itemWidthFactor: itemWidthFactor, + ); + } + + Map _generateLabelsHashMap( + BuildContext context, + Map item, + String? tag, + bool containTag, + ) { + final localization = FFLocalizations.of(context); + return { + '${localization.getVariableText(ptText: "Placa", enText: "License Plate")}:': + item['licensePlate'] ?? '', + '${localization.getVariableText(ptText: "Modelo", enText: "Model")}:': + item['model'] ?? '', + '${localization.getVariableText(ptText: "Proprietário", enText: "Owner")}:': + item['personName'] ?? '', + if (containTag) + '${localization.getVariableText(ptText: "Tag", enText: "Tag")}:': + tag ?? '', + }; + } + + List> _generateStatusHashMapList( + Map item) { + final statusHashMap = widget.model.generateStatusColorMap(item, false); + return statusHashMap != null ? [statusHashMap] : []; + } + + Future _handleCardItemTap( + BuildContext context, + FreCardIcon? cardIcon, + Map item, + ) async { + try { + final dialogContent = widget.model.buildVehicleDetails( + icon: cardIcon, + item: item, + context: context, + model: widget.model, + ); + + await showDialog( + useSafeArea: true, + context: context, + builder: (context) => Dialog( + alignment: Alignment.center, + child: dialogContent, + ), + ) // + .then((response) async { + if (response == true) { + _isSnackble = false; + _pagingController.refresh(); + } else { + _isSnackble = true; + } + }) // + .whenComplete(() {}); + } catch (e, s) { + DialogUtil.errorDefault(context); + LogUtil.requestAPIFailed( + "proccessRequest.php", "", "Consulta de Veículos", e, s); + safeSetState(() { + // _hasData = false; + // _loading = false; + }); + } + } +} diff --git a/lib/pages/vehicles_on_the_property/vehicle_model.dart b/lib/pages/vehicles_on_the_property/vehicle_model.dart index 826095f5..61a53dc5 100644 --- a/lib/pages/vehicles_on_the_property/vehicle_model.dart +++ b/lib/pages/vehicles_on_the_property/vehicle_model.dart @@ -1,54 +1,558 @@ -import 'package:flutter/material.dart'; -import 'package:hub/components/templates_components/details_component/details_component_widget.dart'; -import 'package:hub/flutter_flow/flutter_flow_model.dart'; -import 'package:hub/flutter_flow/internationalization.dart'; -import 'package:hub/pages/vehicles_on_the_property/vehicles_on_the_property.dart'; +import 'dart:developer'; -class VehicleModel extends FlutterFlowModel { +import 'package:flutter/material.dart'; +import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart'; +import 'package:hub/components/templates_components/details_component/details_component_widget.dart'; +import 'package:hub/features/backend/index.dart'; +import 'package:hub/features/storage/index.dart'; +import 'package:hub/flutter_flow/index.dart'; +import 'package:hub/pages/vehicles_on_the_property/vehicles_on_the_property.dart'; +import 'package:hub/shared/extensions/index.dart'; +import 'package:hub/shared/utils/index.dart'; + +/// [VehicleModel] is a class that contains the business logic of the vehicle page. +class VehicleModel extends FlutterFlowModel + with + _BaseVehiclePage, + _VehicleHistoryScreenModel, + _VehicleRegisterScreenModel, + _VehicleUpdateScreenModel { + /// [VehicleModel] is a singleton class that contains the business logic of the vehicle page. static VehicleModel? _instance = VehicleModel._internal(); VehicleModel._internal(); factory VehicleModel() => _instance ?? VehicleModel._internal(); static void resetInstance() => _instance = null; - dynamic item; + final GlobalKey registerFormKey = GlobalKey(); + final GlobalKey updateFormKey = GlobalKey(); @override void initState(BuildContext context) { + log('VehicleModel -> initState'); resetInstance(); - initAsync(); + initializeControllers(context); + WidgetsBinding.instance.addPostFrameCallback((_) async { + amountRegister = '0'; + }); + } + + void initializeControllers(BuildContext context) { + tabBarController = TabController( + vsync: Navigator.of(context), + length: 2, + ); + + textFieldFocusLicensePlate = FocusNode(); + textFieldControllerLicensePlate = TextEditingController(); + + textFieldFocusColor = FocusNode(); + textFieldControllerColor = TextEditingController(); + + textFieldFocusModel = FocusNode(); + textFieldControllerModel = TextEditingController(); } @override - void dispose() {} + void dispose() { + disposeControllers(); + } - Future initAsync() async {} + void disposeControllers() { + tabBarController.dispose(); + textFieldFocusLicensePlate!.dispose(); + textFieldControllerLicensePlate!.dispose(); + textFieldFocusColor!.dispose(); + textFieldControllerColor!.dispose(); + textFieldFocusModel!.dispose(); + textFieldControllerModel!.dispose(); + } - Widget buildVehicleDetails({ - required dynamic item, - required BuildContext context, - required VehicleModel model, - }) { - return DetailsComponentWidget( - buttons: [], - labelsHashMap: Map.from({ - if (item['model'] != null && item['model'] != '') - '${FFLocalizations.of(context).getVariableText(ptText: "Modelo", enText: "Model")}:': - item['model'].toString().toUpperCase(), - if (item['licensePlate'] != null && item['licensePlate'] != '') - '${FFLocalizations.of(context).getVariableText(ptText: "Placa", enText: "License Plate")}:': - item['licensePlate'].toString().toUpperCase(), - if (item['color'] != null && item['color'] != '') - '${FFLocalizations.of(context).getVariableText(ptText: "Cor", enText: "Color")}:': - item['color'].toString().toUpperCase(), - if (item['personName'] != null && item['personName'] != '') - '${FFLocalizations.of(context).getVariableText(ptText: "Proprietário", enText: "Owner")}:': - item['personName'].toString().toUpperCase(), - if (item['tag'] != null && item['tag'] != '') - '${FFLocalizations.of(context).getVariableText(ptText: "Tag", enText: "Tag")}:': - item['tag'].toString().toUpperCase(), - }), - statusHashMap: [], + Future initAsync() async { + amountRegister = + await StorageHelper().get(LocalsStorageKey.vehicleAmountRegister.key); + autoApproval = + await StorageHelper().get(LocalsStorageKey.vehicleAutoApproval.key); + } + + bool isFormValid(BuildContext context, GlobalKey formKey) { + if (formKey.currentState == null) return false; + return formKey.currentState!.validate(); + } +} + +/// [_BaseVehiclePage] is a mixin that contains the base logic of the vehicle page. +mixin class _BaseVehiclePage { + int count = 0; + late final TabController tabBarController; + // dynamic item; + late int vehicleId; + BuildContext context = navigatorKey.currentContext!; + bool isEditing = false; + ApiCallResponse? vehicleResponse; + String? amountRegister = '0'; + late final String? autoApproval; + + VoidCallback? onUpdateVehicle; + VoidCallback? onRegisterVehicle; + VoidCallback? safeSetState; + + FocusNode? textFieldFocusLicensePlate; + TextEditingController? textFieldControllerLicensePlate; + String? textControllerLicensePlateValidator( + BuildContext context, String? value) { + final validationMessage = validateField( + context, value, 'Placa é obrigatória', 'License Plate is required'); + if (validationMessage != null) return validationMessage; + + final brazilianPlateRegex = RegExp(r'^[A-Z]{3}\d{4}$'); + final mercosurPlateRegex = RegExp(r'^[A-Z]{3}\d[A-Z0-9]\d{2}$'); + + if (!brazilianPlateRegex.hasMatch(value!) && + !mercosurPlateRegex.hasMatch(value)) { + return FFLocalizations.of(context).getVariableText( + ptText: 'Placa inválida', + enText: 'Invalid license plate', + ); + } + + return null; + } + + FocusNode? textFieldFocusColor; + TextEditingController? textFieldControllerColor; + String? textControllerColorValidator(BuildContext contexnt, String? value) { + return validateField( + context, value, 'Cor é obrigatória', 'Color is required'); + } + + FocusNode? textFieldFocusModel; + TextEditingController? textFieldControllerModel; + String? textControllerModelValidator(BuildContext contexnt, String? value) { + return validateField( + context, value, 'Modelo é obrigatório', 'Model is required'); + } + + String? validateField( + BuildContext context, String? value, String ptText, String enText) { + if (value == null || value.isEmpty) { + return FFLocalizations.of(context).getVariableText( + ptText: ptText, + enText: enText, + ); + } + return null; + } + + Future switchTab(int index) async { + tabBarController.animateTo(index); + if (index == 0) await handleEditingChanged(false); + safeSetState?.call(); + } + + Future clearFields() async { + textFieldControllerLicensePlate = TextEditingController(text: ''); + textFieldControllerColor = TextEditingController(text: ''); + textFieldControllerModel = TextEditingController(text: ''); + } + + Future handleEditingChanged(bool editing) async { + isEditing = editing; + await clearFields(); + } + + Future setEditForm(dynamic item) async { + if (item != null) { + log('vehicleId: ${item['vehicleId']}'); + vehicleId = item['vehicleId']; + + textFieldControllerLicensePlate = TextEditingController( + text: item != null ? item['licensePlate'] ?? '' : ''); + textFieldControllerColor = + TextEditingController(text: item != null ? item['color'] ?? '' : ''); + textFieldControllerModel = + TextEditingController(text: item != null ? item['model'] ?? '' : ''); + } + } + + Future handleVehicleResponse(ApiCallResponse response, + String successMessage, String errorMessage) async { + if (response.jsonBody['error'] == false) { + await DialogUtil.success(context, successMessage).then((_) async { + await switchTab(0); + }); + } else { + String errorMsg; + try { + errorMsg = response.jsonBody['error_msg']; + } catch (e) { + errorMsg = errorMessage; + } + await DialogUtil.error(context, errorMsg); + } + } +} + +/// [_VehicleRegisterScreenModel] is a mixin that contains the business logic of the vehicle register page. +mixin _VehicleRegisterScreenModel on _BaseVehiclePage { + Future registerVehicle() async { + final response = await PhpGroup.registerVehicle.call( + licensePlate: textFieldControllerLicensePlate!.text, + color: textFieldControllerColor!.text, + model: textFieldControllerModel!.text, + ); + await handleVehicleResponse( + response, + FFLocalizations.of(context).getVariableText( + ptText: 'Veículo cadastrado com sucesso', + enText: 'Vehicle registered successfully', + ), + FFLocalizations.of(context).getVariableText( + ptText: 'Erro ao cadastrar veículo', + enText: 'Error registering vehicle', + ), + ); + } +} + +/// [_VehicleUpdateScreenModel] is a mixin that contains the business logic of the vehicle update page. +mixin _VehicleUpdateScreenModel on _BaseVehiclePage { + Future updateVehicle() async { + final response = await PhpGroup.updateVehicle.call( + licensePlate: textFieldControllerLicensePlate!.text, + color: textFieldControllerColor!.text, + model: textFieldControllerModel!.text, + vehicleId: vehicleId, + ); + await handleVehicleResponse( + response, + FFLocalizations.of(context).getVariableText( + ptText: 'Veículo atualizado com sucesso', + enText: 'Vehicle updated successfully', + ), + FFLocalizations.of(context).getVariableText( + ptText: 'Erro ao atualizar veículo', + enText: 'Error updating vehicle', + ), + ); + } +} + +/// [_VehicleHistoryScreenModel] is a mixin that contains the business logic of the vehicle history page. +mixin _VehicleHistoryScreenModel on _BaseVehiclePage { + Map? generateStatusColorMap(dynamic uItem, bool isDetail) { + if (autoApproval.toBoolean == true) return null; + final theme = FlutterFlowTheme.of(context); + final localization = FFLocalizations.of(context); + + final status = uItem['status']; + final isOwner = uItem['isOwnerVehicle']; + + if (isOwner == null && status == null) return null; + + String byLanguage(String en, String pt) => + localization.getVariableText(enText: en, ptText: pt); + + final vehicleStatusMap = { + "ATI": { + "text": byLanguage('Active', 'Ativo'), + "color": theme.success, + }, + "INA": { + "text": byLanguage('Inactive', 'Inativo'), + "color": theme.accent2, + }, + "APR_CREATE": { + "text": byLanguage('Awaiting Creation', 'Aguardando Criação'), + "color": theme.success, + }, + "APR_DELETE": { + "text": byLanguage('Awaiting Deletion', 'Aguardando Exclusão'), + "color": theme.error, + }, + "APR_UPDATE": { + "text": byLanguage('Awaiting Update', 'Aguardando Atualização'), + "color": theme.accent2, + }, + "AGU_CHANGE": { + "text": byLanguage('Awaiting Change', 'Aguardando Alteração'), + "color": theme.warning, + }, + }; + + if (vehicleStatusMap.containsKey(status)) { + final statusMap = { + vehicleStatusMap[status]!['text'] as String: + vehicleStatusMap[status]!['color'] as Color, + }; + + if (!isDetail && isOwner && (status != 'ATI' && status != 'INA')) { + statusMap[byLanguage('My Vehicle', 'Meu Veículo')] = theme.accent4; + } + + return statusMap; + } + + return {}; + } + + List generateActionButtons(dynamic item) { + final Color iconButtonColor = FlutterFlowTheme.of(context).primaryText; + final FFButtonOptions buttonOptions = FFButtonOptions( + height: 40, + color: FlutterFlowTheme.of(context).primaryBackground, + elevation: 0, + textStyle: TextStyle( + color: FlutterFlowTheme.of(context).primaryText, + fontSize: LimitedFontSizeUtil.getNoResizeFont(context, 15), + ), + splashColor: FlutterFlowTheme.of(context).success, + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).primaryBackground, + width: 1, + ), + ); + if (item['status'] == null) return []; + + final updateText = FFLocalizations.of(context) + .getVariableText(ptText: 'Editar', enText: 'Edit'); + final updateIcon = Icon(Icons.edit, color: iconButtonColor); + Future updateOnPressed() async { + context.pop(); + isEditing = true; + + await switchTab(1); + await setEditForm(item); + } + + final cancelText = FFLocalizations.of(context) + .getVariableText(ptText: 'Cancelar', enText: 'Cancel'); + final cancelIcon = Icon(Icons.close, color: iconButtonColor); + Future cancelOnPressed() async { + showAlertDialog( + context, + FFLocalizations.of(context).getVariableText( + ptText: 'Cancelar Solicitação', + enText: 'Cancel Request', + ), + FFLocalizations.of(context).getVariableText( + ptText: 'Você tem certeza que deseja cancelar essa solicitação?', + enText: 'Are you sure you want to delete this request?', + ), + () async => await processCancelRequest(item['status'], item)); + } + + final deleteText = FFLocalizations.of(context) + .getVariableText(ptText: 'Excluir', enText: 'Delete'); + final deleteIcon = Icon(Icons.delete, color: iconButtonColor); + Future deleteOnPressed() async { + showAlertDialog( + context, + FFLocalizations.of(context).getVariableText( + ptText: 'Excluir Veículo', + enText: 'Delete Vehicle', + ), + FFLocalizations.of(context).getVariableText( + ptText: 'Você tem certeza que deseja excluir esse veículo?', + enText: 'Are you sure you want to delete this vehicle?', + ), + () async => processDeleteRequest(item), + ); + } + + final bool containStatus = item['status'] == null; + final bool isOwnerVehicle = item['isOwnerVehicle']; + final bool isAGU = item['status'].contains('AGU'); + final bool isAPR = item['status'].contains('APR'); + final bool isATI = item['status'].contains('ATI'); + + if (containStatus) + return [ + FFButtonWidget( + text: deleteText, + icon: deleteIcon, + onPressed: deleteOnPressed, + options: buttonOptions, + ), + ]; + + return [ + if (isAGU && isOwnerVehicle) + FFButtonWidget( + text: updateText, + icon: updateIcon, + onPressed: updateOnPressed, + options: buttonOptions, + ), + if ((isAPR || isAGU) && (isOwnerVehicle)) + FFButtonWidget( + text: cancelText, + icon: cancelIcon, + onPressed: cancelOnPressed, + options: buttonOptions, + ), + if (isATI && isOwnerVehicle) + FFButtonWidget( + text: deleteText, + icon: deleteIcon, + onPressed: deleteOnPressed, + options: buttonOptions, + ), + ]; + } + + Future processDeleteRequest(dynamic item) async { + log('processDeleteRequest -> item[$item]'); + bool result = await PhpGroup.deleteVehicle + .call( + vehicleId: item['vehicleId'], + licensePlate: item['licensePlate'], + model: item['model'], + color: item['color'], + ) + .then((value) { + // ignore: unrelated_type_equality_checks + if (value.jsonBody['error'] == true) { + final String errorMsg = value.jsonBody['error_msg']; + showSnackbarMessenger( + context, + FFLocalizations.of(context).getVariableText( + ptText: errorMsg, + enText: 'Error deleting vehicle', + ), + true, + ); + return false; + // ignore: unrelated_type_equality_checks + } + showSnackbarMessenger( + context, + FFLocalizations.of(context).getVariableText( + enText: 'Success deleting vehicle', + ptText: 'Succeso ao excluir veículo', + ), + false, + ); + return true; + }) // + .catchError((err, stack) { + showSnackbarMessenger( + context, + FFLocalizations.of(context).getVariableText( + enText: 'Error deleting vehicle', + ptText: 'Erro ao excluir veículo', + ), + true, + ); + return false; + }); + + context.pop(result); + context.pop(result); + + return result; + } + + Future processCancelRequest(String status, dynamic item) async { + try { + final ApiCallResponse value; + switch (status) { + case 'APR_CREATE': + value = await processCancelDeleteRequest(item); + break; + case 'AGU_CHANGE': + value = await processCancelUpdateRequest(item); + break; + case 'APR_DELETE': + value = await processCancelCreateRequest(item); + break; + default: + throw ArgumentError('Status inválido: $status'); + } + + final bool isError = value.jsonBody['error'] == true; + final String message = FFLocalizations.of(context).getVariableText( + ptText: value.jsonBody['error_msg'] ?? 'Erro ao cancelar solicitação', + enText: + isError ? 'Error canceling request' : 'Success canceling request', + ); + showSnackbarMessenger(context, message, isError); + context.pop(!isError); + context.pop(!isError); + return !isError; + } catch (err) { + final String errorMessage = FFLocalizations.of(context).getVariableText( + ptText: 'Erro ao cancelar solicitação', + enText: 'Error canceling request', + ); + showSnackbarMessenger(context, errorMessage, true); + context.pop(false); + context.pop(false); + return false; + } + } + + Future processCancelDeleteRequest(dynamic item) async { + return await PhpGroup.deleteVehicle.call( + vehicleId: item['vehicleId'], + licensePlate: item['licensePlate'], + model: item['model'], + color: item['color'], + ); + } + + Future processCancelUpdateRequest(dynamic item) async { + return await PhpGroup.deleteVehicle.call( + vehicleId: item['vehicleId'], + licensePlate: item['licensePlate'], + model: item['model'], + color: item['color'], + ); + } + + Future processCancelCreateRequest(dynamic item) async { + return await PhpGroup.cancelDelete.call( + vehicleId: item['vehicleId'], + licensePlate: item['licensePlate'], + model: item['model'], + color: item['color'], + ); + } + + Map generateLabelsHashMap(dynamic item) { + return { + if (item['model'] != null && item['model'] != '') + '${FFLocalizations.of(context).getVariableText(ptText: "Modelo", enText: "Model")}:': + item['model'].toString().toUpperCase(), + if (item['licensePlate'] != null && item['licensePlate'] != '') + '${FFLocalizations.of(context).getVariableText(ptText: "Placa", enText: "License Plate")}:': + item['licensePlate'].toString().toUpperCase(), + if (item['color'] != null && item['color'] != '') + '${FFLocalizations.of(context).getVariableText(ptText: "Cor", enText: "Color")}:': + item['color'].toString().toUpperCase(), + if (item['personName'] != null && item['personName'] != '') + '${FFLocalizations.of(context).getVariableText(ptText: "Proprietário", enText: "Owner")}:': + item['personName'].toString().toUpperCase(), + if (item['tag'] != null && item['tag'] != '') + '${FFLocalizations.of(context).getVariableText(ptText: "Tag", enText: "Tag")}:': + item['tag'].toString().toUpperCase(), + }; + } + + DetailsComponentWidget buildVehicleDetails({ + required dynamic item, + required BuildContext context, + required VehicleModel model, + required FreCardIcon? icon, + }) { + final status = generateStatusColorMap(item, true); + final buttons = generateActionButtons(item); + final labels = generateLabelsHashMap(item); + return DetailsComponentWidget( + icon: icon, + buttons: buttons, + labelsHashMap: labels, + statusHashMap: [status], ); } } diff --git a/lib/pages/vehicles_on_the_property/vehicle_register_screen.dart b/lib/pages/vehicles_on_the_property/vehicle_register_screen.dart new file mode 100644 index 00000000..ff32d4ab --- /dev/null +++ b/lib/pages/vehicles_on_the_property/vehicle_register_screen.dart @@ -0,0 +1,141 @@ +part of 'vehicles_on_the_property.dart'; + +/// [VehicleRegisterScreen] is a StatefulWidget that displays a form to register a vehicle. + +// ignore: must_be_immutable +class VehicleRegisterScreen extends StatefulWidget { + VehicleRegisterScreen(this.model, {super.key}); + late VehicleModel model; + + @override + State createState() => _VehicleRegisterScreenState(); +} + +class _VehicleRegisterScreenState extends State { + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + _buildHeader(context), + _buildBody(context), + ], + ), + ); + } + + Form _buildBody(BuildContext context) { + return Form( + key: widget.model.registerFormKey, + autovalidateMode: AutovalidateMode.onUserInteraction, + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _buildCustomInput( + context: context, + controller: widget.model.textFieldControllerLicensePlate!, + validator: widget.model.textControllerLicensePlateValidator, + focusNode: widget.model.textFieldFocusLicensePlate!, + labelText: FFLocalizations.of(context) + .getVariableText(ptText: 'Placa', enText: 'License Plate'), + hintText: FFLocalizations.of(context) + .getVariableText(ptText: 'Placa', enText: 'License Plate'), + suffixIcon: Symbols.format_color_text, + inputFormatters: [UpperCaseTextFormatter()], + maxLength: 7, + ), + _buildCustomInput( + context: context, + controller: widget.model.textFieldControllerModel!, + validator: widget.model.textControllerModelValidator, + focusNode: widget.model.textFieldFocusModel!, + labelText: FFLocalizations.of(context) + .getVariableText(ptText: 'Modelo', enText: 'Model'), + hintText: FFLocalizations.of(context).getVariableText( + ptText: 'Ex: Voyage, Ford', enText: 'e.g. Voyage, Ford'), + suffixIcon: Symbols.car_repair, + inputFormatters: [], + ), + _buildCustomInput( + context: context, + controller: widget.model.textFieldControllerColor!, + validator: widget.model.textControllerColorValidator, + focusNode: widget.model.textFieldFocusColor!, + labelText: FFLocalizations.of(context) + .getVariableText(ptText: 'Cor', enText: 'Color'), + hintText: FFLocalizations.of(context).getVariableText( + ptText: 'Ex: Preto, Amarelo, Branco', + enText: 'e.g. Black, Yellow, White'), + suffixIcon: Symbols.palette, + inputFormatters: [], + ), + Padding( + padding: const EdgeInsets.fromLTRB(70, 20, 70, 30), + child: SubmitButtonUtil( + labelText: FFLocalizations.of(context) + .getVariableText(ptText: 'Cadastrar', enText: 'Register'), + onPressed: widget.model + .isFormValid(context, widget.model.registerFormKey) + ? widget.model.registerVehicle + : null, + ), + ), + ], + ), + ); + } + + Widget _buildCustomInput({ + required BuildContext context, + required TextEditingController controller, + required String? Function(BuildContext, String?) validator, + required FocusNode focusNode, + required String labelText, + required String hintText, + required IconData suffixIcon, + required final List? inputFormatters, + int maxLength = 80, + }) { + return CustomInputUtil( + controller: controller, + validator: (value) => validator(context, value), + focusNode: focusNode, + labelText: labelText, + hintText: hintText, + suffixIcon: suffixIcon, + haveMaxLength: true, + onChanged: (value) => setState(() {}), + inputFormatters: inputFormatters, + maxLength: maxLength, + ); + } + + Align _buildHeader(BuildContext context) { + double limitedHeaderFontSize = + LimitedFontSizeUtil.getHeaderFontSize(context); + + return Align( + alignment: const AlignmentDirectional(-1.0, 0.0), + child: Padding( + padding: const EdgeInsetsDirectional.fromSTEB(24.0, 20, 0.0, 15), + child: Text( + FFLocalizations.of(context).getVariableText( + ptText: + 'Preencha o formulário de cadastro com os dados do seu veículo', + enText: 'Fill out the registration form with your vehicle data', + ), + textAlign: TextAlign.start, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap() + .containsKey(FlutterFlowTheme.of(context).bodyMediumFamily), + fontSize: limitedHeaderFontSize, + ), + ), + ), + ); + } +} diff --git a/lib/pages/vehicles_on_the_property/vehicle_update_screen.dart b/lib/pages/vehicles_on_the_property/vehicle_update_screen.dart new file mode 100644 index 00000000..1f746b50 --- /dev/null +++ b/lib/pages/vehicles_on_the_property/vehicle_update_screen.dart @@ -0,0 +1,140 @@ +part of 'vehicles_on_the_property.dart'; + +/// [VehicleUpdateScreen] is a StatefulWidget that displays a form to update a vehicle. + +// ignore: must_be_immutable +class VehicleUpdateScreen extends StatefulWidget { + VehicleUpdateScreen(this.model, {super.key}); + late VehicleModel model; + + @override + State createState() => _VehicleUpdateScreenState(); +} + +class _VehicleUpdateScreenState extends State { + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + _buildHeader(context), + buildBody(context), + ], + ), + ); + } + + Form buildBody(BuildContext context) { + return Form( + key: widget.model.updateFormKey, + autovalidateMode: AutovalidateMode.onUserInteraction, + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _buildCustomInput( + context: context, + controller: widget.model.textFieldControllerLicensePlate!, + validator: widget.model.textControllerLicensePlateValidator, + focusNode: widget.model.textFieldFocusLicensePlate!, + labelText: FFLocalizations.of(context) + .getVariableText(ptText: 'Placa', enText: 'License Plate'), + hintText: FFLocalizations.of(context) + .getVariableText(ptText: 'Placa', enText: 'License Plate'), + suffixIcon: Symbols.format_color_text, + inputFormatters: [UpperCaseTextFormatter()], + maxLength: 7, + ), + _buildCustomInput( + context: context, + controller: widget.model.textFieldControllerModel!, + validator: widget.model.textControllerModelValidator, + focusNode: widget.model.textFieldFocusModel!, + labelText: FFLocalizations.of(context) + .getVariableText(ptText: 'Modelo', enText: 'Model'), + hintText: FFLocalizations.of(context).getVariableText( + ptText: 'Ex: Voyage, Ford', enText: 'e.g. Voyage, Ford'), + suffixIcon: Symbols.car_repair, + inputFormatters: [], + ), + _buildCustomInput( + context: context, + controller: widget.model.textFieldControllerColor!, + validator: widget.model.textControllerColorValidator, + focusNode: widget.model.textFieldFocusColor!, + labelText: FFLocalizations.of(context) + .getVariableText(ptText: 'Cor', enText: 'Color'), + hintText: FFLocalizations.of(context).getVariableText( + ptText: 'Ex: Preto, Amarelo, Branco', + enText: 'e.g. Black, Yellow, White'), + suffixIcon: Symbols.palette, + inputFormatters: [], + ), + _buildSubmitButton(context), + ], + ), + ); + } + + Widget _buildHeader(BuildContext context) { + return Align( + alignment: const AlignmentDirectional(-1.0, 0.0), + child: Padding( + padding: const EdgeInsetsDirectional.fromSTEB(24.0, 20, 0.0, 15), + child: Text( + FFLocalizations.of(context).getVariableText( + ptText: + 'Preencha o formulário de alteração com os dados do seu veículo', + enText: 'Fill out the update form with your vehicle data', + ), + textAlign: TextAlign.start, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap() + .containsKey(FlutterFlowTheme.of(context).bodyMediumFamily), + ), + ), + ), + ); + } + + Widget _buildCustomInput({ + required BuildContext context, + required TextEditingController controller, + required String? Function(BuildContext, String?) validator, + required FocusNode focusNode, + required String labelText, + required String hintText, + required IconData suffixIcon, + required List? inputFormatters, + int maxLength = 80, + }) { + return CustomInputUtil( + controller: controller, + validator: (value) => validator(context, value), + focusNode: focusNode, + labelText: labelText, + hintText: hintText, + suffixIcon: suffixIcon, + haveMaxLength: true, + onChanged: (value) => setState(() {}), + inputFormatters: inputFormatters, + maxLength: maxLength, + ); + } + + Widget _buildSubmitButton(BuildContext context) { + return Padding( + padding: const EdgeInsets.fromLTRB(70, 20, 70, 30), + child: SubmitButtonUtil( + labelText: FFLocalizations.of(context) + .getVariableText(ptText: 'Salvar', enText: 'Save'), + onPressed: widget.model.isFormValid(context, widget.model.updateFormKey) + ? () => widget.model.updateVehicle() + : null, + ), + ); + } +} diff --git a/lib/pages/vehicles_on_the_property/vehicles_on_the_property.dart b/lib/pages/vehicles_on_the_property/vehicles_on_the_property.dart index c27a067e..9e51e429 100644 --- a/lib/pages/vehicles_on_the_property/vehicles_on_the_property.dart +++ b/lib/pages/vehicles_on_the_property/vehicles_on_the_property.dart @@ -1,149 +1,184 @@ +import 'dart:collection'; +import 'dart:developer'; + +import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:hub/components/atomic_components/shared_components_atoms/custom_input.dart'; +import 'package:hub/components/atomic_components/shared_components_atoms/submit_button.dart'; +import 'package:hub/components/atomic_components/shared_components_atoms/tabview.dart'; import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart'; import 'package:hub/features/backend/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/features/module/index.dart'; +import 'package:hub/flutter_flow/index.dart'; import 'package:hub/pages/vehicles_on_the_property/vehicle_model.dart'; +import 'package:hub/shared/extensions/index.dart'; +import 'package:hub/shared/mixins/pageable_mixin.dart'; import 'package:hub/shared/utils/dialog_util.dart'; +import 'package:hub/shared/utils/license_util.dart'; import 'package:hub/shared/utils/limited_text_size.dart'; import 'package:hub/shared/utils/log_util.dart'; +import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; +import 'package:material_symbols_icons/symbols.dart'; -class VehicleOnTheProperty extends StatefulWidget { - const VehicleOnTheProperty({super.key}); +part 'vehicle_history_screen.dart'; +part 'vehicle_register_screen.dart'; +part 'vehicle_update_screen.dart'; + +/// [VehiclePage] is a StatefulWidget that displays the vehicle screens. +class VehiclePage extends StatefulWidget { + const VehiclePage({super.key}); @override - _VehicleOnThePropertyState createState() => _VehicleOnThePropertyState(); + // ignore: library_private_types_in_public_api + _VehiclePageState createState() => _VehiclePageState(); } -class _VehicleOnThePropertyState extends State +class _VehiclePageState extends State with TickerProviderStateMixin { - late ScrollController _scrollController; - - int _pageNumber = 1; - bool _hasData = false; - bool _loading = false; int count = 0; - late final VehicleModel model; - - late Future _future; - List _wrap = []; + late final VehicleModel _model; @override void initState() { super.initState(); - model = createModel(context, () => VehicleModel()); - _future = _fetchVisits(); - _scrollController = ScrollController() - ..addListener(() { - if (_scrollController.position.atEdge && - _scrollController.position.pixels != 0) { - _loadMore(); - } + _model = createModel(context, () => VehicleModel()); + + _model.updateOnChange = true; + _model.onUpdateVehicle = () { + safeSetState(() { + _model.clearFields(); }); + }; + _model.onRegisterVehicle = () { + safeSetState(() { + _model.clearFields(); + }); + }; + _model.safeSetState = () { + safeSetState(() {}); + }; } - @override - void dispose() { - _scrollController.dispose(); - super.dispose(); - } + // @override + // void dispose() { + // super.dispose(); + // } @override Widget build(BuildContext context) { - late final limitedHeaderTextSize = - LimitedFontSizeUtil.getHeaderFontSize(context); - + final backgroundColor = FlutterFlowTheme.of(context).primaryBackground; return Scaffold( - backgroundColor: FlutterFlowTheme.of(context).primaryBackground, - appBar: _appBar(context), - body: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.start, - 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 veículo encontrado!", - enText: "No vehicle found", - ), - style: TextStyle( - fontFamily: 'Nunito', - fontSize: limitedHeaderTextSize, - ), - ), - ) - ], - ), - ) - else if (_hasData == true || _pageNumber >= 1) - Expanded( - child: FutureBuilder( - future: _future, - builder: (context, snapshot) { - return ListView.builder( - shrinkWrap: true, - physics: const BouncingScrollPhysics(), - controller: _scrollController, - itemCount: _wrap.length + 1, - itemBuilder: (context, index) { - if (index == 0) { - // Add your item here - return Padding( - padding: const EdgeInsets.only(right: 30, top: 10), - child: Text( - '', - textAlign: TextAlign.right, - ), - ); - } else { - final item = _wrap[index - 1]; - return _item(context, item); - } - }); - }, - )), - if (_hasData == true && _loading == true) - Container( - padding: const EdgeInsets.only(top: 15, bottom: 15), - child: Center( - child: CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation( - FlutterFlowTheme.of(context).primary, - ), - ), - ), - ) - ].addToStart(const SizedBox(height: 0)), - ), + backgroundColor: backgroundColor, + appBar: _buildHeader(context), + body: _buildBody(context), ); } - PreferredSizeWidget _appBar(BuildContext context) { + /// [Body] of the page. + FutureBuilder _buildBody(BuildContext context) { + Widget progressIndicator() { + return CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + FlutterFlowTheme.of(context).primary, + ), + ); + } + + return FutureBuilder( + future: _initializeModule(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return progressIndicator(); + } else if (snapshot.hasError) { + return progressIndicator(); + } else if (snapshot.hasData && snapshot.data == true) { + return _buildVehicleManager(context); + } else { + return _buildVehicleHistory(context); + } + }, + ); + } + + Future _initializeModule() async { + try { + final module = + await LicenseRepositoryImpl().getModule('FRE-HUB-VEHICLES-MANAGER'); + return await LicenseUtil.processModule(module); + } catch (e) { + WidgetsBinding.instance.addPostFrameCallback((_) async { + context.pop(); + await DialogUtil.errorDefault(navigatorKey.currentContext!); + }); + return false; + } + } + + void onEditingChanged([bool? value]) { + bool isFirst = _model.tabBarController.index == 0; + + if (_model.isEditing & isFirst) { + _model.handleEditingChanged(false); + } + if (isFirst) { + setState(() {}); + } + } + + Widget _buildVehicleHistory(BuildContext context) { + return VehicleHistoryScreen(_model); + } + + Widget _buildVehicleManager(BuildContext context) { + final vehicleHistoryScreenLabel = FFLocalizations.of(context) + .getVariableText(ptText: 'Consultar', enText: 'History'); + final vehicleRegisterScreenLabel = FFLocalizations.of(context) + .getVariableText(ptText: 'Cadastrar', enText: 'Register'); + final vehicleUpdateScreenLabel = FFLocalizations.of(context) + .getVariableText(ptText: 'Editar', enText: 'Edit'); + + return TabViewUtil( + context: context, + model: _model, + labelTab1: vehicleHistoryScreenLabel, + labelTab2: _model.isEditing + ? vehicleUpdateScreenLabel + : vehicleRegisterScreenLabel, + controller: _model.tabBarController, + widget1: VehicleHistoryScreen(_model), + widget2: _model.isEditing + ? VehicleUpdateScreen(_model) + : VehicleRegisterScreen(_model), + onEditingChanged: onEditingChanged, + ); + } + + /// ----------------------------------- + /// [Header] of the page. + PreferredSizeWidget _buildHeader(BuildContext context) { + final theme = FlutterFlowTheme.of(context); + final backgroundColor = theme.primaryBackground; + final primaryText = theme.primaryText; + final title = FFLocalizations.of(context) + .getVariableText(enText: 'Vehicles', ptText: 'Veículos'); + final titleStyle = theme.headlineMedium.override( + fontFamily: theme.headlineMediumFamily, + color: primaryText, + fontSize: 16.0, + fontWeight: FontWeight.bold, + letterSpacing: 0.0, + useGoogleFonts: + GoogleFonts.asMap().containsKey(theme.headlineMediumFamily), + ); + final backButton = _backButton(context, theme); + return AppBar( - backgroundColor: FlutterFlowTheme.of(context).primaryBackground, + backgroundColor: backgroundColor, automaticallyImplyLeading: false, - title: Text( - FFLocalizations.of(context) - .getVariableText(enText: 'Vehicles', ptText: 'Veículos'), - style: FlutterFlowTheme.of(context).headlineMedium.override( - fontFamily: FlutterFlowTheme.of(context).headlineMediumFamily, - color: FlutterFlowTheme.of(context).primaryText, - fontSize: 16.0, - fontWeight: FontWeight.bold, - letterSpacing: 0.0, - useGoogleFonts: GoogleFonts.asMap().containsKey( - FlutterFlowTheme.of(context).headlineMediumFamily), - ), - ), - leading: _backButton(context, FlutterFlowTheme.of(context)), + title: Text(title, style: titleStyle), + leading: backButton, centerTitle: true, elevation: 0.0, actions: [], @@ -151,131 +186,22 @@ class _VehicleOnThePropertyState extends State } Widget _backButton(BuildContext context, FlutterFlowTheme theme) { + final Icon icon = Icon( + Icons.keyboard_arrow_left, + color: theme.primaryText, + size: 30.0, + ); + onPressed() => Navigator.of(context).pop(); return FlutterFlowIconButton( key: ValueKey('BackNavigationAppBar'), 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(), + icon: icon, + onPressed: onPressed, ); } - Future _fetchVisits() async { - try { - setState(() => _loading = true); - - var response = - await PhpGroup.getVehiclesByProperty.call(_pageNumber.toString()); - - final List vehicles = response.jsonBody['vehicles'] ?? []; - safeSetState(() => count = response.jsonBody['total_rows'] ?? 0); - - if (vehicles.isNotEmpty) { - setState(() { - _wrap.addAll(vehicles); - _hasData = true; - _loading = false; - }); - - return response; - } - - _showNoMoreDataSnackBar(context); - - setState(() { - _hasData = false; - _loading = false; - }); - - return null; - } catch (e, s) { - DialogUtil.errorDefault(context); - LogUtil.requestAPIFailed( - "proccessRequest.php", "", "Consulta de Veículo", e, s); - setState(() { - _hasData = false; - _loading = false; - }); - } - return null; - } - - void _loadMore() { - if (_hasData == true) { - _pageNumber++; - - _future = _fetchVisits(); - } - } - - 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 _item(BuildContext context, dynamic uItem) { - return CardItemTemplateComponentWidget( - imagePath: null, - labelsHashMap: { - '${FFLocalizations.of(context).getVariableText(ptText: "Placa", enText: "License Plate")}:': - uItem['licensePlate'] ?? '', - '${FFLocalizations.of(context).getVariableText(ptText: "Modelo", enText: "Model")}:': - uItem['model'] ?? '', - '${FFLocalizations.of(context).getVariableText(ptText: "Tag", enText: "Tag")}:': - uItem['tag'] ?? '', - }, - statusHashMap: [], - onTapCardItemAction: () async { - await showDialog( - useSafeArea: true, - context: context, - builder: (context) { - return Dialog( - alignment: Alignment.center, - child: model.buildVehicleDetails( - item: uItem, - context: context, - model: model, - ), - ); - }, - ).whenComplete(() { - safeSetState(() { - _pageNumber = 1; - _wrap = []; - _future = _fetchVisits() - .then((value) => value!.jsonBody['vehicles'] ?? []); - }); - }).catchError((e, s) { - DialogUtil.errorDefault(context); - LogUtil.requestAPIFailed( - "proccessRequest.php", "", "Consulta de Veículos", e, s); - safeSetState(() { - _hasData = false; - _loading = false; - }); - }); - }, - ); - } + /// ----------------------------------- } diff --git a/lib/pages/visits_on_the_property/model.dart b/lib/pages/visits_on_the_property/model.dart index fec4e8a1..b263e717 100644 --- a/lib/pages/visits_on_the_property/model.dart +++ b/lib/pages/visits_on_the_property/model.dart @@ -6,7 +6,7 @@ import 'package:hub/flutter_flow/flutter_flow_theme.dart'; import 'package:hub/flutter_flow/internationalization.dart'; import 'package:hub/pages/vehicles_on_the_property/vehicles_on_the_property.dart'; -class VisitsModel extends FlutterFlowModel { +class VisitsModel extends FlutterFlowModel { static VisitsModel? _instance; VisitsModel._internal({this.onRefresh}); @@ -82,7 +82,7 @@ class VisitsModel extends FlutterFlowModel { ), }), imagePath: - 'https://freaccess.com.br/freaccess/getImage.php?devUUID=${devUUID}&cliID=${cliUUID}&atividade=getFoto&Documento=${item['VDO_DOCUMENTO']}&tipo=E', + 'https://freaccess.com.br/freaccess/getImage.php?devUUID=$devUUID&cliID=$cliUUID&atividade=getFoto&Documento=${item['VDO_DOCUMENTO']}&tipo=E', statusHashMap: [ Map.from({ if (item['VTA_FIXA'] != null) diff --git a/lib/shared/extensions/string_extensions.dart b/lib/shared/extensions/string_extensions.dart index dc0b62c6..43d536ba 100644 --- a/lib/shared/extensions/string_extensions.dart +++ b/lib/shared/extensions/string_extensions.dart @@ -1,19 +1,25 @@ extension StringNullableExtensions on String? { - bool toBoolean() { + bool get toBoolean { if (this == null) return false; return this!.toLowerCase() == 'true'; } - bool isNullOrEmpty() { + bool get isNullOrEmpty { if (this == null) return true; if (this == '') return true; return false; } + + bool get isNotNullAndEmpty { + if (this == null) return false; + if (this == '') return false; + return true; + } } extension StringExtensions on String { - bool toBoolean() { - return this.toLowerCase() == 'true'; + bool get toBoolean { + return toLowerCase() == 'true'; } } diff --git a/lib/shared/index.dart b/lib/shared/index.dart deleted file mode 100644 index 8b137891..00000000 --- a/lib/shared/index.dart +++ /dev/null @@ -1 +0,0 @@ - diff --git a/lib/shared/mixins/pageable_mixin.dart b/lib/shared/mixins/pageable_mixin.dart new file mode 100644 index 00000000..7e967cf8 --- /dev/null +++ b/lib/shared/mixins/pageable_mixin.dart @@ -0,0 +1,98 @@ +import 'package:flutter/material.dart'; +import 'package:hub/flutter_flow/index.dart'; +import 'package:hub/shared/utils/limited_text_size.dart'; +import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; + +extension PagedListViewExtension + on PagedSliverList {} + +mixin Pageable on State { + Expanded buildPaginatedListView( + String noDataFound, + PagingController pg, + Widget Function(BuildContext, Y, int) itemBuilder) { + final theme = FlutterFlowTheme.of(context); + + return Expanded( + child: RefreshIndicator( + backgroundColor: theme.primaryBackground, + color: theme.primary, + onRefresh: () async => pg.refresh(), + child: PagedListView( + pagingController: pg, + builderDelegate: PagedChildBuilderDelegate( + animateTransitions: true, + + itemBuilder: (context, item, index) => + itemBuilder(context, item, index), + // noMoreItemsIndicatorBuilder: , + newPageProgressIndicatorBuilder: (context) => + buildLoadingIndicator(context), + firstPageProgressIndicatorBuilder: (context) => + buildLoadingIndicator(context), + noItemsFoundIndicatorBuilder: (context) => + buildNoDataFound(context, noDataFound), + // firstPageErrorIndicatorBuilder: (context) => const Placeholder(), + // newPageErrorIndicatorBuilder: (context) => const Placeholder(), + ), + ), + ), + ); + } + + Future fetchPage({ + required Future<(bool, dynamic)> Function() dataProvider, + required void Function(dynamic data) onDataAvailable, + required void Function(dynamic data) onDataUnavailable, + required void Function(Object error, StackTrace stackTrace) onFetchError, + }) async { + try { + final (bool isDataAvailable, dynamic data) = await dataProvider(); + if (isDataAvailable) { + onDataAvailable(data); + } else { + onDataUnavailable(data); + } + } catch (error, stackTrace) { + onFetchError(error, stackTrace); + } + } + + void showNoMoreDataSnackBar(BuildContext context) { + final message = FFLocalizations.of(context).getVariableText( + ptText: "Não há mais dados.", + enText: "No more data.", + ); + + showSnackbarMessenger(context, message, true); + } + + Widget buildNoDataFound(BuildContext context, String title) { + final headerFontSize = LimitedFontSizeUtil.getHeaderFontSize(context); + // final bodyFontSize = LimitedFontSizeUtil.getBodyFontSize(context); + return Expanded( + child: Center( + child: Text( + title, + style: TextStyle( + fontFamily: 'Nunito', + fontSize: headerFontSize, + ), + ), + ), + ); + } + + Widget buildLoadingIndicator(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 15), + child: Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + FlutterFlowTheme.of(context).primary, + ), + ), + ), + ); + } +} diff --git a/lib/shared/utils/datetime_util.dart b/lib/shared/utils/datetime_util.dart new file mode 100644 index 00000000..cbbc5cb9 --- /dev/null +++ b/lib/shared/utils/datetime_util.dart @@ -0,0 +1,26 @@ +import 'dart:developer'; + +class DateTimeUtil { + static Future processStartDate(String startDate) async { + try { + if (startDate.isEmpty) return true; + final start = DateTime.tryParse(startDate); + if (start == null) return false; + return DateTime.now().isAfter(start); + } catch (e) { + log('Error processing start date for module: $e'); + } + return false; + } + + static Future processExpirationDate(String expirationDate) async { + try { + if (expirationDate.isEmpty) return false; + final expiration = DateTime.tryParse(expirationDate); + return expiration != null && DateTime.now().isAfter(expiration); + } catch (e) { + log('Error processing expiration date for module: $e'); + } + return false; + } +} diff --git a/lib/shared/utils/license_util.dart b/lib/shared/utils/license_util.dart new file mode 100644 index 00000000..4959f1af --- /dev/null +++ b/lib/shared/utils/license_util.dart @@ -0,0 +1,18 @@ +import 'package:hub/features/module/index.dart'; +import 'package:hub/flutter_flow/index.dart'; +import 'package:hub/shared/utils/datetime_util.dart'; + +class LicenseUtil { + static Future processModule(String? module) async { + if (module == null) return false; + final moduleMap = await stringToMap(module); + final startDate = moduleMap['startDate'] ?? ''; + final expirationDate = moduleMap['expirationDate'] ?? ''; + final isStarted = await DateTimeUtil.processStartDate(startDate); + final isExpired = await DateTimeUtil.processExpirationDate(expirationDate); + if (isStarted && !isExpired) + return EnumDisplay.fromString(moduleMap["display"]) == EnumDisplay.active; + if (isExpired) return false; + return false; + } +} diff --git a/lib/shared/utils/text_util.dart b/lib/shared/utils/text_util.dart index d2876a0b..00d40ef9 100644 --- a/lib/shared/utils/text_util.dart +++ b/lib/shared/utils/text_util.dart @@ -6,11 +6,11 @@ class TextUtil extends StatelessWidget { final TextAlign? textAlign; const TextUtil({ - Key? key, + super.key, required this.text, this.style, this.textAlign, - }) : super(key: key); + }); @override Widget build(BuildContext context) { diff --git a/pubspec.yaml b/pubspec.yaml index 26ed4273..b78ebdf7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,6 +15,7 @@ dependencies: sdk: flutter auto_size_text: 3.0.0 barcode_widget: ^2.0.4 + infinite_scroll_pagination: ^4.1.0 cached_network_image: ^3.4.0 firebase_core: ^3.4.0 flutter_inappwebview: ^6.0.0 @@ -103,7 +104,25 @@ dependencies: # crypto: ^3.0.5 freezed_annotation: ^2.4.4 package_info_plus: ^8.1.1 + sliver_tools: ^0.2.12 # json_annotation: ^4.9.0 + base: + git: + url: 'git@github.com:FRE-Informatica/flutter-freaccess-base.git' + path: 'packages/base' + components: + git: + url: 'git@github.com:FRE-Informatica/flutter-freaccess-base.git' + path: 'packages/components' + templates: + git: + url: 'git@github.com:FRE-Informatica/flutter-freaccess-base.git' + path: 'packages/templates' + theme: + git: + url: 'git@github.com:FRE-Informatica/flutter-freaccess-base.git' + path: 'packages/theme' + dependency_overrides: http: 1.2.1 diff --git a/scripts/httpie.sh b/scripts/httpie.sh new file mode 100755 index 00000000..c9e4e2dc --- /dev/null +++ b/scripts/httpie.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Define the base URL for the API endpoint +BASE_URL="https://freaccess.com.br/freaccess/processRequest.php" + +# Define common parameters +DEV_UUID="6b7b81849c115a8a" +USER_UUID="678aa05b0c2154.50583237" +CLI_ID="7" +ACTIVITY="getVehiclesByProperty" +PAGE_SIZE="10" + +# Function to perform the HTTP request +perform_request() { + local page=$1 + http --form POST "$BASE_URL" \ + devUUID="$DEV_UUID" \ + userUUID="$USER_UUID" \ + cliID="$CLI_ID" \ + atividade="$ACTIVITY" \ + page="$page" \ + pageSize="$PAGE_SIZE" \ + --check-status \ + --ignore-stdin \ + --timeout=10 +} + +# Perform requests for pages 1 and 2 +perform_request 1 +perform_request 2