This commit is contained in:
J. A. Messias 2025-01-29 10:27:23 -03:00
parent 3b7a33801f
commit 7dd7ed7a32
14 changed files with 581 additions and 529 deletions

View File

@ -82,7 +82,7 @@ void main() {
// LocalsTest.unlinkLocal();
VehicleTest.vehiclePage();
VehicleTest.historyScreen();
VehicleTest.registerScreen();
VehicleTest.updateScreen();
// VehicleTest.historyScreen();
// VehicleTest.registerScreen();
// VehicleTest.updateScreen();
}

View File

@ -1,7 +1,6 @@
part of 'app_test.dart';
Future<void> _loggedWithMultiLocalsAccount(PatrolIntegrationTester $,
[bool forceLinkedLocal = true]) async {
Future<void> _loggedWithMultiLocalsAccount(PatrolIntegrationTester $, [bool forceLinkedLocal = true]) async {
await _init($);
await StorageHelper() //
.set(SecureStorageKey.isLogged.value, 'true');
@ -19,18 +18,15 @@ Future<void> _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<void> _loggedWithSomeoneLocalAccount(PatrolIntegrationTester $,
[bool forceLinkedLocal = true]) async {
Future<void> _loggedWithSomeoneLocalAccount(PatrolIntegrationTester $, [bool forceLinkedLocal = true]) async {
await _init($);
await StorageHelper() //
.set(SecureStorageKey.isLogged.value, 'true');
@ -48,13 +44,11 @@ Future<void> _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<void> _navigateToSignUp(PatrolIntegrationTester $) async {
}
Future<void> _navigateBackUsingSystemGesture() async =>
IntegrationTestWidgetsFlutterBinding.instance.keyboard
.isLogicalKeyPressed(LogicalKeyboardKey.escape);
IntegrationTestWidgetsFlutterBinding.instance.keyboard.isLogicalKeyPressed(LogicalKeyboardKey.escape);
Future<void> _initializeTracking() async {
print('Requesting tracking authorization...');

View File

@ -64,6 +64,74 @@ class VehicleTest {
}
},
);
patrol(
'License',
(PatrolIntegrationTester tester) async {
$ = tester;
$.tester.printToConsole('Vehicle Page');
await _loggedWithMultiLocalsAccount($);
// await StorageHelper().set(
// LicenseKeys.vehiclesManager.value,
// <String, dynamic>{
// '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,
<String, dynamic>{
'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<void> historyScreen() async {

View File

@ -19,12 +19,10 @@ class BottomArrowLinkedLocalsComponentWidget extends StatefulWidget {
ApiCallResponse? response;
@override
State<BottomArrowLinkedLocalsComponentWidget> createState() =>
_BottomArrowLinkedLocalsComponentWidgetState();
State<BottomArrowLinkedLocalsComponentWidget> createState() => _BottomArrowLinkedLocalsComponentWidgetState();
}
class _BottomArrowLinkedLocalsComponentWidgetState
extends State<BottomArrowLinkedLocalsComponentWidget> {
class _BottomArrowLinkedLocalsComponentWidgetState extends State<BottomArrowLinkedLocalsComponentWidget> {
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<void> _handleError(
BuildContext context, String errorMsg) async {
static Future<void> _handleError(BuildContext context, String errorMsg) async {
await DialogUtil.error(context, errorMsg);
}
Future<dynamic> _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<String, Color> _statusHashMap(dynamic local) {
return Map<String, Color>.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),
)))),

View File

@ -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();

View File

@ -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, '');

View File

@ -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<bool> handleUnavailable(
BuildContext context, List<dynamic> locals) async {
static Future<bool> handleUnavailable(BuildContext context, List<dynamic> 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<bool> 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<bool> updateStorageUtil(Map<String, dynamic> 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<bool> isInactived(List<dynamic> 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<dynamic> 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<bool> 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<bool> 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<bool> 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;
}
}

View File

@ -9,23 +9,16 @@ import 'package:hub/shared/extensions/dialog_extensions.dart';
import 'package:hub/shared/utils/path_util.dart';
abstract class MenuLocalDataSource {
Future<MenuItem?> addMenuEntry(Key key, EnumMenuItem item,
List<MenuItem?> entries, IconData icon, String text, Function() action);
Future<MenuItem?> addMenuEntry(
Key key, EnumMenuItem item, List<MenuItem?> entries, IconData icon, String text, Function() action);
Future<bool> processDisplayDefault(
EnumMenuItem item, MenuEntry opt, List<MenuItem?> entries);
Future<bool> processDisplayDefault(EnumMenuItem item, MenuEntry opt, List<MenuItem?> entries);
Future<void> handleMenu(EnumMenuItem item, EnumDisplay display, MenuEntry opt,
List<MenuItem?> entries);
Future<bool> processStartDate(String startDate, MenuEntry entry);
Future<bool> processExpirationDate(String expirationDate, MenuEntry entry);
Future<void> handleMenu(EnumMenuItem item, EnumDisplay display, MenuEntry opt, List<MenuItem?> 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<bool> processDisplayDefault(
EnumMenuItem item, MenuEntry opt, List<MenuItem?> entries) async {
Future<bool> processDisplayDefault(EnumMenuItem item, MenuEntry opt, List<MenuItem?> entries) async {
if (opt.key == 'FRE-HUB-LOGOUT') {
await addMenuEntry(
ValueKey<String>(opt.key), item, entries, opt.icon, opt.name,
() async {
await addMenuEntry(ValueKey<String>(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<void> handleMenu(EnumMenuItem item, EnumDisplay display, MenuEntry opt,
List<MenuItem?> entries) async {
Future<void> handleMenu(EnumMenuItem item, EnumDisplay display, MenuEntry opt, List<MenuItem?> entries) async {
try {
switch (display.value) {
case 'VISIVEL':
await addMenuEntry(
ValueKey<String>(opt.key), item, entries, opt.icon, opt.name,
() async {
await addMenuEntry(ValueKey<String>(opt.key), item, entries, opt.icon, opt.name, () async {
await PathUtil.nav(opt.route);
});
break;
case 'DESABILITADO':
await addMenuEntry(
ValueKey<String>(opt.key), item, entries, opt.icon, opt.name,
() async {
await DialogUnavailable.unavailableFeature(
navigatorKey.currentContext!);
await addMenuEntry(ValueKey<String>(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<bool> 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<bool> 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;
}
}

View File

@ -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<List<MenuItem?>> entries2Items(
List<MenuEntry> menuEntries, EnumMenuItem menuItem) async {
Future<List<MenuItem?>> entries2Items(List<MenuEntry> menuEntries, EnumMenuItem menuItem) async {
List<MenuItem?> 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<EnumDisplay> processDisplay(
Map<String, dynamic> module, bool isNewVersion) async {
Future<EnumDisplay> processDisplay(Map<String, dynamic> 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<bool> _shouldUpdateDisplay(
Map<String, dynamic> module, bool isNewVersion) async {
const keysToCheck = [
LicenseKeys.residents,
LicenseKeys.vehicles,
LicenseKeys.openedVisits
];
Future<bool> _shouldUpdateDisplay(Map<String, dynamic> module, bool isNewVersion) async {
const keysToCheck = [LicenseKeys.residents, LicenseKeys.vehicles, LicenseKeys.openedVisits];
return isNewVersion && keysToCheck.any((key) => module['key'] == key.value);
}
}

View File

@ -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<VehiclePage> {
/// [VehicleModel] is a class that contains the business logic of the vehicle page.
class VehicleModel extends FlutterFlowModel<VehiclePage>
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<VehiclePage> {
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<FormState> registerFormKey = GlobalKey<FormState>();
final GlobalKey<FormState> updateFormKey = GlobalKey<FormState>();
ApiCallResponse? vehicleResponse;
bool isEditing = false;
Future<void> 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<VehiclePage> {
return null;
}
Future<void> 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<String, Color>? generateStatusColorMap(dynamic uItem) {
final statusMap = {
"ATI": {
@ -179,147 +220,183 @@ class VehicleModel extends FlutterFlowModel<VehiclePage> {
),
);
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<void> 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<void> 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<ApiCallResponse> processCancelDeleteRequest() async {
final int id = item['vehicleId'];
return await PhpGroup.deleteVehicle.call(vehicleId: id);
}
Future<ApiCallResponse> processCancelUpdateRequest() async {
final int id = item['vehicleId'];
return await PhpGroup.deleteVehicle.call(vehicleId: id);
}
Future<ApiCallResponse> processCancelCreateRequest() async {
final int id = item['vehicleId'];
return await PhpGroup.deleteVehicle.call(vehicleId: id);
}
Map<String, String> generateLabelsHashMap(dynamic item) {
return {
if (item['model'] != null && item['model'] != '')
@ -354,21 +431,42 @@ class VehicleModel extends FlutterFlowModel<VehiclePage> {
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<void> 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<void> updateVehicle() async {
final response = await PhpGroup.updateVehicle.call(
licensePlate: textFieldControllerLicensePlate!.text,
@ -398,50 +496,4 @@ class VehicleModel extends FlutterFlowModel<VehiclePage> {
await DialogUtil.error(context, errorMessage);
}
}
Future<void> 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();
}
}

View File

@ -14,89 +14,97 @@ class VehicleRegisterScreen extends StatefulWidget {
class _VehicleRegisterScreenState extends State<VehicleRegisterScreen> {
@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,
),
),
),
);
}
}

View File

@ -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<VehiclePage> 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<bool> _buildBody(BuildContext context) {
Widget progressIndicator() {
return CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
FlutterFlowTheme.of(context).primary,
@ -80,22 +82,14 @@ class _VehiclePageState extends State<VehiclePage> with TickerProviderStateMixin
);
}
Widget errorEvent() {
WidgetsBinding.instance.addPostFrameCallback((_) async {
context.pop();
await DialogUtil.errorDefault(navigatorKey.currentContext!);
});
return progressEvent();
}
return FutureBuilder<String?>(
future: LicenseRepositoryImpl().getModule('FRE-HUB-VEHICLES-MANAGER'),
return FutureBuilder<bool>(
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<VehiclePage> with TickerProviderStateMixin
);
}
Future<bool> _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<VehiclePage> 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;

View File

@ -0,0 +1,26 @@
import 'dart:developer';
class DateTimeUtil {
static Future<bool> 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<bool> 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;
}
}

View File

@ -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<bool> 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;
}
}