From 7dd7ed7a324e8ddc0603adcb19a1828b2dc8dac7 Mon Sep 17 00:00:00 2001 From: "J. A. Messias" Date: Wed, 29 Jan 2025 10:27:23 -0300 Subject: [PATCH] WIP --- integration_test/app_test.dart | 6 +- integration_test/utils_test.dart | 33 +- integration_test/vehicle_test.dart | 68 +++ ..._arrow_linked_locals_component_widget.dart | 82 ++-- .../backend/api_requests/api_calls.dart | 2 +- .../locals_remote_data_source.dart | 2 +- lib/features/local/utils/local_util.dart | 111 ++--- .../data_sources/menu_local_data_source.dart | 64 +-- .../repositories/menu_repository_impl.dart | 34 +- .../vehicle_model.dart | 450 ++++++++++-------- .../vehicle_register_screen.dart | 162 ++++--- .../vehicles_on_the_property.dart | 53 ++- lib/shared/utils/datetime_util.dart | 26 + lib/shared/utils/license_util.dart | 17 + 14 files changed, 581 insertions(+), 529 deletions(-) create mode 100644 lib/shared/utils/datetime_util.dart create mode 100644 lib/shared/utils/license_util.dart diff --git a/integration_test/app_test.dart b/integration_test/app_test.dart index b67c9085..028840ca 100644 --- a/integration_test/app_test.dart +++ b/integration_test/app_test.dart @@ -82,7 +82,7 @@ void main() { // LocalsTest.unlinkLocal(); VehicleTest.vehiclePage(); - VehicleTest.historyScreen(); - VehicleTest.registerScreen(); - VehicleTest.updateScreen(); + // VehicleTest.historyScreen(); + // VehicleTest.registerScreen(); + // VehicleTest.updateScreen(); } diff --git a/integration_test/utils_test.dart b/integration_test/utils_test.dart index 309c0923..d3b4bfca 100644 --- a/integration_test/utils_test.dart +++ b/integration_test/utils_test.dart @@ -1,7 +1,6 @@ part of 'app_test.dart'; -Future _loggedWithMultiLocalsAccount(PatrolIntegrationTester $, - [bool forceLinkedLocal = true]) async { +Future _loggedWithMultiLocalsAccount(PatrolIntegrationTester $, [bool forceLinkedLocal = true]) async { await _init($); await StorageHelper() // .set(SecureStorageKey.isLogged.value, 'true'); @@ -19,18 +18,15 @@ Future _loggedWithMultiLocalsAccount(PatrolIntegrationTester $, await StorageHelper() // .set(LocalsStorageKey.isNewVersion.key, true); if (forceLinkedLocal == true) { - await StorageHelper() // - .set(ProfileStorageKey.clientUUID.key, '7'); - await PhpGroup // - .resopndeVinculo - .call(tarefa: 'A'); - await LicenseRepositoryImpl() // - .resetLicense(); + await StorageHelper().set(ProfileStorageKey.clientUUID.key, '7'); + await StorageHelper().set(ProfileStorageKey.ownerUUID.key, '7'); + await StorageHelper().set(ProfileStorageKey.clientName.key, 'FRE ACCESS DEMO'); + await PhpGroup.respondeVinculo.call(tarefa: 'A'); + await LicenseRepositoryImpl().resetLicense(); } } -Future _loggedWithSomeoneLocalAccount(PatrolIntegrationTester $, - [bool forceLinkedLocal = true]) async { +Future _loggedWithSomeoneLocalAccount(PatrolIntegrationTester $, [bool forceLinkedLocal = true]) async { await _init($); await StorageHelper() // .set(SecureStorageKey.isLogged.value, 'true'); @@ -48,13 +44,11 @@ Future _loggedWithSomeoneLocalAccount(PatrolIntegrationTester $, await StorageHelper() // .set(LocalsStorageKey.isNewVersion.key, true); if (forceLinkedLocal == true) { - await StorageHelper() // - .set(ProfileStorageKey.clientUUID.key, '7'); - await PhpGroup // - .resopndeVinculo - .call(tarefa: 'A'); - await LicenseRepositoryImpl() // - .resetLicense(); + await StorageHelper().set(ProfileStorageKey.clientUUID.key, '7'); + await StorageHelper().set(ProfileStorageKey.ownerUUID.key, '7'); + await StorageHelper().set(ProfileStorageKey.clientName.key, 'FRE ACCESS DEMO'); + await PhpGroup.respondeVinculo.call(tarefa: 'A'); + await LicenseRepositoryImpl().resetLicense(); } } @@ -101,8 +95,7 @@ Future _navigateToSignUp(PatrolIntegrationTester $) async { } Future _navigateBackUsingSystemGesture() async => - IntegrationTestWidgetsFlutterBinding.instance.keyboard - .isLogicalKeyPressed(LogicalKeyboardKey.escape); + IntegrationTestWidgetsFlutterBinding.instance.keyboard.isLogicalKeyPressed(LogicalKeyboardKey.escape); Future _initializeTracking() async { print('Requesting tracking authorization...'); diff --git a/integration_test/vehicle_test.dart b/integration_test/vehicle_test.dart index f96e8580..1fa43131 100644 --- a/integration_test/vehicle_test.dart +++ b/integration_test/vehicle_test.dart @@ -64,6 +64,74 @@ class VehicleTest { } }, ); + patrol( + 'License', + (PatrolIntegrationTester tester) async { + $ = tester; + $.tester.printToConsole('Vehicle Page'); + + await _loggedWithMultiLocalsAccount($); + + // await StorageHelper().set( + // LicenseKeys.vehiclesManager.value, + // { + // 'display': 'VISIVEL', + // 'expirationDate': '', + // 'startDate': '', + // 'quantity': 0, + // }, + // ); + // // await $.pumpAndSettle(); + // ff.navigatorKey.currentContext!.go('/vehiclesOnThePropertyPage'); + // await Future.delayed(const Duration(milliseconds: 1000)); + + await StorageHelper().set( + LicenseKeys.vehiclesManager.value, + { + 'display': 'VISIVEL', + 'expirationDate': '', + 'startDate': '', + 'quantity': 0, + }, + ); + 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 { 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..0b3ddd85 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 @@ -19,12 +19,10 @@ class BottomArrowLinkedLocalsComponentWidget extends StatefulWidget { ApiCallResponse? response; @override - State createState() => - _BottomArrowLinkedLocalsComponentWidgetState(); + State createState() => _BottomArrowLinkedLocalsComponentWidgetState(); } -class _BottomArrowLinkedLocalsComponentWidgetState - extends State { +class _BottomArrowLinkedLocalsComponentWidgetState extends State { late BottomArrowLinkedLocalsComponentModel _model; bool _loading = false; @@ -42,8 +40,7 @@ class _BottomArrowLinkedLocalsComponentWidgetState @override void initState() { super.initState(); - _model = - createModel(context, () => BottomArrowLinkedLocalsComponentModel()); + _model = createModel(context, () => BottomArrowLinkedLocalsComponentModel()); _localsFuture = _fetchLocals(); } @@ -109,14 +106,10 @@ class _BottomArrowLinkedLocalsComponentWidgetState if (isEnabled) { final local = locals[0]; - await StorageHelper() - .set(ProfileStorageKey.clientName.key, local['CLI_NOME']); - await StorageHelper() - .set(ProfileStorageKey.ownerName.key, local['CLU_OWNER_DSC']); - await StorageHelper() - .set(ProfileStorageKey.clientUUID.key, local['CLI_ID']); - await StorageHelper() - .set(ProfileStorageKey.ownerUUID.key, local['CLU_OWNER_ID']); + await StorageHelper().set(ProfileStorageKey.clientName.key, local['CLI_NOME']); + await StorageHelper().set(ProfileStorageKey.ownerName.key, local['CLU_OWNER_DSC']); + await StorageHelper().set(ProfileStorageKey.clientUUID.key, local['CLI_ID']); + await StorageHelper().set(ProfileStorageKey.ownerUUID.key, local['CLU_OWNER_ID']); context.pop(); return widget.response; @@ -140,22 +133,20 @@ class _BottomArrowLinkedLocalsComponentWidgetState return null; } - static Future _handleError( - BuildContext context, String errorMsg) async { + static Future _handleError(BuildContext context, String errorMsg) async { await DialogUtil.error(context, errorMsg); } Future _fetchResponseLink(String status, String cliID) async { try { await StorageHelper().set(ProfileStorageKey.clientUUID.key, cliID); - var response = await PhpGroup.resopndeVinculo.call(tarefa: status); + var response = await PhpGroup.respondeVinculo.call(tarefa: status); if (response.jsonBody['error'] == false) { return { 'error': false, - 'error_msg': FFLocalizations.of(context).getVariableText( - ptText: "Vínculo Ativado com Sucesso", - enText: "Link Activated Successfully") + 'error_msg': FFLocalizations.of(context) + .getVariableText(ptText: "Vínculo Ativado com Sucesso", enText: "Link Activated Successfully") }; } else { await StorageHelper().set(ProfileStorageKey.clientUUID.key, ''); @@ -163,8 +154,7 @@ class _BottomArrowLinkedLocalsComponentWidgetState } } catch (e, s) { await DialogUtil.errorDefault(context); - LogUtil.requestAPIFailed( - 'responderVinculo.php', '', 'Responder Vínculo', e, s); + LogUtil.requestAPIFailed('responderVinculo.php', '', 'Responder Vínculo', e, s); return { 'error': true, 'error_msg': FFLocalizations.of(context).getVariableText( @@ -189,17 +179,14 @@ class _BottomArrowLinkedLocalsComponentWidgetState Map _statusHashMap(dynamic local) { return Map.from({ if (local['CLU_STATUS'] == 'A') - FFLocalizations.of(context).getVariableText( - ptText: 'Ativo', - enText: 'Active'): FlutterFlowTheme.of(context).success + FFLocalizations.of(context).getVariableText(ptText: 'Ativo', enText: 'Active'): + FlutterFlowTheme.of(context).success else if (local['CLU_STATUS'] == 'B') - FFLocalizations.of(context).getVariableText( - ptText: 'Bloqueado', - enText: 'Blocked'): FlutterFlowTheme.of(context).error + FFLocalizations.of(context).getVariableText(ptText: 'Bloqueado', enText: 'Blocked'): + FlutterFlowTheme.of(context).error else - FFLocalizations.of(context).getVariableText( - ptText: 'Pendente', - enText: 'Pending'): FlutterFlowTheme.of(context).warning + FFLocalizations.of(context).getVariableText(ptText: 'Pendente', enText: 'Pending'): + FlutterFlowTheme.of(context).warning }); } @@ -212,21 +199,16 @@ class _BottomArrowLinkedLocalsComponentWidgetState statusHashMap: [_statusHashMap(local)], onTapCardItemAction: () async { if (local['CLU_STATUS'] == 'A') { - await StorageHelper() - .set(ProfileStorageKey.clientUUID.key, local['CLI_ID']); - await StorageHelper() - .set(ProfileStorageKey.clientName.key, local['CLI_NOME']); - await StorageHelper() - .set(ProfileStorageKey.ownerName.key, local['CLU_OWNER_DSC']); - await StorageHelper() - .set(ProfileStorageKey.ownerUUID.key, local['CLU_OWNER_ID']); + await StorageHelper().set(ProfileStorageKey.clientUUID.key, local['CLI_ID']); + await StorageHelper().set(ProfileStorageKey.clientName.key, local['CLI_NOME']); + await StorageHelper().set(ProfileStorageKey.ownerName.key, local['CLU_OWNER_DSC']); + await StorageHelper().set(ProfileStorageKey.ownerUUID.key, local['CLU_OWNER_ID']); context.pop(true); return true; } else if (local['CLU_STATUS'] == 'B') { String message = FFLocalizations.of(context).getVariableText( - ptText: - 'Local Bloqueado para Acesso, Entre em Contato com Administração', + ptText: 'Local Bloqueado para Acesso, Entre em Contato com Administração', enText: 'Location Blocked for Access, Contact Administration', ); @@ -242,8 +224,7 @@ class _BottomArrowLinkedLocalsComponentWidgetState String localName = local['CLI_NOME']; showAlertDialog( context, - FFLocalizations.of(context).getVariableText( - ptText: 'Ativar Vínculo', enText: 'Activate Link'), + FFLocalizations.of(context).getVariableText(ptText: 'Ativar Vínculo', enText: 'Activate Link'), FFLocalizations.of(context).getVariableText( ptText: 'Deseja aceitar o vínculo a $localName?', enText: 'Do you wish to accept the link to $localName?'), @@ -300,8 +281,7 @@ class _BottomArrowLinkedLocalsComponentWidgetState height: height - (height * 0.5), decoration: BoxDecoration( color: FlutterFlowTheme.of(context).primaryBackground, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(25), topRight: Radius.circular(25))), + borderRadius: const BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25))), child: Column( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.start, @@ -313,16 +293,13 @@ class _BottomArrowLinkedLocalsComponentWidgetState mainAxisSize: MainAxisSize.max, children: [ Center( - child: Text(FFLocalizations.of(context).getVariableText( - ptText: "Nenhum Local Encontrado.", - enText: "No local found")), + child: Text(FFLocalizations.of(context) + .getVariableText(ptText: "Nenhum Local Encontrado.", enText: "No local found")), ) ], ), ) - else if (_hasData == true && - _loading == false && - _localsWrap.isNotEmpty) + else if (_hasData == true && _loading == false && _localsWrap.isNotEmpty) Expanded(child: _listItems(context)), if (_loading == true) Container( @@ -339,8 +316,7 @@ class _BottomArrowLinkedLocalsComponentWidgetState padding: const EdgeInsets.only(top: 10), child: Center( child: Text( - FFLocalizations.of(context).getVariableText( - ptText: 'Escolha um local', enText: 'Choose a location'), + FFLocalizations.of(context).getVariableText(ptText: 'Escolha um local', enText: 'Choose a location'), overflow: TextOverflow.ellipsis, style: const TextStyle(fontWeight: FontWeight.bold), )))), diff --git a/lib/features/backend/api_requests/api_calls.dart b/lib/features/backend/api_requests/api_calls.dart index e73db478..1b205891 100644 --- a/lib/features/backend/api_requests/api_calls.dart +++ b/lib/features/backend/api_requests/api_calls.dart @@ -47,7 +47,7 @@ class PhpGroup extends Api { static GetMessagesCall getMessagesCall = GetMessagesCall(); static ChangeNotifica changeNotifica = ChangeNotifica(); static UpdateIDE updateIDE = UpdateIDE(); - static RespondeVinculo resopndeVinculo = RespondeVinculo(); + static RespondeVinculo respondeVinculo = RespondeVinculo(); static ChangePass changePass = ChangePass(); static ChangePanic changePanic = ChangePanic(); static DeleteAccount deleteAccount = DeleteAccount(); diff --git a/lib/features/local/data/data_sources/locals_remote_data_source.dart b/lib/features/local/data/data_sources/locals_remote_data_source.dart index 0babde3a..85ec65fa 100644 --- a/lib/features/local/data/data_sources/locals_remote_data_source.dart +++ b/lib/features/local/data/data_sources/locals_remote_data_source.dart @@ -224,7 +224,7 @@ class LocalsRemoteDataSourceImpl implements LocalsRemoteDataSource { enText: 'Device unlinked successfully', ptText: 'Dispositivo desvinculado com sucesso', ); - final bool status = await PhpGroup.resopndeVinculo.call(tarefa: 'I').then((value) async { + final bool status = await PhpGroup.respondeVinculo.call(tarefa: 'I').then((value) async { if (value.jsonBody['error'] == false) { await StorageHelper().set(ProfileStorageKey.clientName.key, ''); await StorageHelper().set(ProfileStorageKey.ownerName.key, ''); diff --git a/lib/features/local/utils/local_util.dart b/lib/features/local/utils/local_util.dart index 8de35b63..35b6ed37 100644 --- a/lib/features/local/utils/local_util.dart +++ b/lib/features/local/utils/local_util.dart @@ -11,13 +11,10 @@ import 'package:hub/shared/utils/log_util.dart'; class LocalUtil { static void handleError(BuildContext context, String errorMsg) async { - final String devUUID = - await StorageHelper().get(ProfileStorageKey.devUUID.key) ?? ''; - final String userUUID = - await StorageHelper().get(ProfileStorageKey.userUUID.key) ?? ''; + final String devUUID = await StorageHelper().get(ProfileStorageKey.devUUID.key) ?? ''; + final String userUUID = await StorageHelper().get(ProfileStorageKey.userUUID.key) ?? ''; final bool isAuthenticated = userUUID.isNotEmpty && devUUID.isNotEmpty; - final bool isDevLinked = - !errorMsg.contains('Esse dispositivo nao pertence a esse usuario'); + final bool isDevLinked = !errorMsg.contains('Esse dispositivo nao pertence a esse usuario'); log('() => isLinked: $errorMsg'); if (!isAuthenticated) { errorMsg = FFLocalizations.of(context).getVariableText( @@ -39,20 +36,15 @@ class LocalUtil { // await DialogUtil.error(context, errorMsg).whenComplete(() async => await LocalsRemoteDataSourceImpl().selectLocal(context, null)); } - static Future handleUnavailable( - BuildContext context, List locals) async { + static Future handleUnavailable(BuildContext context, List locals) async { log('() => isUnavailable'); try { - await StorageHelper() - .set(ProfileStorageKey.clientUUID.key, locals[0]['CLI_ID']); - await StorageHelper() - .set(ProfileStorageKey.ownerUUID.key, locals[0]['CLU_OWNER_ID']); - await StorageHelper() - .set(ProfileStorageKey.clientName.key, locals[0]['CLI_NOME']); - await StorageHelper() - .set(ProfileStorageKey.ownerName.key, locals[0]['CLU_OWNER_DSC']); + await StorageHelper().set(ProfileStorageKey.clientUUID.key, locals[0]['CLI_ID']); + await StorageHelper().set(ProfileStorageKey.ownerUUID.key, locals[0]['CLU_OWNER_ID']); + await StorageHelper().set(ProfileStorageKey.clientName.key, locals[0]['CLI_NOME']); + await StorageHelper().set(ProfileStorageKey.ownerName.key, locals[0]['CLU_OWNER_DSC']); - var response = await PhpGroup.resopndeVinculo.call(tarefa: 'A'); + var response = await PhpGroup.respondeVinculo.call(tarefa: 'A'); if (response.jsonBody['error'] == true) { await StorageHelper().set(ProfileStorageKey.clientUUID.key, ''); await StorageHelper().set(ProfileStorageKey.ownerUUID.key, ''); @@ -62,13 +54,10 @@ class LocalUtil { return false; } if (response.jsonBody['error'] == false) - return await LocalsRemoteDataSourceImpl() - .processProperty(context) - .then((value) => value); + return await LocalsRemoteDataSourceImpl().processProperty(context).then((value) => value); } catch (e, s) { await DialogUtil.errorDefault(context); - LogUtil.requestAPIFailed( - 'responderVinculo.php', '', 'Responder Vínculo', e, s); + LogUtil.requestAPIFailed('responderVinculo.php', '', 'Responder Vínculo', e, s); return false; } return false; @@ -76,19 +65,12 @@ class LocalUtil { static Future handleEnabled(BuildContext context, dynamic local) async { log('() => isEnabled'); - await StorageHelper() - .set(ProfileStorageKey.clientUUID.key, local['CLI_ID']); - await StorageHelper() - .set(ProfileStorageKey.ownerUUID.key, local['CLU_OWNER_ID']); - await StorageHelper() - .set(ProfileStorageKey.clientName.key, local['CLI_NOME']); - await StorageHelper() - .set(ProfileStorageKey.ownerName.key, local['CLU_OWNER_DSC']); - await StorageHelper() - .set(ProfileStorageKey.userName.key, local['USU_NOME']); - return await LocalsRemoteDataSourceImpl() - .processProperty(context) - .then((v) async { + await StorageHelper().set(ProfileStorageKey.clientUUID.key, local['CLI_ID']); + await StorageHelper().set(ProfileStorageKey.ownerUUID.key, local['CLU_OWNER_ID']); + await StorageHelper().set(ProfileStorageKey.clientName.key, local['CLI_NOME']); + await StorageHelper().set(ProfileStorageKey.ownerName.key, local['CLU_OWNER_DSC']); + await StorageHelper().set(ProfileStorageKey.userName.key, local['USU_NOME']); + return await LocalsRemoteDataSourceImpl().processProperty(context).then((v) async { if (v == true) return await LicenseRepositoryImpl().updateLicense(); return v; }); @@ -111,10 +93,8 @@ class LocalUtil { static Future updateStorageUtil(Map jsonBody) async { try { - await StorageHelper() - .set(LocalsStorageKey.whatsapp.key, jsonBody['whatsapp'] ?? false); - await StorageHelper().set( - LocalsStorageKey.provisional.key, jsonBody['provisional'] ?? false); + await StorageHelper().set(LocalsStorageKey.whatsapp.key, jsonBody['whatsapp'] ?? false); + await StorageHelper().set(LocalsStorageKey.provisional.key, jsonBody['provisional'] ?? false); await StorageHelper().set( LocalsStorageKey.pets.key, jsonBody['pet'] ?? false, @@ -136,21 +116,14 @@ class LocalUtil { ); } - await StorageHelper().set( - LocalsStorageKey.petAmount.key, - jsonBody['petAmountRegister']?.toString().isEmpty ?? true - ? '0' - : jsonBody['petAmountRegister'].toString()); - await StorageHelper().set(ProfileStorageKey.userName.key, - jsonBody['visitado']['VDO_NOME'] ?? ''); - await StorageHelper().set(ProfileStorageKey.userEmail.key, - jsonBody['visitado']['VDO_EMAIL'] ?? ''); - await StorageHelper().set( - LocalsStorageKey.provisional.key, jsonBody['provisional'] ?? false); + await StorageHelper().set(LocalsStorageKey.petAmount.key, + jsonBody['petAmountRegister']?.toString().isEmpty ?? true ? '0' : jsonBody['petAmountRegister'].toString()); + await StorageHelper().set(ProfileStorageKey.userName.key, jsonBody['visitado']['VDO_NOME'] ?? ''); + await StorageHelper().set(ProfileStorageKey.userEmail.key, jsonBody['visitado']['VDO_EMAIL'] ?? ''); + await StorageHelper().set(LocalsStorageKey.provisional.key, jsonBody['provisional'] ?? false); final bool isNewVersion = jsonBody['newVersion'] ?? false; - await StorageHelper() - .set(LocalsStorageKey.isNewVersion.key, isNewVersion); + await StorageHelper().set(LocalsStorageKey.isNewVersion.key, isNewVersion); return isNewVersion; } catch (e, s) { log('Error in _updateStorageUtil: $e', stackTrace: s); @@ -163,44 +136,30 @@ class LocalUtil { } static Future isInactived(List locals) async { - String cliUUID = - (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; - return locals - .where( - (local) => local['CLI_ID'] != cliUUID && local['CLU_STATUS'] == 'A') - .isNotEmpty; + String cliUUID = (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; + return locals.where((local) => local['CLI_ID'] != cliUUID && local['CLU_STATUS'] == 'A').isNotEmpty; } static bool isPending(List locals) { - return locals - .where( - (local) => local['CLU_STATUS'] != 'B' && local['CLU_STATUS'] != 'A') - .isNotEmpty; + return locals.where((local) => local['CLU_STATUS'] != 'B' && local['CLU_STATUS'] != 'A').isNotEmpty; } static Future isUnselected() async { - String cliUUID = - (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; - String cliName = - (await StorageHelper().get(ProfileStorageKey.clientName.key)) ?? ''; - String ownerUUID = - (await StorageHelper().get(ProfileStorageKey.ownerUUID.key)) ?? ''; + String cliUUID = (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; + String cliName = (await StorageHelper().get(ProfileStorageKey.clientName.key)) ?? ''; + String ownerUUID = (await StorageHelper().get(ProfileStorageKey.ownerUUID.key)) ?? ''; return cliUUID.isEmpty && cliName.isEmpty && ownerUUID.isEmpty; } static Future isSelected(bool isInactived) async { - String cliUUID = - (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; - String cliName = - (await StorageHelper().get(ProfileStorageKey.clientName.key)) ?? ''; + String cliUUID = (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; + String cliName = (await StorageHelper().get(ProfileStorageKey.clientName.key)) ?? ''; return cliUUID.isNotEmpty && cliName.isNotEmpty && isInactived; } static Future isAvailable() async { - String cliUUID = - (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; - String cliName = - (await StorageHelper().get(ProfileStorageKey.clientName.key)) ?? ''; + String cliUUID = (await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? ''; + String cliName = (await StorageHelper().get(ProfileStorageKey.clientName.key)) ?? ''; return cliUUID.isNotEmpty && cliName.isNotEmpty; } } 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..b736ed41 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 @@ -9,23 +9,16 @@ import 'package:hub/shared/extensions/dialog_extensions.dart'; import 'package:hub/shared/utils/path_util.dart'; abstract class MenuLocalDataSource { - Future addMenuEntry(Key key, EnumMenuItem item, - List entries, IconData icon, String text, Function() action); + Future addMenuEntry( + Key key, EnumMenuItem item, List entries, IconData icon, String text, Function() action); - Future processDisplayDefault( - EnumMenuItem item, MenuEntry opt, List entries); + Future processDisplayDefault(EnumMenuItem item, MenuEntry opt, List entries); - Future handleMenu(EnumMenuItem item, EnumDisplay display, MenuEntry opt, - List entries); - - Future processStartDate(String startDate, MenuEntry entry); - - Future processExpirationDate(String expirationDate, MenuEntry entry); + Future handleMenu(EnumMenuItem item, EnumDisplay display, MenuEntry opt, List entries); } class MenuLocalDataSourceImpl implements MenuLocalDataSource { - static final MenuLocalDataSourceImpl _instance = - MenuLocalDataSourceImpl._internal(); + static final MenuLocalDataSourceImpl _instance = MenuLocalDataSourceImpl._internal(); factory MenuLocalDataSourceImpl() => _instance; @@ -52,12 +45,9 @@ class MenuLocalDataSourceImpl implements MenuLocalDataSource { } @override - Future processDisplayDefault( - EnumMenuItem item, MenuEntry opt, List entries) async { + Future processDisplayDefault(EnumMenuItem item, MenuEntry opt, List entries) async { if (opt.key == 'FRE-HUB-LOGOUT') { - await addMenuEntry( - ValueKey(opt.key), item, entries, opt.icon, opt.name, - () async { + await addMenuEntry(ValueKey(opt.key), item, entries, opt.icon, opt.name, () async { await AuthenticationService.signOut(navigatorKey.currentContext!); }); return true; @@ -66,23 +56,17 @@ class MenuLocalDataSourceImpl implements MenuLocalDataSource { } @override - Future handleMenu(EnumMenuItem item, EnumDisplay display, MenuEntry opt, - List entries) async { + Future handleMenu(EnumMenuItem item, EnumDisplay display, MenuEntry opt, List entries) async { try { switch (display.value) { case 'VISIVEL': - await addMenuEntry( - ValueKey(opt.key), item, entries, opt.icon, opt.name, - () async { + await addMenuEntry(ValueKey(opt.key), item, entries, opt.icon, opt.name, () async { await PathUtil.nav(opt.route); }); break; case 'DESABILITADO': - await addMenuEntry( - ValueKey(opt.key), item, entries, opt.icon, opt.name, - () async { - await DialogUnavailable.unavailableFeature( - navigatorKey.currentContext!); + await addMenuEntry(ValueKey(opt.key), item, entries, opt.icon, opt.name, () async { + await DialogUnavailable.unavailableFeature(navigatorKey.currentContext!); }); break; case 'INVISIVEL': @@ -92,30 +76,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..bf59361b 100644 --- a/lib/features/menu/data/repositories/menu_repository_impl.dart +++ b/lib/features/menu/data/repositories/menu_repository_impl.dart @@ -4,20 +4,19 @@ 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(); @override - Future> entries2Items( - List menuEntries, EnumMenuItem menuItem) async { + Future> entries2Items(List menuEntries, EnumMenuItem menuItem) async { List entries = []; // final bool isNewVersion = await StorageHelper().get(KeychainStorageKey.isNewVersion.value).then((v) => v.toBoolean()); try { for (var entry in menuEntries) { - final bool isDefault = await menuDataSource.processDisplayDefault( - menuItem, entry, entries); + final bool isDefault = await menuDataSource.processDisplayDefault(menuItem, entry, entries); if (isDefault) continue; final licenseValue = await LicenseRepositoryImpl().getModule(entry.key); if (licenseValue != null) { @@ -25,22 +24,18 @@ 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 isExpired = - await menuDataSource.processExpirationDate(expirationDate, entry); + final isStarted = await DateTimeUtil.processStartDate(startDate); + final isExpired = await DateTimeUtil.processExpirationDate(expirationDate); if (isStarted && !isExpired) { await menuDataSource.handleMenu(menuItem, display, entry, entries); } if (isExpired) { log('Entry ${entry.key} is expired'); - await menuDataSource.handleMenu( - menuItem, EnumDisplay.inactive, entry, entries); + await menuDataSource.handleMenu(menuItem, EnumDisplay.inactive, entry, entries); } if (!isStarted) { log('Entry ${entry.key} is not started'); - await menuDataSource.handleMenu( - menuItem, EnumDisplay.inactive, entry, entries); + await menuDataSource.handleMenu(menuItem, EnumDisplay.inactive, entry, entries); } } } @@ -50,11 +45,9 @@ class MenuRepositoryImpl implements MenuRepository { return entries; } - Future processDisplay( - Map module, bool isNewVersion) async { + Future processDisplay(Map module, bool isNewVersion) async { if (await _shouldUpdateDisplay(module, isNewVersion)) { - final displayValue = - module['display'] == EnumDisplay.active ? 'VISIVEL' : 'INVISIVEL'; + final displayValue = module['display'] == EnumDisplay.active ? 'VISIVEL' : 'INVISIVEL'; await LicenseLocalDataSourceImpl(DatabaseService.database) .setDisplayByKey(['FRE-HUB-ABOUT-PROPERTY'], displayValue); return EnumDisplay.fromString(displayValue); @@ -63,13 +56,8 @@ class MenuRepositoryImpl implements MenuRepository { return EnumDisplay.fromString(module['display']); } - Future _shouldUpdateDisplay( - Map module, bool isNewVersion) async { - const keysToCheck = [ - LicenseKeys.residents, - LicenseKeys.vehicles, - LicenseKeys.openedVisits - ]; + Future _shouldUpdateDisplay(Map module, bool isNewVersion) async { + const keysToCheck = [LicenseKeys.residents, LicenseKeys.vehicles, LicenseKeys.openedVisits]; return isNewVersion && keysToCheck.any((key) => module['key'] == key.value); } } diff --git a/lib/pages/vehicles_on_the_property/vehicle_model.dart b/lib/pages/vehicles_on_the_property/vehicle_model.dart index 4732e9ba..0e225a37 100644 --- a/lib/pages/vehicles_on_the_property/vehicle_model.dart +++ b/lib/pages/vehicles_on_the_property/vehicle_model.dart @@ -5,7 +5,15 @@ import 'package:hub/flutter_flow/index.dart'; import 'package:hub/pages/vehicles_on_the_property/vehicles_on_the_property.dart'; import 'package:hub/shared/utils/index.dart'; -class VehicleModel extends FlutterFlowModel { +/// [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; + @override void initState(BuildContext context) { resetInstance(); @@ -36,25 +44,31 @@ class VehicleModel extends FlutterFlowModel { textFieldControllerModel!.dispose(); } - static VehicleModel? _instance = VehicleModel._internal(); - VehicleModel._internal(); - factory VehicleModel() => _instance ?? VehicleModel._internal(); - static void resetInstance() => _instance = null; - - dynamic item; - String? vehicleId; - - late final TabController tabBarController; - VoidCallback? onUpdateVehicle; - VoidCallback? onRegisterVehicle; - VoidCallback? safeSetState; - final GlobalKey registerFormKey = GlobalKey(); final GlobalKey updateFormKey = GlobalKey(); - ApiCallResponse? vehicleResponse; - bool isEditing = false; + Future initAsync() async {} + + bool isFormValid(BuildContext context) { + if (registerFormKey.currentState == null) return false; + return registerFormKey.currentState!.validate(); + } +} + +/// [_BaseVehiclePage] is a mixin that contains the base logic of the vehicle page. +mixin class _BaseVehiclePage { + int count = 0; + late final VehicleModel model; + late final TabController tabBarController; + dynamic item; BuildContext context = navigatorKey.currentContext!; + bool isEditing = false; + String? vehicleId; + ApiCallResponse? vehicleResponse; + + VoidCallback? onUpdateVehicle; + VoidCallback? onRegisterVehicle; + VoidCallback? safeSetState; FocusNode? textFieldFocusLicensePlate; TextEditingController? textFieldControllerLicensePlate; @@ -105,8 +119,35 @@ class VehicleModel extends FlutterFlowModel { return null; } - Future initAsync() async {} + void switchTab(int index) { + tabBarController.animateTo(index); + if (index == 0) handleEditingChanged(false); + safeSetState?.call(); + } + void clearFields() async { + textFieldControllerLicensePlate!.clear(); + textFieldControllerColor!.clear(); + textFieldControllerModel!.clear(); + } + + void handleEditingChanged(bool editing) { + isEditing = editing; + clearFields(); + } + + void setEditForm() { + if (item != null) { + vehicleId = item['vehicleId']; + textFieldControllerLicensePlate!.text = item['licensePlate']; + textFieldControllerColor!.text = item['color']; + textFieldControllerModel!.text = item['model']; + } + } +} + +/// [_VehicleHistoryScreenModel] is a mixin that contains the business logic of the vehicle history page. +mixin _VehicleHistoryScreenModel on _BaseVehiclePage { Map? generateStatusColorMap(dynamic uItem) { final statusMap = { "ATI": { @@ -179,147 +220,183 @@ class VehicleModel extends FlutterFlowModel { ), ); + final updateText = FFLocalizations.of(context).getVariableText(ptText: 'Editar', enText: 'Edit'); + final updateIcon = Icon(Icons.edit, color: iconButtonColor); + Future updateOnPressed() async { + context.pop(); + isEditing = true; + item = item; + + switchTab(1); + setEditForm(); + } + + 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']), + ); + } + + 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 => await processDeleteRequest(), + ); + } + return [ if (item['status'].contains('AGU')) FFButtonWidget( - text: FFLocalizations.of(context).getVariableText( - ptText: 'Editar', - enText: 'Edit', - ), - icon: Icon( - Icons.close, - color: iconButtonColor, - ), - onPressed: () async { - context.pop(); - isEditing = true; - item = item; - - switchTab(1); - setEditForm(); - }, + text: updateText, + icon: updateIcon, + onPressed: updateOnPressed, options: buttonOptions, ), if (item['status'].contains('APR') || item['status'].contains('AGU')) FFButtonWidget( - text: FFLocalizations.of(context).getVariableText( - ptText: 'Cancelar', - enText: 'Cancel', - ), - icon: Icon(Icons.close, color: iconButtonColor), - onPressed: () 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 { - int id = item['vehicleId']; - await PhpGroup.deleteVehicle.call(vehicleId: id).then((value) { - context.pop(value); - context.pop(value); - - if (value.jsonBody['error'] == false) { - showSnackbar( - context, - FFLocalizations.of(context).getVariableText( - ptText: 'Erro ao cancelar solicitação', - enText: 'Error canceling request', - ), - true, - ); - } else if (value.jsonBody['error'] == true) { - showSnackbar( - context, - FFLocalizations.of(context).getVariableText( - enText: 'Success canceling request', - ptText: 'Succeso ao cancelar solicitação', - ), - false, - ); - } - }).catchError((err, stack) { - context.pop(); - showSnackbar( - context, - FFLocalizations.of(context).getVariableText( - enText: 'Error canceling request', - ptText: 'Erro ao cancelar solicitação', - ), - true, - ); - }); - }); - }, + text: cancelText, + icon: cancelIcon, + onPressed: cancelOnPressed, options: buttonOptions, ), if (item['status'].contains('ATI')) FFButtonWidget( - text: FFLocalizations.of(context).getVariableText( - ptText: 'Excluir', - enText: 'Delete', - ), - icon: Icon( - Icons.close, - color: iconButtonColor, - ), - onPressed: () 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 { - int id = item['vehicleId']; - await PhpGroup.deleteVehicle.call(vehicleId: id).then((value) { - context.pop(value); - context.pop(value); - - if (value == false) { - showSnackbar( - context, - FFLocalizations.of(context).getVariableText( - ptText: 'Erro ao excluir veículo', - enText: 'Error deleting vehicle', - ), - true, - ); - } else if (value == true) { - showSnackbar( - context, - FFLocalizations.of(context).getVariableText( - enText: 'Success deleting vehicle', - ptText: 'Succeso ao excluir veículo', - ), - false, - ); - } - }).catchError((err, stack) { - context.pop(); - showSnackbar( - context, - FFLocalizations.of(context).getVariableText( - enText: 'Error deleting vehicle', - ptText: 'Erro ao excluir veículo', - ), - true, - ); - }); - }); - }, + text: deleteText, + icon: deleteIcon, + onPressed: deleteOnPressed, options: buttonOptions, ), ]; } + Future processDeleteRequest() async { + int id = item['vehicleId']; + await PhpGroup.deleteVehicle.call(vehicleId: id).then((value) { + context.pop(value); + context.pop(value); + + // ignore: unrelated_type_equality_checks + if (value == false) { + showSnackbar( + context, + FFLocalizations.of(context).getVariableText( + ptText: 'Erro ao excluir veículo', + enText: 'Error deleting vehicle', + ), + true, + ); + // ignore: unrelated_type_equality_checks + } else if (value == true) { + showSnackbar( + context, + FFLocalizations.of(context).getVariableText( + enText: 'Success deleting vehicle', + ptText: 'Succeso ao excluir veículo', + ), + false, + ); + } + }).catchError((err, stack) { + context.pop(); + showSnackbar( + context, + FFLocalizations.of(context).getVariableText( + enText: 'Error deleting vehicle', + ptText: 'Erro ao excluir veículo', + ), + true, + ); + }); + } + + Future processCancelRequest(String status) async { + late final ApiCallResponse value; + try { + switch (status) { + case 'APR_CREATE': + value = await processCancelDeleteRequest(); + break; + case 'AGU_CHANGE': + value = await processCancelUpdateRequest(); + break; + case 'APR_DELETE': + value = await processCancelCreateRequest(); + break; + default: + break; + } + + context.pop(value); + context.pop(value); + + if (value.jsonBody['error'] == false) { + showSnackbar( + context, + FFLocalizations.of(context).getVariableText( + ptText: 'Erro ao cancelar solicitação', + enText: 'Error canceling request', + ), + true, + ); + } else if (value.jsonBody['error'] == true) { + showSnackbar( + context, + FFLocalizations.of(context).getVariableText( + enText: 'Success canceling request', + ptText: 'Succeso ao cancelar solicitação', + ), + false, + ); + } + } catch (err) { + context.pop(); + showSnackbar( + context, + FFLocalizations.of(context).getVariableText( + enText: 'Error canceling request', + ptText: 'Erro ao cancelar solicitação', + ), + true, + ); + } + } + + Future processCancelDeleteRequest() async { + final int id = item['vehicleId']; + return await PhpGroup.deleteVehicle.call(vehicleId: id); + } + + Future processCancelUpdateRequest() async { + final int id = item['vehicleId']; + return await PhpGroup.deleteVehicle.call(vehicleId: id); + } + + Future processCancelCreateRequest() async { + final int id = item['vehicleId']; + return await PhpGroup.deleteVehicle.call(vehicleId: id); + } + Map generateLabelsHashMap(dynamic item) { return { if (item['model'] != null && item['model'] != '') @@ -354,21 +431,42 @@ class VehicleModel extends FlutterFlowModel { statusHashMap: [status], ); } +} - void setEditForm() { - if (item != null) { - vehicleId = item['vehicleId']; - textFieldControllerLicensePlate!.text = item['licensePlate']; - textFieldControllerColor!.text = item['color']; - textFieldControllerModel!.text = item['model']; +/// [_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, + ); + if (response.jsonBody['error'] == false) { + await DialogUtil.success( + context, + FFLocalizations.of(context).getVariableText( + ptText: 'Veículo cadastrado com sucesso', + enText: 'Vehicle registered successfully', + )).then((_) async { + switchTab(0); + }); + } else { + String errorMessage; + try { + errorMessage = response.jsonBody['message']; + } catch (e) { + errorMessage = FFLocalizations.of(context).getVariableText( + ptText: 'Erro ao cadastrar veículo', + enText: 'Error registering vehicle', + ); + } + await DialogUtil.error(context, errorMessage); } } +} - bool isFormValid(BuildContext context) { - if (registerFormKey.currentState == null) return false; - return registerFormKey.currentState!.validate(); - } - +/// [_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, @@ -398,50 +496,4 @@ class VehicleModel extends FlutterFlowModel { await DialogUtil.error(context, errorMessage); } } - - Future registerVehicle() async { - final response = await PhpGroup.registerVehicle.call( - licensePlate: textFieldControllerLicensePlate!.text, - color: textFieldControllerColor!.text, - model: textFieldControllerModel!.text, - ); - if (response.jsonBody['error'] == false) { - await DialogUtil.success( - context, - FFLocalizations.of(context).getVariableText( - ptText: 'Veículo cadastrado com sucesso', - enText: 'Vehicle registered successfully', - )).then((_) async { - switchTab(0); - }); - } else { - String errorMessage; - try { - errorMessage = response.jsonBody['message']; - } catch (e) { - errorMessage = FFLocalizations.of(context).getVariableText( - ptText: 'Erro ao cadastrar veículo', - enText: 'Error registering vehicle', - ); - } - await DialogUtil.error(context, errorMessage); - } - } - - void switchTab(int index) { - tabBarController.animateTo(index); - if (index == 0) handleEditingChanged(false); - safeSetState?.call(); - } - - void clearFields() async { - textFieldControllerLicensePlate!.clear(); - textFieldControllerColor!.clear(); - textFieldControllerModel!.clear(); - } - - void handleEditingChanged(bool editing) { - isEditing = editing; - clearFields(); - } } diff --git a/lib/pages/vehicles_on_the_property/vehicle_register_screen.dart b/lib/pages/vehicles_on_the_property/vehicle_register_screen.dart index b6dd5004..07b786a3 100644 --- a/lib/pages/vehicles_on_the_property/vehicle_register_screen.dart +++ b/lib/pages/vehicles_on_the_property/vehicle_register_screen.dart @@ -14,89 +14,97 @@ class VehicleRegisterScreen extends StatefulWidget { class _VehicleRegisterScreenState extends State { @override Widget build(BuildContext context) { - double limitedInputFontSize = LimitedFontSizeUtil.getInputFontSize(context); - double limitedHeaderFontSize = LimitedFontSizeUtil.getHeaderFontSize(context); - double limitedSubHeaderFontSize = LimitedFontSizeUtil.getSubHeaderFontSize(context); - return SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.max, children: [ - 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, - ), - ), - ), - ), - Form( - key: widget.model.registerFormKey, - autovalidateMode: AutovalidateMode.onUserInteraction, - child: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - CustomInputUtil( - controller: widget.model.textFieldControllerLicensePlate, - validator: widget.model.textControllerLicensePlateValidator.asValidator(context), - 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, - haveMaxLength: true, - onChanged: (value) => setState(() {}), - maxLength: 80, - ), - CustomInputUtil( - controller: widget.model.textFieldControllerModel, - validator: widget.model.textControllerModelValidator.asValidator(context), - 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, - haveMaxLength: true, - onChanged: (value) => setState(() {}), - maxLength: 80, - ), - CustomInputUtil( - controller: widget.model.textFieldControllerColor, - validator: widget.model.textControllerColorValidator.asValidator(context), - 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, - haveMaxLength: true, - onChanged: (value) => setState(() {}), - maxLength: 80, - ), - 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.registerVehicle : null), - ), - ], - )), + _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: [ + CustomInputUtil( + controller: widget.model.textFieldControllerLicensePlate, + validator: widget.model.textControllerLicensePlateValidator.asValidator(context), + 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, + haveMaxLength: true, + onChanged: (value) => setState(() {}), + maxLength: 80, + ), + CustomInputUtil( + controller: widget.model.textFieldControllerModel, + validator: widget.model.textControllerModelValidator.asValidator(context), + 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, + haveMaxLength: true, + onChanged: (value) => setState(() {}), + maxLength: 80, + ), + CustomInputUtil( + controller: widget.model.textFieldControllerColor, + validator: widget.model.textControllerColorValidator.asValidator(context), + 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, + haveMaxLength: true, + onChanged: (value) => setState(() {}), + maxLength: 80, + ), + 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.registerVehicle : null), + ), + ], + )); + } + + Align _buildHeader(BuildContext context) { + // double limitedInputFontSize = LimitedFontSizeUtil.getInputFontSize(context); + double limitedHeaderFontSize = LimitedFontSizeUtil.getHeaderFontSize(context); + // double limitedSubHeaderFontSize = LimitedFontSizeUtil.getSubHeaderFontSize(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/vehicles_on_the_property.dart b/lib/pages/vehicles_on_the_property/vehicles_on_the_property.dart index 468bd50f..c96108fb 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 @@ -14,6 +14,7 @@ import 'package:hub/flutter_flow/flutter_flow_util.dart'; import 'package:hub/flutter_flow/index.dart'; import 'package:hub/pages/vehicles_on_the_property/vehicle_model.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:material_symbols_icons/symbols.dart'; @@ -27,6 +28,7 @@ class VehiclePage extends StatefulWidget { const VehiclePage({super.key}); @override + // ignore: library_private_types_in_public_api _VehiclePageState createState() => _VehiclePageState(); } @@ -55,24 +57,24 @@ class _VehiclePageState extends State with TickerProviderStateMixin }; } - @override - void dispose() { - super.dispose(); - } + // @override + // void dispose() { + // super.dispose(); + // } @override Widget build(BuildContext context) { final backgroundColor = FlutterFlowTheme.of(context).primaryBackground; return Scaffold( backgroundColor: backgroundColor, - appBar: _buildAppBar(context), - body: buildBody(context), + appBar: _buildHeader(context), + body: _buildBody(context), ); } /// [Body] of the page. - Widget buildBody(BuildContext context) { - Widget progressEvent() { + FutureBuilder _buildBody(BuildContext context) { + Widget progressIndicator() { return CircularProgressIndicator( valueColor: AlwaysStoppedAnimation( FlutterFlowTheme.of(context).primary, @@ -80,22 +82,14 @@ class _VehiclePageState extends State with TickerProviderStateMixin ); } - Widget errorEvent() { - WidgetsBinding.instance.addPostFrameCallback((_) async { - context.pop(); - await DialogUtil.errorDefault(navigatorKey.currentContext!); - }); - return progressEvent(); - } - - return FutureBuilder( - future: LicenseRepositoryImpl().getModule('FRE-HUB-VEHICLES-MANAGER'), + return FutureBuilder( + future: _initializeModule(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { - return progressEvent(); + return progressIndicator(); } else if (snapshot.hasError) { - return errorEvent(); - } else if (snapshot.hasData && snapshot.data!.isNotEmpty) { + return progressIndicator(); + } else if (snapshot.hasData && snapshot.data == true) { return _buildVehicleManager(context); } else { return _buildVehicleHistory(context); @@ -104,6 +98,19 @@ class _VehiclePageState extends State with TickerProviderStateMixin ); } + 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) { setState(() { _model.handleEditingChanged(value); @@ -134,8 +141,8 @@ class _VehiclePageState extends State with TickerProviderStateMixin } /// ----------------------------------- - /// [AppBar] with the title of the page. - PreferredSizeWidget _buildAppBar(BuildContext context) { + /// [Header] of the page. + PreferredSizeWidget _buildHeader(BuildContext context) { final theme = FlutterFlowTheme.of(context); final backgroundColor = theme.primaryBackground; final primaryText = theme.primaryText; 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..78e8bcb8 --- /dev/null +++ b/lib/shared/utils/license_util.dart @@ -0,0 +1,17 @@ +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; + } +}