Merge branch 'develop' into feat/fd-465
This commit is contained in:
commit
c0061eabe0
|
@ -1 +1 @@
|
||||||
gradle 7.6.1
|
gradle 8.10.2
|
||||||
|
|
|
@ -7,9 +7,11 @@ import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:hub/components/atomic_components/shared_components_atoms/submit_button.dart';
|
||||||
import 'package:hub/components/molecular_components/throw_exception/throw_exception_widget.dart';
|
import 'package:hub/components/molecular_components/throw_exception/throw_exception_widget.dart';
|
||||||
import 'package:hub/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart';
|
import 'package:hub/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart';
|
||||||
import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart';
|
import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart';
|
||||||
|
import 'package:hub/components/templates_components/details_component/details_component_widget.dart';
|
||||||
import 'package:hub/features/backend/api_requests/index.dart';
|
import 'package:hub/features/backend/api_requests/index.dart';
|
||||||
import 'package:hub/features/local/index.dart';
|
import 'package:hub/features/local/index.dart';
|
||||||
import 'package:hub/features/menu/index.dart';
|
import 'package:hub/features/menu/index.dart';
|
||||||
|
@ -20,6 +22,8 @@ import 'package:hub/features/storage/index.dart';
|
||||||
import 'package:hub/flutter_flow/index.dart' as ff;
|
import 'package:hub/flutter_flow/index.dart' as ff;
|
||||||
import 'package:hub/flutter_flow/index.dart';
|
import 'package:hub/flutter_flow/index.dart';
|
||||||
import 'package:hub/main.dart';
|
import 'package:hub/main.dart';
|
||||||
|
import 'package:hub/pages/vehicles_on_the_property/vehicles_on_the_property.dart';
|
||||||
|
import 'package:integration_test/integration_test.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:flutter_web_plugins/url_strategy.dart';
|
import 'package:flutter_web_plugins/url_strategy.dart';
|
||||||
|
|
||||||
|
@ -27,7 +31,6 @@ import 'app_test.dart';
|
||||||
import 'fuzzer/fuzzer.dart';
|
import 'fuzzer/fuzzer.dart';
|
||||||
|
|
||||||
import 'package:patrol_finders/patrol_finders.dart';
|
import 'package:patrol_finders/patrol_finders.dart';
|
||||||
import 'package:integration_test/integration_test.dart';
|
|
||||||
export 'package:flutter_test/flutter_test.dart';
|
export 'package:flutter_test/flutter_test.dart';
|
||||||
export 'package:patrol/patrol.dart';
|
export 'package:patrol/patrol.dart';
|
||||||
|
|
||||||
|
@ -56,6 +59,7 @@ part 'storage_test.dart';
|
||||||
part 'utils_test.dart';
|
part 'utils_test.dart';
|
||||||
|
|
||||||
part 'welcome_test.dart';
|
part 'welcome_test.dart';
|
||||||
|
part 'vehicle_test.dart';
|
||||||
|
|
||||||
late PatrolTester $;
|
late PatrolTester $;
|
||||||
|
|
||||||
|
@ -83,5 +87,9 @@ void main() {
|
||||||
|
|
||||||
LocalsTest.setLocal();
|
LocalsTest.setLocal();
|
||||||
LocalsTest.unlinkLocal();
|
LocalsTest.unlinkLocal();
|
||||||
LocalsTest.attachLocal();
|
|
||||||
|
VehicleTest.vehiclePage();
|
||||||
|
VehicleTest.historyScreen();
|
||||||
|
VehicleTest.registerScreen();
|
||||||
|
VehicleTest.updateScreen();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,320 @@
|
||||||
|
part of 'app_test.dart';
|
||||||
|
|
||||||
|
class VehicleTest {
|
||||||
|
static Future<void> _initVehicleModule() async {
|
||||||
|
final vehicleParam = <String, dynamic>{
|
||||||
|
'display': 'VISIVEL',
|
||||||
|
'expirationDate': '',
|
||||||
|
'startDate': '',
|
||||||
|
'quantity': 0,
|
||||||
|
};
|
||||||
|
final vehicleManagerParam = <String, dynamic>{
|
||||||
|
'display': 'VISIVEL',
|
||||||
|
'expirationDate': '',
|
||||||
|
'startDate': '',
|
||||||
|
'quantity': 0,
|
||||||
|
};
|
||||||
|
await LicenseRepositoryImpl()
|
||||||
|
.setModule(LicenseKeys.vehicles.value, vehicleParam);
|
||||||
|
await LicenseRepositoryImpl()
|
||||||
|
.setModule(LicenseKeys.vehiclesManager.value, vehicleManagerParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> vehiclePage() async {
|
||||||
|
patrolWidgetTest(
|
||||||
|
'Vehicle Page',
|
||||||
|
(PatrolTester tester) async {
|
||||||
|
$ = tester;
|
||||||
|
$.tester.printToConsole('Vehicle Page');
|
||||||
|
final PatrolFinder throwsException = $(Dialog).$(ThrowExceptionWidget);
|
||||||
|
|
||||||
|
await _loggedWithMultiLocalsAccount();
|
||||||
|
await _initVehicleModule();
|
||||||
|
await $.pumpAndSettle();
|
||||||
|
await $.pumpWidgetAndSettle(const App());
|
||||||
|
|
||||||
|
ff.navigatorKey.currentContext!.go('/vehiclesOnThePropertyPage');
|
||||||
|
|
||||||
|
final String title = MenuEntry.entries //
|
||||||
|
.where((entry) => entry.key == 'FRE-HUB-VEHICLES') //
|
||||||
|
.map((entry) => entry.name)
|
||||||
|
.first;
|
||||||
|
|
||||||
|
final PatrolFinder appBar = await $(AppBar) //
|
||||||
|
.waitUntilExists();
|
||||||
|
final PatrolFinder titleAppBar = await appBar //
|
||||||
|
.$(title)
|
||||||
|
.waitUntilVisible();
|
||||||
|
expect(titleAppBar, findsOneWidget);
|
||||||
|
|
||||||
|
final PatrolFinder tab1 = await $(#TabView_Tab1) //
|
||||||
|
.waitUntilExists();
|
||||||
|
final PatrolFinder tab2 = await $(#TabView_Tab2) //
|
||||||
|
.waitUntilExists();
|
||||||
|
|
||||||
|
await tab2.tap();
|
||||||
|
await Future.delayed(const Duration(milliseconds: 500));
|
||||||
|
await tab1.tap();
|
||||||
|
|
||||||
|
final PatrolFinder listViewFinder = await $(VehicleHistoryScreen) //
|
||||||
|
.$(ListView)
|
||||||
|
.waitUntilVisible();
|
||||||
|
|
||||||
|
expect(listViewFinder, findsOneWidget);
|
||||||
|
|
||||||
|
final PatrolFinder entriesFinder = await $(listViewFinder)
|
||||||
|
.$(CardItemTemplateComponentWidget)
|
||||||
|
.waitUntilVisible();
|
||||||
|
|
||||||
|
expect(entriesFinder, findsWidgets);
|
||||||
|
final int entriesCount = entriesFinder.evaluate().length;
|
||||||
|
await $.pumpAndSettle();
|
||||||
|
|
||||||
|
if (entriesCount > 0)
|
||||||
|
for (int i = 0; i < entriesCount; i++) {
|
||||||
|
await $(entriesFinder.at(i)).scrollTo();
|
||||||
|
|
||||||
|
await $(entriesFinder.at(i))
|
||||||
|
.waitUntilVisible(timeout: const Duration(seconds: 1))
|
||||||
|
.tap(
|
||||||
|
settleTimeout: const Duration(seconds: 1),
|
||||||
|
settlePolicy: SettlePolicy.noSettle,
|
||||||
|
);
|
||||||
|
|
||||||
|
await $.pumpAndSettle(duration: Duration(milliseconds: 500));
|
||||||
|
final PatrolFinder detailsFinder =
|
||||||
|
await $(DetailsComponentWidget).waitUntilVisible();
|
||||||
|
expect(detailsFinder, findsOneWidget);
|
||||||
|
|
||||||
|
await _navigateBackUsingSystemGesture();
|
||||||
|
|
||||||
|
// await $.native.pressBack().then((_) => $.pumpAndSettle());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
patrolWidgetTest(
|
||||||
|
'License',
|
||||||
|
(PatrolTester tester) async {
|
||||||
|
$ = tester;
|
||||||
|
$.tester.printToConsole('Vehicle Page');
|
||||||
|
|
||||||
|
await _loggedWithMultiLocalsAccount();
|
||||||
|
await _initVehicleModule();
|
||||||
|
await $.pumpAndSettle();
|
||||||
|
|
||||||
|
await $.pumpWidgetAndSettle(const App());
|
||||||
|
|
||||||
|
ff.navigatorKey.currentContext!.go('/vehiclesOnThePropertyPage');
|
||||||
|
final String title = MenuEntry.entries //
|
||||||
|
.where((entry) => entry.key == 'FRE-HUB-VEHICLES') //
|
||||||
|
.map((entry) => entry.name)
|
||||||
|
.first;
|
||||||
|
|
||||||
|
final PatrolFinder appBar = await $(AppBar) //
|
||||||
|
.waitUntilExists();
|
||||||
|
final PatrolFinder titleAppBar = await appBar //
|
||||||
|
.$(title)
|
||||||
|
.waitUntilVisible();
|
||||||
|
expect(titleAppBar, findsOneWidget);
|
||||||
|
|
||||||
|
final PatrolFinder tab1 = await $(#TabView_Tab1) //
|
||||||
|
.waitUntilExists();
|
||||||
|
final PatrolFinder tab2 = await $(#TabView_Tab2) //
|
||||||
|
.waitUntilExists();
|
||||||
|
|
||||||
|
await tab2.tap();
|
||||||
|
await Future.delayed(const Duration(milliseconds: 500));
|
||||||
|
await tab1.tap();
|
||||||
|
|
||||||
|
final PatrolFinder listViewFinder = await $(VehicleHistoryScreen) //
|
||||||
|
.$(ListView)
|
||||||
|
.waitUntilVisible();
|
||||||
|
|
||||||
|
expect(listViewFinder, findsOneWidget);
|
||||||
|
|
||||||
|
final PatrolFinder entriesFinder = await $(listViewFinder)
|
||||||
|
.$(CardItemTemplateComponentWidget)
|
||||||
|
.waitUntilVisible();
|
||||||
|
|
||||||
|
expect(entriesFinder, findsWidgets);
|
||||||
|
await $.pumpAndSettle();
|
||||||
|
await Future.delayed(const Duration(milliseconds: 1000));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> historyScreen() async {
|
||||||
|
patrolWidgetTest(
|
||||||
|
'historyScreen',
|
||||||
|
(PatrolTester tester) async {
|
||||||
|
$ = tester;
|
||||||
|
$.tester.printToConsole('Vehicle Page');
|
||||||
|
final PatrolFinder throwsException = $(Dialog).$(ThrowExceptionWidget);
|
||||||
|
|
||||||
|
await _loggedWithMultiLocalsAccount();
|
||||||
|
await _initVehicleModule();
|
||||||
|
await $.pumpAndSettle();
|
||||||
|
await $.pumpWidgetAndSettle(const App());
|
||||||
|
|
||||||
|
ff.navigatorKey.currentContext!.go('/vehiclesOnThePropertyPage');
|
||||||
|
|
||||||
|
final String title = MenuEntry.entries //
|
||||||
|
.where((entry) => entry.key == 'FRE-HUB-VEHICLES') //
|
||||||
|
.map((entry) => entry.name)
|
||||||
|
.first;
|
||||||
|
|
||||||
|
final PatrolFinder appBar = await $(AppBar) //
|
||||||
|
.waitUntilExists();
|
||||||
|
final PatrolFinder titleAppBar = await appBar //
|
||||||
|
.$(title)
|
||||||
|
.waitUntilVisible();
|
||||||
|
expect(titleAppBar, findsOneWidget);
|
||||||
|
|
||||||
|
final PatrolFinder listViewFinder = await $(VehicleHistoryScreen) //
|
||||||
|
.$(ListView)
|
||||||
|
.waitUntilVisible();
|
||||||
|
|
||||||
|
expect(listViewFinder, findsOneWidget);
|
||||||
|
|
||||||
|
final PatrolFinder entriesFinder = await $(listViewFinder)
|
||||||
|
.$(CardItemTemplateComponentWidget)
|
||||||
|
.waitUntilVisible();
|
||||||
|
|
||||||
|
expect(entriesFinder, findsWidgets);
|
||||||
|
await $.pumpAndSettle();
|
||||||
|
await $(entriesFinder.first)
|
||||||
|
.waitUntilVisible(timeout: const Duration(seconds: 1))
|
||||||
|
.tap(
|
||||||
|
settleTimeout: const Duration(seconds: 1),
|
||||||
|
settlePolicy: SettlePolicy.noSettle,
|
||||||
|
);
|
||||||
|
|
||||||
|
await $.pumpAndSettle(duration: Duration(milliseconds: 500));
|
||||||
|
final PatrolFinder detailsFinder =
|
||||||
|
await $(DetailsComponentWidget).waitUntilVisible();
|
||||||
|
expect(detailsFinder, findsOneWidget);
|
||||||
|
|
||||||
|
await _navigateBackUsingSystemGesture();
|
||||||
|
|
||||||
|
/// Iterable Test
|
||||||
|
// final int entriesCount = entriesFinder.evaluate().length;
|
||||||
|
// for (int i = 0; i < entriesCount; i++) {
|
||||||
|
// // await $(entriesFinder.at(i)).scrollTo();
|
||||||
|
|
||||||
|
// await $(entriesFinder.at(i))
|
||||||
|
// .waitUntilVisible(timeout: const Duration(seconds: 1))
|
||||||
|
// .tap(
|
||||||
|
// settleTimeout: const Duration(seconds: 1),
|
||||||
|
// settlePolicy: SettlePolicy.noSettle,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// await $.pumpAndSettle(duration: Duration(milliseconds: 500));
|
||||||
|
// final PatrolFinder detailsFinder =
|
||||||
|
// await $(DetailsComponentWidget).waitUntilVisible();
|
||||||
|
// expect(detailsFinder, findsOneWidget);
|
||||||
|
|
||||||
|
// await _navigateBackUsingSystemGesture();
|
||||||
|
|
||||||
|
// // await $.native.pressBack().then((_) => $.pumpAndSettle());
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> registerScreen() async {
|
||||||
|
patrolWidgetTest(
|
||||||
|
'registerScreen',
|
||||||
|
(PatrolTester tester) async {
|
||||||
|
$ = tester;
|
||||||
|
$.tester.printToConsole('Vehicle Register Page');
|
||||||
|
|
||||||
|
await _loggedWithMultiLocalsAccount();
|
||||||
|
await _initVehicleModule();
|
||||||
|
await $.pumpAndSettle();
|
||||||
|
await $.pumpWidgetAndSettle(const App());
|
||||||
|
|
||||||
|
ff.navigatorKey.currentContext!.go('/vehiclesOnThePropertyPage');
|
||||||
|
|
||||||
|
final PatrolFinder tab2 = await $(#TabView_Tab2) //
|
||||||
|
.waitUntilExists();
|
||||||
|
|
||||||
|
await tab2.tap();
|
||||||
|
|
||||||
|
final PatrolFinder licensePlateField =
|
||||||
|
await $(TextField).at(0).waitUntilVisible();
|
||||||
|
final PatrolFinder modelField =
|
||||||
|
await $(TextField).at(1).waitUntilVisible();
|
||||||
|
final PatrolFinder colorField =
|
||||||
|
await $(TextField).at(2).waitUntilVisible();
|
||||||
|
final PatrolFinder submitButton =
|
||||||
|
await $(SubmitButtonUtil).waitUntilVisible();
|
||||||
|
|
||||||
|
await licensePlateField.enterText('ABC1234');
|
||||||
|
await modelField.enterText('Voyage');
|
||||||
|
await colorField.enterText('Black');
|
||||||
|
await submitButton.tap();
|
||||||
|
|
||||||
|
await $.pumpAndSettle();
|
||||||
|
final PatrolFinder successDialog = await $(Dialog).waitUntilVisible();
|
||||||
|
expect(successDialog, findsOneWidget);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> updateScreen() async {
|
||||||
|
patrolWidgetTest(
|
||||||
|
'updateScreen',
|
||||||
|
(PatrolTester tester) async {
|
||||||
|
$ = tester;
|
||||||
|
$.tester.printToConsole('Vehicle Update Page');
|
||||||
|
|
||||||
|
await _loggedWithMultiLocalsAccount();
|
||||||
|
await _initVehicleModule();
|
||||||
|
await $.pumpAndSettle();
|
||||||
|
await $.pumpWidgetAndSettle(const App());
|
||||||
|
|
||||||
|
ff.navigatorKey.currentContext!.go('/vehiclesOnThePropertyPage');
|
||||||
|
|
||||||
|
final PatrolFinder tab1 = await $(#TabView_Tab1) //
|
||||||
|
.waitUntilExists();
|
||||||
|
|
||||||
|
await tab1.tap();
|
||||||
|
|
||||||
|
final PatrolFinder listViewFinder = await $(VehicleHistoryScreen) //
|
||||||
|
.$(ListView)
|
||||||
|
.waitUntilVisible();
|
||||||
|
|
||||||
|
expect(listViewFinder, findsOneWidget);
|
||||||
|
|
||||||
|
final PatrolFinder entriesFinder = await $(listViewFinder)
|
||||||
|
.$(CardItemTemplateComponentWidget)
|
||||||
|
.waitUntilVisible();
|
||||||
|
|
||||||
|
expect(entriesFinder, findsWidgets);
|
||||||
|
await $(entriesFinder.at(0)).tap();
|
||||||
|
|
||||||
|
final PatrolFinder editButton =
|
||||||
|
await $(FFButtonWidget).$('Edit').waitUntilVisible();
|
||||||
|
await editButton.tap();
|
||||||
|
|
||||||
|
final PatrolFinder licensePlateField =
|
||||||
|
await $(TextField).at(0).waitUntilVisible();
|
||||||
|
final PatrolFinder modelField =
|
||||||
|
await $(TextField).at(1).waitUntilVisible();
|
||||||
|
final PatrolFinder colorField =
|
||||||
|
await $(TextField).at(2).waitUntilVisible();
|
||||||
|
final PatrolFinder submitButton =
|
||||||
|
await $(SubmitButtonUtil).waitUntilVisible();
|
||||||
|
|
||||||
|
await licensePlateField.enterText('XYZ5678');
|
||||||
|
await modelField.enterText('Fiesta');
|
||||||
|
await colorField.enterText('Red');
|
||||||
|
await submitButton.tap();
|
||||||
|
|
||||||
|
await $.pumpAndSettle();
|
||||||
|
final PatrolFinder successDialog = await $(Dialog).waitUntilVisible();
|
||||||
|
expect(successDialog, findsOneWidget);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,15 @@ import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
||||||
import 'package:hub/shared/utils/limited_text_size.dart';
|
import 'package:hub/shared/utils/limited_text_size.dart';
|
||||||
|
|
||||||
|
class UpperCaseTextFormatter extends TextInputFormatter {
|
||||||
|
@override
|
||||||
|
TextEditingValue formatEditUpdate(
|
||||||
|
TextEditingValue oldValue, TextEditingValue newValue) {
|
||||||
|
return TextEditingValue(
|
||||||
|
text: newValue.text.toUpperCase(), selection: newValue.selection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ignore: must_be_immutable
|
// ignore: must_be_immutable
|
||||||
class CustomInputUtil extends StatefulWidget {
|
class CustomInputUtil extends StatefulWidget {
|
||||||
final TextEditingController? controller;
|
final TextEditingController? controller;
|
||||||
|
@ -20,22 +29,25 @@ class CustomInputUtil extends StatefulWidget {
|
||||||
final String? Function(String?)? validator;
|
final String? Function(String?)? validator;
|
||||||
final bool haveMaxLength;
|
final bool haveMaxLength;
|
||||||
final void Function(String)? onChanged;
|
final void Function(String)? onChanged;
|
||||||
|
final List<TextInputFormatter>? inputFormatters;
|
||||||
|
|
||||||
CustomInputUtil(
|
CustomInputUtil({
|
||||||
{super.key,
|
super.key,
|
||||||
this.controller,
|
this.controller,
|
||||||
required this.labelText,
|
required this.labelText,
|
||||||
required this.hintText,
|
required this.hintText,
|
||||||
required this.suffixIcon,
|
required this.suffixIcon,
|
||||||
this.autoFocus = false,
|
this.autoFocus = false,
|
||||||
required this.focusNode,
|
required this.focusNode,
|
||||||
this.onChanged,
|
this.onChanged,
|
||||||
this.textInputAction = TextInputAction.next,
|
this.textInputAction = TextInputAction.next,
|
||||||
this.keyboardType = TextInputType.text,
|
this.keyboardType = TextInputType.text,
|
||||||
this.maxLength = 80,
|
this.maxLength = 80,
|
||||||
this.validator,
|
this.validator,
|
||||||
this.obscureText,
|
this.obscureText,
|
||||||
required this.haveMaxLength});
|
this.inputFormatters,
|
||||||
|
required this.haveMaxLength,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CustomInputUtil> createState() => _CustomInputUtilState();
|
State<CustomInputUtil> createState() => _CustomInputUtilState();
|
||||||
|
@ -152,6 +164,7 @@ class _CustomInputUtilState extends State<CustomInputUtil> {
|
||||||
keyboardType: widget.keyboardType,
|
keyboardType: widget.keyboardType,
|
||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
LengthLimitingTextInputFormatter(widget.maxLength),
|
LengthLimitingTextInputFormatter(widget.maxLength),
|
||||||
|
if (widget.inputFormatters != null) ...widget.inputFormatters!
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -10,7 +10,7 @@ class TabViewUtil extends StatelessWidget {
|
||||||
String labelTab1;
|
String labelTab1;
|
||||||
String labelTab2;
|
String labelTab2;
|
||||||
final TabController controller;
|
final TabController controller;
|
||||||
final Function(bool) onEditingChanged;
|
final Function([bool]) onEditingChanged;
|
||||||
Widget widget1;
|
Widget widget1;
|
||||||
Widget widget2;
|
Widget widget2;
|
||||||
|
|
||||||
|
@ -49,15 +49,17 @@ class TabViewUtil extends StatelessWidget {
|
||||||
padding: const EdgeInsets.all(4.0),
|
padding: const EdgeInsets.all(4.0),
|
||||||
tabs: [
|
tabs: [
|
||||||
Tab(
|
Tab(
|
||||||
|
key: ValueKey('TabView_Tab1'),
|
||||||
text: labelTab1,
|
text: labelTab1,
|
||||||
),
|
),
|
||||||
Tab(
|
Tab(
|
||||||
|
key: ValueKey('TabView_Tab2'),
|
||||||
text: labelTab2,
|
text: labelTab2,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
controller: controller,
|
controller: controller,
|
||||||
onTap: (i) async {
|
onTap: (i) async {
|
||||||
if (i == 1) onEditingChanged(false);
|
onEditingChanged();
|
||||||
[() async {}, () async {}][i]();
|
[() async {}, () async {}][i]();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
@ -209,6 +209,7 @@ class _BottomArrowLinkedLocalsComponentWidgetState
|
||||||
return CardItemTemplateComponentWidget(
|
return CardItemTemplateComponentWidget(
|
||||||
key: ValueKey<String>(local['CLI_NOME']),
|
key: ValueKey<String>(local['CLI_NOME']),
|
||||||
imagePath: _imagePath(local),
|
imagePath: _imagePath(local),
|
||||||
|
icon: null,
|
||||||
labelsHashMap: _labelsHashMap(local),
|
labelsHashMap: _labelsHashMap(local),
|
||||||
statusHashMap: [_statusHashMap(local)],
|
statusHashMap: [_statusHashMap(local)],
|
||||||
onTapCardItemAction: () async {
|
onTapCardItemAction: () async {
|
||||||
|
|
|
@ -12,18 +12,74 @@ import 'card_item_template_component_model.dart';
|
||||||
|
|
||||||
export 'card_item_template_component_model.dart';
|
export 'card_item_template_component_model.dart';
|
||||||
|
|
||||||
|
class FreCardIcon extends StatelessWidget {
|
||||||
|
final double height;
|
||||||
|
final double width;
|
||||||
|
final Icon icon;
|
||||||
|
|
||||||
|
const FreCardIcon({
|
||||||
|
super.key,
|
||||||
|
required this.height,
|
||||||
|
required this.width,
|
||||||
|
required this.icon,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
height: height,
|
||||||
|
width: width,
|
||||||
|
child: icon,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FreCardPin extends StatelessWidget {
|
||||||
|
final double height;
|
||||||
|
final double width;
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
const FreCardPin({
|
||||||
|
super.key,
|
||||||
|
required this.height,
|
||||||
|
required this.width,
|
||||||
|
required this.color,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Container(
|
||||||
|
height: height,
|
||||||
|
width: width,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: color,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class CardItemTemplateComponentWidget extends StatefulWidget {
|
class CardItemTemplateComponentWidget extends StatefulWidget {
|
||||||
const CardItemTemplateComponentWidget({
|
const CardItemTemplateComponentWidget({
|
||||||
super.key,
|
super.key,
|
||||||
required this.labelsHashMap,
|
required this.labelsHashMap,
|
||||||
required this.statusHashMap,
|
required this.statusHashMap,
|
||||||
required this.imagePath,
|
this.imagePath,
|
||||||
|
this.icon,
|
||||||
|
this.pin,
|
||||||
|
this.itemWidthFactor = 0.25,
|
||||||
required this.onTapCardItemAction,
|
required this.onTapCardItemAction,
|
||||||
});
|
});
|
||||||
|
|
||||||
final Map<String, String>? labelsHashMap;
|
final Map<String, String>? labelsHashMap;
|
||||||
final List<Map<String, Color>?> statusHashMap;
|
final List<Map<String, Color>?> statusHashMap;
|
||||||
final String? imagePath;
|
final String? imagePath;
|
||||||
|
final FreCardIcon? icon;
|
||||||
|
final FreCardPin? pin;
|
||||||
|
final double itemWidthFactor;
|
||||||
final Future Function()? onTapCardItemAction;
|
final Future Function()? onTapCardItemAction;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -125,9 +181,24 @@ class _CardItemTemplateComponentWidgetState
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _generateIcon() {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
widget.icon!,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _generatePin() {
|
||||||
|
return widget.pin!;
|
||||||
|
}
|
||||||
|
|
||||||
List<Widget> _generateStatus() {
|
List<Widget> _generateStatus() {
|
||||||
double limitedBodyTextSize = LimitedFontSizeUtil.getBodyFontSize(context);
|
double limitedBodyTextSize = LimitedFontSizeUtil.getBodyFontSize(context);
|
||||||
|
|
||||||
return statusLinkedHashMap.expand((statusLinked) {
|
return statusLinkedHashMap.expand((statusLinked) {
|
||||||
|
log('statusHashMap: ${statusLinked.length}');
|
||||||
|
|
||||||
return statusLinked.entries.map((entry) {
|
return statusLinked.entries.map((entry) {
|
||||||
final text = entry.key;
|
final text = entry.key;
|
||||||
final color = entry.value;
|
final color = entry.value;
|
||||||
|
@ -136,7 +207,7 @@ class _CardItemTemplateComponentWidgetState
|
||||||
message: text,
|
message: text,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(5),
|
padding: const EdgeInsets.all(5),
|
||||||
width: MediaQuery.of(context).size.width * 0.25,
|
width: MediaQuery.of(context).size.width * widget.itemWidthFactor,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: color,
|
color: color,
|
||||||
borderRadius: BorderRadius.circular(5),
|
borderRadius: BorderRadius.circular(5),
|
||||||
|
@ -162,38 +233,50 @@ class _CardItemTemplateComponentWidgetState
|
||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (context, constraints) {
|
builder: (context, constraints) {
|
||||||
if (constraints.maxWidth > 360) {
|
if (constraints.maxWidth > 360) {
|
||||||
return Row(
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Row(
|
||||||
child: Column(
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
mainAxisSize: MainAxisSize.max,
|
||||||
mainAxisSize: MainAxisSize.min,
|
children: [if (widget.pin != null) _generatePin()]),
|
||||||
children: [
|
Row(
|
||||||
..._generateLabels(),
|
mainAxisSize: MainAxisSize.max,
|
||||||
SizedBox(height: 3),
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
Wrap(
|
children: [
|
||||||
spacing: 8,
|
Expanded(
|
||||||
runSpacing: 4,
|
child: Column(
|
||||||
children: _generateStatus(),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
..._generateLabels(),
|
||||||
|
Wrap(
|
||||||
|
spacing: 8,
|
||||||
|
runSpacing: 4,
|
||||||
|
children: _generateStatus(),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
.addToEnd(const SizedBox(height: 5))
|
||||||
|
.divide(const SizedBox(height: 3))
|
||||||
|
.addToStart(const SizedBox(height: 5)),
|
||||||
),
|
),
|
||||||
]
|
),
|
||||||
.addToEnd(const SizedBox(height: 5))
|
if (widget.icon != null) _generateIcon(),
|
||||||
.divide(const SizedBox(height: 1))
|
if (widget.imagePath != null) _generateImage()
|
||||||
.addToStart(const SizedBox(height: 5)),
|
]
|
||||||
),
|
.addToEnd(const SizedBox(width: 10))
|
||||||
|
.addToStart(const SizedBox(width: 10)),
|
||||||
),
|
),
|
||||||
if (widget.imagePath != null) _generateImage(),
|
].addToStart(SizedBox(height: 5)).addToEnd(SizedBox(height: 5)),
|
||||||
]
|
|
||||||
.addToEnd(const SizedBox(width: 10))
|
|
||||||
.addToStart(const SizedBox(width: 10)),
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (widget.imagePath != null) _generateImage(),
|
if (widget.imagePath != null) _generateImage(),
|
||||||
|
if (widget.icon != null) _generateIcon(),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:collection';
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart';
|
||||||
import 'package:hub/components/templates_components/details_component/details_component_model.dart';
|
import 'package:hub/components/templates_components/details_component/details_component_model.dart';
|
||||||
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
||||||
import 'package:hub/flutter_flow/flutter_flow_util.dart';
|
import 'package:hub/flutter_flow/flutter_flow_util.dart';
|
||||||
|
@ -14,6 +15,7 @@ class DetailsComponentWidget extends StatefulWidget {
|
||||||
required this.labelsHashMap,
|
required this.labelsHashMap,
|
||||||
required this.statusHashMap,
|
required this.statusHashMap,
|
||||||
this.imagePath,
|
this.imagePath,
|
||||||
|
this.icon,
|
||||||
this.onTapCardItemAction,
|
this.onTapCardItemAction,
|
||||||
required this.buttons,
|
required this.buttons,
|
||||||
});
|
});
|
||||||
|
@ -23,6 +25,7 @@ class DetailsComponentWidget extends StatefulWidget {
|
||||||
final String? imagePath;
|
final String? imagePath;
|
||||||
final Future Function()? onTapCardItemAction;
|
final Future Function()? onTapCardItemAction;
|
||||||
final List<Widget>? buttons;
|
final List<Widget>? buttons;
|
||||||
|
final FreCardIcon? icon;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<DetailsComponentWidget> createState() => _DetailsComponentWidgetState();
|
State<DetailsComponentWidget> createState() => _DetailsComponentWidgetState();
|
||||||
|
@ -64,225 +67,232 @@ class _DetailsComponentWidgetState extends State<DetailsComponentWidget> {
|
||||||
// CachedNetworkImage.evictFromCache(widget.imagePath ?? '');
|
// CachedNetworkImage.evictFromCache(widget.imagePath ?? '');
|
||||||
final double limitedBodyFontSize =
|
final double limitedBodyFontSize =
|
||||||
LimitedFontSizeUtil.getBodyFontSize(context);
|
LimitedFontSizeUtil.getBodyFontSize(context);
|
||||||
return Material(
|
return Container(
|
||||||
type: MaterialType.transparency,
|
constraints: BoxConstraints(
|
||||||
child: Container(
|
maxWidth: MediaQuery.of(context).size.width,
|
||||||
constraints: BoxConstraints(
|
maxHeight: MediaQuery.of(context).size.height,
|
||||||
maxWidth: MediaQuery.of(context).size.width,
|
),
|
||||||
maxHeight: MediaQuery.of(context).size.height,
|
decoration: BoxDecoration(
|
||||||
),
|
color: FlutterFlowTheme.of(context).primaryBackground,
|
||||||
decoration: BoxDecoration(
|
borderRadius: const BorderRadius.all(Radius.circular(25.0)),
|
||||||
color: FlutterFlowTheme.of(context).primaryBackground,
|
),
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(25.0)),
|
child: SingleChildScrollView(
|
||||||
),
|
child: Column(
|
||||||
child: SingleChildScrollView(
|
mainAxisSize: MainAxisSize.max,
|
||||||
child: Column(
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.max,
|
children: [
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
SizedBox(height: MediaQuery.of(context).size.height * 0.02),
|
||||||
children: [
|
if (widget.imagePath != null && widget.imagePath != '')
|
||||||
SizedBox(height: MediaQuery.of(context).size.height * 0.02),
|
Container(
|
||||||
if (widget.imagePath != null && widget.imagePath != '')
|
width: MediaQuery.of(context).size.width * 0.3,
|
||||||
Container(
|
height: MediaQuery.of(context).size.width * 0.3,
|
||||||
width: MediaQuery.of(context).size.width * 0.3,
|
clipBehavior: Clip.antiAlias,
|
||||||
height: MediaQuery.of(context).size.width * 0.3,
|
decoration: const BoxDecoration(
|
||||||
clipBehavior: Clip.antiAlias,
|
shape: BoxShape.circle,
|
||||||
decoration: const BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: CachedNetworkImage(
|
|
||||||
fadeInDuration: const Duration(milliseconds: 100),
|
|
||||||
fadeOutDuration: const Duration(milliseconds: 100),
|
|
||||||
imageUrl: widget.imagePath!,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
useOldImageOnUrlChange: true,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
SizedBox(height: MediaQuery.of(context).size.height * 0.03),
|
child: CachedNetworkImage(
|
||||||
Row(
|
fadeInDuration: const Duration(milliseconds: 100),
|
||||||
children: statusLinkedHashMap.expand((linkedHashMap) {
|
fadeOutDuration: const Duration(milliseconds: 100),
|
||||||
return linkedHashMap.entries
|
imageUrl: widget.imagePath!,
|
||||||
.map((MapEntry<String, Color> item) {
|
fit: BoxFit.cover,
|
||||||
return Expanded(
|
useOldImageOnUrlChange: true,
|
||||||
child: Padding(
|
),
|
||||||
padding: EdgeInsets.symmetric(
|
),
|
||||||
horizontal: MediaQuery.of(context).size.width * 0.05,
|
if (widget.icon != null && widget.icon != '')
|
||||||
),
|
Container(
|
||||||
child: TextFormField(
|
width: MediaQuery.of(context).size.width * 0.3,
|
||||||
autofocus: false,
|
height: MediaQuery.of(context).size.width * 0.3,
|
||||||
canRequestFocus: false,
|
clipBehavior: Clip.antiAlias,
|
||||||
readOnly: true,
|
decoration: const BoxDecoration(
|
||||||
obscureText: false,
|
shape: BoxShape.circle,
|
||||||
decoration: InputDecoration(
|
),
|
||||||
isDense: true,
|
child: widget.icon!,
|
||||||
enabledBorder: OutlineInputBorder(
|
),
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
SizedBox(height: MediaQuery.of(context).size.height * 0.03),
|
||||||
borderSide: BorderSide(
|
Row(
|
||||||
color: item.value,
|
children: statusLinkedHashMap.expand((linkedHashMap) {
|
||||||
),
|
return linkedHashMap.entries
|
||||||
),
|
.map((MapEntry<String, Color> item) {
|
||||||
filled: true,
|
return Expanded(
|
||||||
fillColor: item.value,
|
child: Padding(
|
||||||
labelText: item.key,
|
padding: EdgeInsets.symmetric(
|
||||||
labelStyle: FlutterFlowTheme.of(context)
|
horizontal: MediaQuery.of(context).size.width * 0.05,
|
||||||
.labelMedium
|
),
|
||||||
.override(
|
child: TextFormField(
|
||||||
fontFamily: FlutterFlowTheme.of(context)
|
autofocus: false,
|
||||||
.labelMediumFamily,
|
canRequestFocus: false,
|
||||||
fontWeight: FontWeight.bold,
|
readOnly: true,
|
||||||
color: FlutterFlowTheme.of(context).info,
|
initialValue: item.key,
|
||||||
letterSpacing: 0.0,
|
obscureText: false,
|
||||||
useGoogleFonts:
|
decoration: InputDecoration(
|
||||||
GoogleFonts.asMap().containsKey(
|
isDense: true,
|
||||||
FlutterFlowTheme.of(context)
|
enabledBorder: OutlineInputBorder(
|
||||||
.labelMediumFamily,
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
),
|
borderSide: BorderSide(
|
||||||
fontSize: limitedBodyFontSize,
|
color: item.value,
|
||||||
),
|
|
||||||
hintStyle: FlutterFlowTheme.of(context)
|
|
||||||
.labelMedium
|
|
||||||
.override(
|
|
||||||
fontFamily: FlutterFlowTheme.of(context)
|
|
||||||
.labelMediumFamily,
|
|
||||||
color: FlutterFlowTheme.of(context).info,
|
|
||||||
letterSpacing: 0.0,
|
|
||||||
useGoogleFonts:
|
|
||||||
GoogleFonts.asMap().containsKey(
|
|
||||||
FlutterFlowTheme.of(context)
|
|
||||||
.labelMediumFamily,
|
|
||||||
),
|
|
||||||
fontSize: limitedBodyFontSize,
|
|
||||||
),
|
|
||||||
focusedBorder: InputBorder.none,
|
|
||||||
errorBorder: InputBorder.none,
|
|
||||||
focusedErrorBorder: InputBorder.none,
|
|
||||||
suffixIcon: Icon(
|
|
||||||
Icons.info,
|
|
||||||
color: FlutterFlowTheme.of(context).info,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
style: FlutterFlowTheme.of(context)
|
filled: true,
|
||||||
.bodyMedium
|
fillColor: item.value,
|
||||||
|
// labelText: item.key,
|
||||||
|
labelStyle: FlutterFlowTheme.of(context)
|
||||||
|
.labelMedium
|
||||||
.override(
|
.override(
|
||||||
fontFamily: FlutterFlowTheme.of(context)
|
fontFamily: FlutterFlowTheme.of(context)
|
||||||
.bodyMediumFamily,
|
.labelMediumFamily,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
color: FlutterFlowTheme.of(context).info,
|
color: FlutterFlowTheme.of(context).info,
|
||||||
letterSpacing: 0.0,
|
letterSpacing: 0.0,
|
||||||
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
||||||
FlutterFlowTheme.of(context).bodyMediumFamily,
|
FlutterFlowTheme.of(context)
|
||||||
|
.labelMediumFamily,
|
||||||
),
|
),
|
||||||
fontSize: limitedBodyFontSize,
|
fontSize: limitedBodyFontSize,
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
hintStyle: FlutterFlowTheme.of(context)
|
||||||
maxLines: null,
|
.labelMedium
|
||||||
keyboardType: TextInputType.name,
|
.override(
|
||||||
validator: _model.textController1Validator
|
fontFamily: FlutterFlowTheme.of(context)
|
||||||
.asValidator(context),
|
.labelMediumFamily,
|
||||||
),
|
color: FlutterFlowTheme.of(context).info,
|
||||||
),
|
letterSpacing: 0.0,
|
||||||
);
|
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
||||||
}).toList();
|
FlutterFlowTheme.of(context)
|
||||||
}).toList(),
|
.labelMediumFamily,
|
||||||
),
|
),
|
||||||
SizedBox(height: MediaQuery.of(context).size.height * 0.03),
|
fontSize: limitedBodyFontSize,
|
||||||
ListView.builder(
|
),
|
||||||
shrinkWrap: true,
|
focusedBorder: InputBorder.none,
|
||||||
itemCount: labelsLinkedHashMap.length,
|
errorBorder: InputBorder.none,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
focusedErrorBorder: InputBorder.none,
|
||||||
itemBuilder: (context, index) {
|
suffixIcon: Icon(
|
||||||
String key = labelsLinkedHashMap.keys.elementAt(index);
|
Icons.info,
|
||||||
String value = labelsLinkedHashMap[key]!;
|
color: FlutterFlowTheme.of(context).info,
|
||||||
// return Text('key: $key, value: $value');
|
|
||||||
return TextFormField(
|
|
||||||
readOnly: true,
|
|
||||||
initialValue: value,
|
|
||||||
style: FlutterFlowTheme.of(context).bodyMedium.override(
|
|
||||||
fontFamily:
|
|
||||||
FlutterFlowTheme.of(context).bodyMediumFamily,
|
|
||||||
color: FlutterFlowTheme.of(context).primaryText,
|
|
||||||
letterSpacing: 0.0,
|
|
||||||
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
|
||||||
FlutterFlowTheme.of(context).bodyMediumFamily,
|
|
||||||
),
|
),
|
||||||
fontSize: limitedBodyFontSize,
|
|
||||||
),
|
),
|
||||||
decoration: InputDecoration(
|
style: FlutterFlowTheme.of(context)
|
||||||
labelText: key,
|
.labelMedium
|
||||||
filled: true,
|
.override(
|
||||||
fillColor: FlutterFlowTheme.of(context).primaryBackground,
|
fontFamily: FlutterFlowTheme.of(context)
|
||||||
border: OutlineInputBorder(
|
.labelMediumFamily,
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
fontWeight: FontWeight.bold,
|
||||||
borderSide: BorderSide(
|
color: FlutterFlowTheme.of(context).info,
|
||||||
color: FlutterFlowTheme.of(context)
|
letterSpacing: 0.0,
|
||||||
.primaryBackground, // Change border color here
|
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
||||||
),
|
|
||||||
),
|
|
||||||
labelStyle: FlutterFlowTheme.of(context)
|
|
||||||
.labelMedium
|
|
||||||
.override(
|
|
||||||
fontFamily:
|
|
||||||
FlutterFlowTheme.of(context).labelMediumFamily,
|
FlutterFlowTheme.of(context).labelMediumFamily,
|
||||||
color: FlutterFlowTheme.of(context).primaryText,
|
),
|
||||||
letterSpacing: 0.0,
|
fontSize: limitedBodyFontSize,
|
||||||
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
|
||||||
FlutterFlowTheme.of(context).labelMediumFamily,
|
|
||||||
),
|
),
|
||||||
),
|
textAlign: TextAlign.center,
|
||||||
hintStyle: FlutterFlowTheme.of(context)
|
maxLines: null,
|
||||||
.labelMedium
|
keyboardType: TextInputType.name,
|
||||||
.override(
|
validator: _model.textController1Validator
|
||||||
fontFamily:
|
.asValidator(context),
|
||||||
FlutterFlowTheme.of(context).labelMediumFamily,
|
|
||||||
color: FlutterFlowTheme.of(context).primaryText,
|
|
||||||
letterSpacing: 0.0,
|
|
||||||
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
|
||||||
FlutterFlowTheme.of(context).labelMediumFamily,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: FlutterFlowTheme.of(context)
|
|
||||||
.primaryBackground, // Change border color here
|
|
||||||
),
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: FlutterFlowTheme.of(context)
|
|
||||||
.primaryBackground, // Change border color here
|
|
||||||
),
|
|
||||||
),
|
|
||||||
errorBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: FlutterFlowTheme.of(context)
|
|
||||||
.primaryBackground, // Change border color here
|
|
||||||
),
|
|
||||||
),
|
|
||||||
focusedErrorBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: FlutterFlowTheme.of(context)
|
|
||||||
.primaryBackground, // Change border color here
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
}).toList();
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
SizedBox(height: MediaQuery.of(context).size.height * 0.03),
|
||||||
|
ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: labelsLinkedHashMap.length,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
String key = labelsLinkedHashMap.keys.elementAt(index);
|
||||||
|
String value = labelsLinkedHashMap[key]!;
|
||||||
|
// return Text('key: $key, value: $value');
|
||||||
|
return TextFormField(
|
||||||
|
readOnly: true,
|
||||||
|
initialValue: value,
|
||||||
|
style: FlutterFlowTheme.of(context).bodyMedium.override(
|
||||||
|
fontFamily:
|
||||||
|
FlutterFlowTheme.of(context).bodyMediumFamily,
|
||||||
|
color: FlutterFlowTheme.of(context).primaryText,
|
||||||
|
letterSpacing: 0.0,
|
||||||
|
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
||||||
|
FlutterFlowTheme.of(context).bodyMediumFamily,
|
||||||
|
),
|
||||||
|
fontSize: limitedBodyFontSize,
|
||||||
|
),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: key,
|
||||||
|
filled: true,
|
||||||
|
fillColor: FlutterFlowTheme.of(context).primaryBackground,
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: FlutterFlowTheme.of(context)
|
||||||
|
.primaryBackground, // Change border color here
|
||||||
|
),
|
||||||
|
),
|
||||||
|
labelStyle: FlutterFlowTheme.of(context)
|
||||||
|
.labelMedium
|
||||||
|
.override(
|
||||||
|
fontFamily:
|
||||||
|
FlutterFlowTheme.of(context).labelMediumFamily,
|
||||||
|
color: FlutterFlowTheme.of(context).primaryText,
|
||||||
|
letterSpacing: 0.0,
|
||||||
|
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
||||||
|
FlutterFlowTheme.of(context).labelMediumFamily,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
hintStyle: FlutterFlowTheme.of(context)
|
||||||
|
.labelMedium
|
||||||
|
.override(
|
||||||
|
fontFamily:
|
||||||
|
FlutterFlowTheme.of(context).labelMediumFamily,
|
||||||
|
color: FlutterFlowTheme.of(context).primaryText,
|
||||||
|
letterSpacing: 0.0,
|
||||||
|
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
||||||
|
FlutterFlowTheme.of(context).labelMediumFamily,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: FlutterFlowTheme.of(context)
|
||||||
|
.primaryBackground, // Change border color here
|
||||||
|
),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: FlutterFlowTheme.of(context)
|
||||||
|
.primaryBackground, // Change border color here
|
||||||
|
),
|
||||||
|
),
|
||||||
|
errorBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: FlutterFlowTheme.of(context)
|
||||||
|
.primaryBackground, // Change border color here
|
||||||
|
),
|
||||||
|
),
|
||||||
|
focusedErrorBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: FlutterFlowTheme.of(context)
|
||||||
|
.primaryBackground, // Change border color here
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(height: MediaQuery.of(context).size.height * 0.02),
|
||||||
|
if (widget.buttons!.isNotEmpty || widget.buttons != null)
|
||||||
|
OverflowBar(
|
||||||
|
overflowAlignment: OverflowBarAlignment.center,
|
||||||
|
alignment: MainAxisAlignment.center,
|
||||||
|
overflowSpacing: 2,
|
||||||
|
spacing: 2,
|
||||||
|
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: widget.buttons!,
|
||||||
),
|
),
|
||||||
SizedBox(height: MediaQuery.of(context).size.height * 0.02),
|
SizedBox(height: MediaQuery.of(context).size.height * 0.02),
|
||||||
if (widget.buttons!.isNotEmpty || widget.buttons != null)
|
],
|
||||||
OverflowBar(
|
|
||||||
overflowAlignment: OverflowBarAlignment.center,
|
|
||||||
alignment: MainAxisAlignment.center,
|
|
||||||
overflowSpacing: 2,
|
|
||||||
spacing: 2,
|
|
||||||
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
||||||
children: widget.buttons!,
|
|
||||||
),
|
|
||||||
SizedBox(height: MediaQuery.of(context).size.height * 0.02),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -34,6 +34,7 @@ class RegisiterVistorTemplateComponentWidget extends StatefulWidget {
|
||||||
class _RegisiterVistorTemplateComponentWidgetState
|
class _RegisiterVistorTemplateComponentWidgetState
|
||||||
extends State<RegisiterVistorTemplateComponentWidget> {
|
extends State<RegisiterVistorTemplateComponentWidget> {
|
||||||
late RegisiterVistorTemplateComponentModel _model;
|
late RegisiterVistorTemplateComponentModel _model;
|
||||||
|
final bool _isLoading = false;
|
||||||
final scaffoldKey = GlobalKey<ScaffoldState>();
|
final scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
|
|
||||||
bool _isVisitorRegistered = false;
|
bool _isVisitorRegistered = false;
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
class DeadCode {
|
|
||||||
final String? desc;
|
|
||||||
|
|
||||||
const DeadCode([this.desc = '']);
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
export 'anotations.dart';
|
|
|
@ -75,6 +75,184 @@ class FreAccessWSGlobal extends Api {
|
||||||
@override
|
@override
|
||||||
GetLicense getLicense = GetLicense();
|
GetLicense getLicense = GetLicense();
|
||||||
static GetProvSchedules getProvSchedules = GetProvSchedules();
|
static GetProvSchedules getProvSchedules = GetProvSchedules();
|
||||||
|
static RegisterVehicle registerVehicle = RegisterVehicle();
|
||||||
|
static UpdateVehicle updateVehicle = UpdateVehicle();
|
||||||
|
static DeleteVehicle deleteVehicle = DeleteVehicle();
|
||||||
|
static CancelDeleteVehicle cancelDelete = CancelDeleteVehicle();
|
||||||
|
}
|
||||||
|
|
||||||
|
class CancelDeleteVehicle {
|
||||||
|
Future<ApiCallResponse> call({
|
||||||
|
required final int vehicleId,
|
||||||
|
required final String licensePlate,
|
||||||
|
required final String model,
|
||||||
|
required final String color,
|
||||||
|
}) async {
|
||||||
|
final String baseUrl = PhpGroup.getBaseUrl();
|
||||||
|
final String devUUID =
|
||||||
|
(await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? '';
|
||||||
|
final String userUUID =
|
||||||
|
(await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? '';
|
||||||
|
final String cliID =
|
||||||
|
(await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? '';
|
||||||
|
const String atividade = 'cancelDeleteVehicleRequest';
|
||||||
|
|
||||||
|
return await ApiManager.instance.makeApiCall(
|
||||||
|
callName: atividade,
|
||||||
|
apiUrl: '$baseUrl/processRequest.php',
|
||||||
|
callType: ApiCallType.POST,
|
||||||
|
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||||
|
params: {
|
||||||
|
'devUUID': devUUID,
|
||||||
|
'userUUID': userUUID,
|
||||||
|
'cliID': cliID,
|
||||||
|
'atividade': atividade,
|
||||||
|
'vehicleId': vehicleId,
|
||||||
|
'licensePlate': licensePlate,
|
||||||
|
'model': model,
|
||||||
|
'color': color
|
||||||
|
},
|
||||||
|
bodyType: BodyType.X_WWW_FORM_URL_ENCODED,
|
||||||
|
returnBody: true,
|
||||||
|
encodeBodyUtf8: false,
|
||||||
|
decodeUtf8: false,
|
||||||
|
cache: false,
|
||||||
|
isStreamingApi: false,
|
||||||
|
alwaysAllowBody: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DeleteVehicle {
|
||||||
|
Future<ApiCallResponse> call({
|
||||||
|
required final int vehicleId,
|
||||||
|
required final String licensePlate,
|
||||||
|
required final String model,
|
||||||
|
required final String color,
|
||||||
|
}) async {
|
||||||
|
final String baseUrl = PhpGroup.getBaseUrl();
|
||||||
|
final String devUUID =
|
||||||
|
(await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? '';
|
||||||
|
final String userUUID =
|
||||||
|
(await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? '';
|
||||||
|
final String cliID =
|
||||||
|
(await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? '';
|
||||||
|
const String atividade = 'deleteVehicle';
|
||||||
|
|
||||||
|
return await ApiManager.instance.makeApiCall(
|
||||||
|
callName: 'deleteVehicle',
|
||||||
|
apiUrl: '$baseUrl/processRequest.php',
|
||||||
|
callType: ApiCallType.POST,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
'devUUID': devUUID,
|
||||||
|
'userUUID': userUUID,
|
||||||
|
'cliID': cliID,
|
||||||
|
'atividade': atividade,
|
||||||
|
'vehicleId': vehicleId,
|
||||||
|
'licensePlate': licensePlate,
|
||||||
|
'model': model,
|
||||||
|
'color': color
|
||||||
|
},
|
||||||
|
bodyType: BodyType.X_WWW_FORM_URL_ENCODED,
|
||||||
|
returnBody: true,
|
||||||
|
encodeBodyUtf8: false,
|
||||||
|
decodeUtf8: false,
|
||||||
|
cache: false,
|
||||||
|
isStreamingApi: false,
|
||||||
|
alwaysAllowBody: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RegisterVehicle {
|
||||||
|
Future<ApiCallResponse> call({
|
||||||
|
final String? licensePlate,
|
||||||
|
final String? color,
|
||||||
|
final String? model,
|
||||||
|
}) async {
|
||||||
|
final String baseUrl = PhpGroup.getBaseUrl();
|
||||||
|
|
||||||
|
final String devUUID =
|
||||||
|
(await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? '';
|
||||||
|
final String userUUID =
|
||||||
|
(await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? '';
|
||||||
|
final String cliID =
|
||||||
|
(await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? '';
|
||||||
|
const String atividade = 'insertVehicle';
|
||||||
|
|
||||||
|
return await ApiManager.instance.makeApiCall(
|
||||||
|
callName: 'registerVehicle',
|
||||||
|
apiUrl: '$baseUrl/processRequest.php',
|
||||||
|
callType: ApiCallType.POST,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
'devUUID': devUUID,
|
||||||
|
'userUUID': userUUID,
|
||||||
|
'cliID': cliID,
|
||||||
|
'atividade': atividade,
|
||||||
|
'licensePlate': licensePlate,
|
||||||
|
'color': color,
|
||||||
|
'model': model,
|
||||||
|
},
|
||||||
|
bodyType: BodyType.X_WWW_FORM_URL_ENCODED,
|
||||||
|
returnBody: true,
|
||||||
|
encodeBodyUtf8: false,
|
||||||
|
decodeUtf8: false,
|
||||||
|
cache: false,
|
||||||
|
isStreamingApi: false,
|
||||||
|
alwaysAllowBody: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UpdateVehicle {
|
||||||
|
Future<ApiCallResponse> call({
|
||||||
|
required final int vehicleId,
|
||||||
|
final String? licensePlate,
|
||||||
|
final String? color,
|
||||||
|
final String? model,
|
||||||
|
}) async {
|
||||||
|
final String baseUrl = PhpGroup.getBaseUrl();
|
||||||
|
final String devUUID =
|
||||||
|
(await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? '';
|
||||||
|
final String userUUID =
|
||||||
|
(await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? '';
|
||||||
|
final String cliID =
|
||||||
|
(await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? '';
|
||||||
|
const String atividade = 'updateVehicleToInsertRequest';
|
||||||
|
|
||||||
|
return await ApiManager.instance.makeApiCall(
|
||||||
|
callName: 'updateVehicle',
|
||||||
|
apiUrl: '$baseUrl/processRequest.php',
|
||||||
|
callType: ApiCallType.POST,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
'devUUID': devUUID,
|
||||||
|
'userUUID': userUUID,
|
||||||
|
'cliID': cliID,
|
||||||
|
'atividade': atividade,
|
||||||
|
'licensePlate': licensePlate,
|
||||||
|
'color': color,
|
||||||
|
'model': model,
|
||||||
|
'vehicleId': vehicleId
|
||||||
|
},
|
||||||
|
bodyType: BodyType.X_WWW_FORM_URL_ENCODED,
|
||||||
|
returnBody: true,
|
||||||
|
encodeBodyUtf8: false,
|
||||||
|
decodeUtf8: false,
|
||||||
|
cache: false,
|
||||||
|
isStreamingApi: false,
|
||||||
|
alwaysAllowBody: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
static GetCategories getCategories = GetCategories();
|
static GetCategories getCategories = GetCategories();
|
||||||
static GetDocuments getDocuments = GetDocuments();
|
static GetDocuments getDocuments = GetDocuments();
|
||||||
}
|
}
|
||||||
|
|
|
@ -496,11 +496,11 @@ class ApiManager {
|
||||||
result = ApiCallResponse(null, {}, -1, exception: e);
|
result = ApiCallResponse(null, {}, -1, exception: e);
|
||||||
}
|
}
|
||||||
|
|
||||||
log('API Call: $callName');
|
print('API Call: $callName');
|
||||||
log('URL: $apiUrl');
|
print('URL: $apiUrl');
|
||||||
log('Headers: $headers');
|
print('Headers: $headers');
|
||||||
log('Params: $params');
|
print('Params: $params');
|
||||||
log('Response: ${result.jsonBody}');
|
print('Response: ${result.jsonBody}');
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,12 +46,15 @@ class _AccessHistoryState extends State<AccessHistoryScreen> {
|
||||||
selectedTypeSubject.listen((value) {});
|
selectedTypeSubject.listen((value) {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_model = createModel(context, () => AcessHistoryPageModel());
|
_model = createModel(context, () => AcessHistoryPageModel());
|
||||||
_accessFuture = fetchAccessHistoryService();
|
_accessFuture = fetchAccessHistoryService();
|
||||||
|
|
||||||
|
|
||||||
_scrollController = ScrollController()
|
_scrollController = ScrollController()
|
||||||
..addListener(() {
|
..addListener(() {
|
||||||
if (_scrollController.position.atEdge &&
|
if (_scrollController.position.atEdge &&
|
||||||
|
|
|
@ -80,7 +80,6 @@ class LocalsRepositoryImpl implements LocalsRepository {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log('_handleLocal -> Local selected');
|
log('_handleLocal -> Local selected');
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -115,6 +115,9 @@ class LocalUtil {
|
||||||
.set(LocalsStorageKey.whatsapp.key, jsonBody['whatsapp'] ?? false);
|
.set(LocalsStorageKey.whatsapp.key, jsonBody['whatsapp'] ?? false);
|
||||||
await StorageHelper().set(
|
await StorageHelper().set(
|
||||||
LocalsStorageKey.provisional.key, jsonBody['provisional'] ?? false);
|
LocalsStorageKey.provisional.key, jsonBody['provisional'] ?? false);
|
||||||
|
await StorageHelper().set(LocalsStorageKey.vehicleAutoApproval.key,
|
||||||
|
jsonBody['vehicleAutoApproval'] ?? false);
|
||||||
|
|
||||||
await StorageHelper().set(
|
await StorageHelper().set(
|
||||||
LocalsStorageKey.pets.key,
|
LocalsStorageKey.pets.key,
|
||||||
jsonBody['pet'] ?? false,
|
jsonBody['pet'] ?? false,
|
||||||
|
@ -141,6 +144,13 @@ class LocalUtil {
|
||||||
jsonBody['petAmountRegister']?.toString().isEmpty ?? true
|
jsonBody['petAmountRegister']?.toString().isEmpty ?? true
|
||||||
? '0'
|
? '0'
|
||||||
: jsonBody['petAmountRegister'].toString());
|
: jsonBody['petAmountRegister'].toString());
|
||||||
|
|
||||||
|
await StorageHelper().set(
|
||||||
|
LocalsStorageKey.vehicleAmountRegister.key,
|
||||||
|
jsonBody['vehicleAmountRegister']?.toString().isEmpty ?? true
|
||||||
|
? '0'
|
||||||
|
: jsonBody['vehicleAmountRegister'].toString());
|
||||||
|
|
||||||
await StorageHelper().set(ProfileStorageKey.userName.key,
|
await StorageHelper().set(ProfileStorageKey.userName.key,
|
||||||
jsonBody['visitado']['VDO_NOME'] ?? '');
|
jsonBody['visitado']['VDO_NOME'] ?? '');
|
||||||
await StorageHelper().set(ProfileStorageKey.userEmail.key,
|
await StorageHelper().set(ProfileStorageKey.userEmail.key,
|
||||||
|
|
|
@ -17,10 +17,6 @@ abstract class MenuLocalDataSource {
|
||||||
|
|
||||||
Future<void> handleMenu(EnumMenuItem item, EnumDisplay display, MenuEntry opt,
|
Future<void> handleMenu(EnumMenuItem item, EnumDisplay display, MenuEntry opt,
|
||||||
List<MenuItem?> entries);
|
List<MenuItem?> entries);
|
||||||
|
|
||||||
Future<bool> processStartDate(String startDate, MenuEntry entry);
|
|
||||||
|
|
||||||
Future<bool> processExpirationDate(String expirationDate, MenuEntry entry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MenuLocalDataSourceImpl implements MenuLocalDataSource {
|
class MenuLocalDataSourceImpl implements MenuLocalDataSource {
|
||||||
|
@ -92,30 +88,4 @@ class MenuLocalDataSourceImpl implements MenuLocalDataSource {
|
||||||
log('Error processing display for module ${opt.key}: $e');
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:hub/features/menu/index.dart';
|
||||||
import 'package:hub/features/module/index.dart';
|
import 'package:hub/features/module/index.dart';
|
||||||
import 'package:hub/features/storage/index.dart';
|
import 'package:hub/features/storage/index.dart';
|
||||||
import 'package:hub/flutter_flow/custom_functions.dart';
|
import 'package:hub/flutter_flow/custom_functions.dart';
|
||||||
|
import 'package:hub/shared/utils/datetime_util.dart';
|
||||||
|
|
||||||
class MenuRepositoryImpl implements MenuRepository {
|
class MenuRepositoryImpl implements MenuRepository {
|
||||||
final MenuLocalDataSource menuDataSource = MenuLocalDataSourceImpl();
|
final MenuLocalDataSource menuDataSource = MenuLocalDataSourceImpl();
|
||||||
|
@ -25,10 +26,9 @@ class MenuRepositoryImpl implements MenuRepository {
|
||||||
final display = EnumDisplay.fromString(licenseMap['display']);
|
final display = EnumDisplay.fromString(licenseMap['display']);
|
||||||
final startDate = licenseMap['startDate'] ?? '';
|
final startDate = licenseMap['startDate'] ?? '';
|
||||||
final expirationDate = licenseMap['expirationDate'] ?? '';
|
final expirationDate = licenseMap['expirationDate'] ?? '';
|
||||||
final isStarted =
|
final isStarted = await DateTimeUtil.processStartDate(startDate);
|
||||||
await menuDataSource.processStartDate(startDate, entry);
|
|
||||||
final isExpired =
|
final isExpired =
|
||||||
await menuDataSource.processExpirationDate(expirationDate, entry);
|
await DateTimeUtil.processExpirationDate(expirationDate);
|
||||||
if (isStarted && !isExpired) {
|
if (isStarted && !isExpired) {
|
||||||
await menuDataSource.handleMenu(menuItem, display, entry, entries);
|
await menuDataSource.handleMenu(menuItem, display, entry, entries);
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,16 @@ class MenuEntry implements BaseModule {
|
||||||
route: '/residentsOnThePropertyPage',
|
route: '/residentsOnThePropertyPage',
|
||||||
types: [MenuEntryType.Property],
|
types: [MenuEntryType.Property],
|
||||||
),
|
),
|
||||||
|
// MenuEntry(
|
||||||
|
// key: 'FRE-HUB-VEHICLES-MANAGER',
|
||||||
|
// icon: Icons.directions_car,
|
||||||
|
// name: FFLocalizations.of(navigatorKey.currentContext!).getVariableText(
|
||||||
|
// ptText: 'Veículos',
|
||||||
|
// enText: 'Vehicles',
|
||||||
|
// ),
|
||||||
|
// route: '/vehiclesOnThePropertyPage',
|
||||||
|
// types: [MenuEntryType.Property],
|
||||||
|
// ),
|
||||||
MenuEntry(
|
MenuEntry(
|
||||||
key: 'FRE-HUB-VEHICLES',
|
key: 'FRE-HUB-VEHICLES',
|
||||||
icon: Icons.directions_car,
|
icon: Icons.directions_car,
|
||||||
|
|
|
@ -94,7 +94,7 @@ class LicenseLocalDataSourceImpl implements LicenseLocalDataSource {
|
||||||
Future<bool> isNewVersion() async {
|
Future<bool> isNewVersion() async {
|
||||||
final String? reponse =
|
final String? reponse =
|
||||||
await StorageHelper().get(LocalsStorageKey.isNewVersion.key);
|
await StorageHelper().get(LocalsStorageKey.isNewVersion.key);
|
||||||
final bool isNewVersion = reponse.toBoolean();
|
final bool isNewVersion = reponse.toBoolean;
|
||||||
return isNewVersion;
|
return isNewVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ enum LicenseKeys {
|
||||||
access('FRE-HUB-ACCESS'),
|
access('FRE-HUB-ACCESS'),
|
||||||
openedVisits('FRE-HUB-OPENED-VISITS'),
|
openedVisits('FRE-HUB-OPENED-VISITS'),
|
||||||
vehicles('FRE-HUB-VEHICLES'),
|
vehicles('FRE-HUB-VEHICLES'),
|
||||||
|
vehiclesManager('FRE-HUB-VEHICLES-MANAGER'),
|
||||||
residents('FRE-HUB-RESIDENTS'),
|
residents('FRE-HUB-RESIDENTS'),
|
||||||
about('FRE-HUB-ABOUT-SYSTEM'),
|
about('FRE-HUB-ABOUT-SYSTEM'),
|
||||||
pets('FRE-HUB-PETS'),
|
pets('FRE-HUB-PETS'),
|
||||||
|
@ -63,7 +64,7 @@ class License {
|
||||||
static Future<String> _precessWpp() async {
|
static Future<String> _precessWpp() async {
|
||||||
final bool whatsapp = await StorageHelper()
|
final bool whatsapp = await StorageHelper()
|
||||||
.get(LocalsStorageKey.whatsapp.key)
|
.get(LocalsStorageKey.whatsapp.key)
|
||||||
.then((v) => v.toBoolean());
|
.then((v) => v.toBoolean);
|
||||||
if (whatsapp)
|
if (whatsapp)
|
||||||
return ModuleStatus.active.key;
|
return ModuleStatus.active.key;
|
||||||
else
|
else
|
||||||
|
@ -73,7 +74,7 @@ class License {
|
||||||
static Future<String> _processProvisional() async {
|
static Future<String> _processProvisional() async {
|
||||||
final bool provisional = await StorageHelper()
|
final bool provisional = await StorageHelper()
|
||||||
.get(LocalsStorageKey.provisional.key)
|
.get(LocalsStorageKey.provisional.key)
|
||||||
.then((v) => v.toBoolean());
|
.then((v) => v.toBoolean);
|
||||||
if (provisional)
|
if (provisional)
|
||||||
return ModuleStatus.active.key;
|
return ModuleStatus.active.key;
|
||||||
else
|
else
|
||||||
|
@ -83,7 +84,7 @@ class License {
|
||||||
static Future<String> _processPets() async {
|
static Future<String> _processPets() async {
|
||||||
final bool pets = await StorageHelper()
|
final bool pets = await StorageHelper()
|
||||||
.get(LocalsStorageKey.pets.key)
|
.get(LocalsStorageKey.pets.key)
|
||||||
.then((v) => v.toBoolean());
|
.then((v) => v.toBoolean);
|
||||||
if (pets)
|
if (pets)
|
||||||
return ModuleStatus.active.key;
|
return ModuleStatus.active.key;
|
||||||
else
|
else
|
||||||
|
@ -163,6 +164,13 @@ class License {
|
||||||
startDate: '',
|
startDate: '',
|
||||||
quantity: 0,
|
quantity: 0,
|
||||||
),
|
),
|
||||||
|
Module(
|
||||||
|
key: LicenseKeys.vehicles.value,
|
||||||
|
display: ModuleStatus.inactive.key,
|
||||||
|
expirationDate: '',
|
||||||
|
startDate: '',
|
||||||
|
quantity: 0,
|
||||||
|
),
|
||||||
Module(
|
Module(
|
||||||
key: LicenseKeys.residents.value,
|
key: LicenseKeys.residents.value,
|
||||||
display: isNewVersionWithModule
|
display: isNewVersionWithModule
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
|
||||||
import 'package:app_links/app_links.dart';
|
import 'package:app_links/app_links.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hub/features/storage/index.dart';
|
import 'package:hub/features/storage/index.dart';
|
||||||
|
|
|
@ -34,7 +34,9 @@ enum LocalsStorageKey implements DatabaseStorageKey {
|
||||||
panic('fre_panic'),
|
panic('fre_panic'),
|
||||||
person('fre_person'),
|
person('fre_person'),
|
||||||
requestOSNotification('fre_requestOSnotification'),
|
requestOSNotification('fre_requestOSnotification'),
|
||||||
isNewVersion('fre_isNewVersion');
|
isNewVersion('fre_isNewVersion'),
|
||||||
|
vehicleAutoApproval('fre_vehicleAutoApproval'),
|
||||||
|
vehicleAmountRegister('fre_vehicleAmountRegister');
|
||||||
|
|
||||||
final String key;
|
final String key;
|
||||||
|
|
||||||
|
|
|
@ -529,7 +529,7 @@ void setAppLanguage(BuildContext context, String language) =>
|
||||||
void setDarkModeSetting(BuildContext context, ThemeMode themeMode) =>
|
void setDarkModeSetting(BuildContext context, ThemeMode themeMode) =>
|
||||||
App.of(context).setThemeMode(themeMode);
|
App.of(context).setThemeMode(themeMode);
|
||||||
|
|
||||||
void showSnackbar(
|
void showSnackbarMessenger(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
String message,
|
String message,
|
||||||
bool error, {
|
bool error, {
|
||||||
|
@ -537,38 +537,47 @@ void showSnackbar(
|
||||||
int duration = 4,
|
int duration = 4,
|
||||||
}) {
|
}) {
|
||||||
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context)
|
||||||
SnackBar(
|
.showSnackBar(showSnackbar(context, message, error));
|
||||||
content: Row(
|
}
|
||||||
children: [
|
|
||||||
if (loading)
|
SnackBar showSnackbar(
|
||||||
Padding(
|
BuildContext context,
|
||||||
padding: const EdgeInsetsDirectional.only(end: 10.0),
|
String message,
|
||||||
child: SizedBox(
|
bool error, {
|
||||||
height: 20,
|
bool loading = false,
|
||||||
width: 20,
|
int duration = 4,
|
||||||
child: CircularProgressIndicator(
|
}) {
|
||||||
color: FlutterFlowTheme.of(context).info,
|
return SnackBar(
|
||||||
),
|
content: Row(
|
||||||
|
children: [
|
||||||
|
if (loading)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.only(end: 10.0),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 20,
|
||||||
|
width: 20,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: FlutterFlowTheme.of(context).info,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
|
||||||
message,
|
|
||||||
style: TextStyle(
|
|
||||||
color: FlutterFlowTheme.of(context).info,
|
|
||||||
fontSize: LimitedFontSizeUtil.getBodyFontSize(context),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
Text(
|
||||||
),
|
message,
|
||||||
duration: Duration(seconds: duration),
|
style: TextStyle(
|
||||||
backgroundColor: error
|
color: FlutterFlowTheme.of(context).info,
|
||||||
? FlutterFlowTheme.of(context).error
|
fontSize: LimitedFontSizeUtil.getBodyFontSize(context),
|
||||||
: FlutterFlowTheme.of(context).success,
|
),
|
||||||
behavior: SnackBarBehavior.floating,
|
),
|
||||||
shape: RoundedRectangleBorder(
|
],
|
||||||
borderRadius: BorderRadius.circular(30),
|
),
|
||||||
),
|
duration: Duration(seconds: duration),
|
||||||
|
backgroundColor: error
|
||||||
|
? FlutterFlowTheme.of(context).error
|
||||||
|
: FlutterFlowTheme.of(context).success,
|
||||||
|
behavior: SnackBarBehavior.floating,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(30),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -198,7 +198,7 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) {
|
||||||
FFRoute(
|
FFRoute(
|
||||||
name: 'vehiclesOnThePropertyPage',
|
name: 'vehiclesOnThePropertyPage',
|
||||||
path: '/vehiclesOnThePropertyPage',
|
path: '/vehiclesOnThePropertyPage',
|
||||||
builder: (context, params) => const VehicleOnTheProperty()),
|
builder: (context, params) => const VehiclePage()),
|
||||||
FFRoute(
|
FFRoute(
|
||||||
name: 'receptionPage',
|
name: 'receptionPage',
|
||||||
path: '/receptionPage',
|
path: '/receptionPage',
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:app_tracking_transparency/app_tracking_transparency.dart';
|
import 'package:app_tracking_transparency/app_tracking_transparency.dart';
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||||
|
@ -29,7 +27,8 @@ Future<void> initializeApp() async {
|
||||||
Future<void> _initializeTracking() async {
|
Future<void> _initializeTracking() async {
|
||||||
log('Requesting tracking authorization...');
|
log('Requesting tracking authorization...');
|
||||||
await AppTrackingTransparency.requestTrackingAuthorization();
|
await AppTrackingTransparency.requestTrackingAuthorization();
|
||||||
log('Tracking authorization requested');
|
|
||||||
|
print('Tracking authorization requested');
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _initializeFirebase() async {
|
Future<void> _initializeFirebase() async {
|
||||||
|
@ -51,12 +50,14 @@ void _initializeUrlStrategy() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _initializeSystemSettings() async {
|
Future<void> _initializeSystemSettings() async {
|
||||||
log('Initializing System Settings...');
|
print('Initializing System Settings...');
|
||||||
final crashlyticsInstance = FirebaseCrashlytics.instance;
|
|
||||||
|
|
||||||
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||||
|
final crashlyticsInstance = FirebaseCrashlytics.instance;
|
||||||
|
|
||||||
if (kDebugMode) {
|
if (kDebugMode) {
|
||||||
log('Debug mode');
|
print('Debug mode');
|
||||||
|
await crashlyticsInstance.setCrashlyticsCollectionEnabled(false);
|
||||||
} else {
|
} else {
|
||||||
log('Release mode');
|
log('Release mode');
|
||||||
|
|
||||||
|
@ -72,10 +73,22 @@ Future<void> _initializeSystemSettings() async {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
await crashlyticsInstance.setCrashlyticsCollectionEnabled(true);
|
await crashlyticsInstance.setCrashlyticsCollectionEnabled(true);
|
||||||
// if (crashlyticsInstance.isCrashlyticsCollectionEnabled) {
|
if (crashlyticsInstance.isCrashlyticsCollectionEnabled) {
|
||||||
FlutterError.onError = crashlyticsInstance.recordFlutterError;
|
// Configura o tratamento de erros não capturados
|
||||||
log('Crashlytics enabled');
|
FlutterError.onError = crashlyticsInstance.recordFlutterError;
|
||||||
// }
|
|
||||||
|
crashlyticsInstance.checkForUnsentReports().then((unsentReports) {
|
||||||
|
if (unsentReports) {
|
||||||
|
crashlyticsInstance.sendUnsentReports();
|
||||||
|
print('Existem relatórios de falhas não enviados.');
|
||||||
|
} else {
|
||||||
|
print('Todos os relatórios de falhas foram enviados.');
|
||||||
|
}
|
||||||
|
}).catchError((error) {
|
||||||
|
print('Erro ao verificar ou enviar relatórios não enviados: $error');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
print('Crashlytics enabled');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -299,7 +299,7 @@ class _LiberationHistoryWidgetState extends State<LiberationHistoryWidget> {
|
||||||
)
|
)
|
||||||
.then((message) {
|
.then((message) {
|
||||||
if (message != null || message != '') {
|
if (message != null || message != '') {
|
||||||
showSnackbar(
|
showSnackbarMessenger(
|
||||||
context,
|
context,
|
||||||
FFLocalizations.of(context).getVariableText(
|
FFLocalizations.of(context).getVariableText(
|
||||||
enText: 'Successfully resolved visit',
|
enText: 'Successfully resolved visit',
|
||||||
|
@ -308,7 +308,7 @@ class _LiberationHistoryWidgetState extends State<LiberationHistoryWidget> {
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
showSnackbar(context, message, true);
|
showSnackbarMessenger(context, message, true);
|
||||||
}
|
}
|
||||||
}).whenComplete(() {
|
}).whenComplete(() {
|
||||||
safeSetState(() {
|
safeSetState(() {
|
||||||
|
|
|
@ -478,7 +478,7 @@ class PetsPageModel extends FlutterFlowModel<PetsPageWidget> {
|
||||||
context.pop(value);
|
context.pop(value);
|
||||||
|
|
||||||
if (value == false) {
|
if (value == false) {
|
||||||
showSnackbar(
|
showSnackbarMessenger(
|
||||||
context,
|
context,
|
||||||
FFLocalizations.of(context).getVariableText(
|
FFLocalizations.of(context).getVariableText(
|
||||||
ptText: 'Erro ao excluir pet',
|
ptText: 'Erro ao excluir pet',
|
||||||
|
@ -487,7 +487,7 @@ class PetsPageModel extends FlutterFlowModel<PetsPageWidget> {
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
} else if (value == true) {
|
} else if (value == true) {
|
||||||
showSnackbar(
|
showSnackbarMessenger(
|
||||||
context,
|
context,
|
||||||
FFLocalizations.of(context).getVariableText(
|
FFLocalizations.of(context).getVariableText(
|
||||||
enText: 'Success deleting pet',
|
enText: 'Success deleting pet',
|
||||||
|
@ -498,7 +498,7 @@ class PetsPageModel extends FlutterFlowModel<PetsPageWidget> {
|
||||||
}
|
}
|
||||||
}).catchError((err, stack) {
|
}).catchError((err, stack) {
|
||||||
context.pop();
|
context.pop();
|
||||||
showSnackbar(
|
showSnackbarMessenger(
|
||||||
context,
|
context,
|
||||||
FFLocalizations.of(context).getVariableText(
|
FFLocalizations.of(context).getVariableText(
|
||||||
enText: 'Error deleting pet',
|
enText: 'Error deleting pet',
|
||||||
|
|
|
@ -86,9 +86,9 @@ class _PetsPageWidgetState extends State<PetsPageWidget>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onEditingChanged(bool value) {
|
void onEditingChanged([bool? value]) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_model.handleEditingChanged(value);
|
_model.handleEditingChanged(value!);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:awesome_notifications/awesome_notifications.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
|
||||||
|
|
|
@ -472,7 +472,7 @@ class ScheduleCompleteVisitPageModel
|
||||||
context.pop(value);
|
context.pop(value);
|
||||||
|
|
||||||
if (value == false) {
|
if (value == false) {
|
||||||
showSnackbar(
|
showSnackbarMessenger(
|
||||||
context,
|
context,
|
||||||
FFLocalizations.of(context).getVariableText(
|
FFLocalizations.of(context).getVariableText(
|
||||||
enText: 'Error blocking visit',
|
enText: 'Error blocking visit',
|
||||||
|
@ -481,7 +481,7 @@ class ScheduleCompleteVisitPageModel
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
} else if (value == true) {
|
} else if (value == true) {
|
||||||
showSnackbar(
|
showSnackbarMessenger(
|
||||||
context,
|
context,
|
||||||
FFLocalizations.of(context).getVariableText(
|
FFLocalizations.of(context).getVariableText(
|
||||||
enText: 'Success canceling visit',
|
enText: 'Success canceling visit',
|
||||||
|
@ -492,7 +492,7 @@ class ScheduleCompleteVisitPageModel
|
||||||
}
|
}
|
||||||
}).catchError((err, stack) {
|
}).catchError((err, stack) {
|
||||||
context.pop();
|
context.pop();
|
||||||
showSnackbar(
|
showSnackbarMessenger(
|
||||||
context,
|
context,
|
||||||
FFLocalizations.of(context).getVariableText(
|
FFLocalizations.of(context).getVariableText(
|
||||||
enText: 'Error blocking visit',
|
enText: 'Error blocking visit',
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
export 'vehicles_on_the_property.dart';
|
||||||
|
export 'vehicle_model.dart';
|
|
@ -0,0 +1,246 @@
|
||||||
|
part of 'vehicles_on_the_property.dart';
|
||||||
|
|
||||||
|
class VehicleHistoryScreen extends StatefulWidget {
|
||||||
|
final VehicleModel model;
|
||||||
|
const VehicleHistoryScreen(this.model, {super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<VehicleHistoryScreen> createState() => _VehicleHistoryScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _VehicleHistoryScreenState extends State<VehicleHistoryScreen>
|
||||||
|
with Pageable {
|
||||||
|
final apiCall = PhpGroup.getVehiclesByProperty;
|
||||||
|
int totalOwnerVehicles = 0;
|
||||||
|
final PagingController<int, dynamic> _pagingController =
|
||||||
|
PagingController<int, dynamic>(firstPageKey: 1);
|
||||||
|
bool _isSnackble = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_pagingController.addPageRequestListener(
|
||||||
|
(int pageKey) => fetchPage(
|
||||||
|
dataProvider: () async {
|
||||||
|
final newItems = await apiCall.call(pageKey.toString());
|
||||||
|
if (newItems.jsonBody == null) return (false, null);
|
||||||
|
final List<dynamic> vehicles =
|
||||||
|
(newItems.jsonBody['vehicles'] as List<dynamic>?) ?? [];
|
||||||
|
|
||||||
|
safeSetState(() {
|
||||||
|
totalOwnerVehicles = newItems.jsonBody['total_owner_vehicles'] ?? 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return (vehicles.isNotEmpty, vehicles);
|
||||||
|
},
|
||||||
|
onDataUnavailable: (vehicles) {
|
||||||
|
setState(() {});
|
||||||
|
final bool isFirst = pageKey == 2;
|
||||||
|
if (!isFirst && _isSnackble) showNoMoreDataSnackBar(context);
|
||||||
|
|
||||||
|
_pagingController.appendLastPage(vehicles);
|
||||||
|
},
|
||||||
|
onDataAvailable: (vehicles) {
|
||||||
|
setState(() {});
|
||||||
|
_pagingController.appendPage(vehicles, pageKey + 1);
|
||||||
|
},
|
||||||
|
onFetchError: (e, s) {
|
||||||
|
DialogUtil.errorDefault(context);
|
||||||
|
LogUtil.requestAPIFailed(
|
||||||
|
"proccessRequest.php", "", "Consulta de Veículo", e, s);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
_pagingController.addStatusListener(_showError);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_pagingController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _showError(PagingStatus status) async {
|
||||||
|
if (status == PagingStatus.subsequentPageError) {
|
||||||
|
final message = FFLocalizations.of(context).getVariableText(
|
||||||
|
enText: 'Something went wrong while fetching a new page.',
|
||||||
|
ptText: 'Algo deu errado ao buscar uma nova página.',
|
||||||
|
);
|
||||||
|
final retry = FFLocalizations.of(context).getVariableText(
|
||||||
|
enText: 'Retry',
|
||||||
|
ptText: 'Recarregar',
|
||||||
|
);
|
||||||
|
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(message),
|
||||||
|
action: SnackBarAction(
|
||||||
|
label: retry,
|
||||||
|
onPressed: () => _pagingController.retryLastFailedRequest(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
_buildHeader(context),
|
||||||
|
_buildBody(context),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildHeader(BuildContext context) {
|
||||||
|
final bodyFontSize = LimitedFontSizeUtil.getBodyFontSize(context);
|
||||||
|
final headerTitle = FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: "Meus Veículos: ",
|
||||||
|
enText: "My Vehicles: ",
|
||||||
|
);
|
||||||
|
final totalRegisteredVehicles = widget.model.amountRegister;
|
||||||
|
|
||||||
|
return SizedBox(
|
||||||
|
height: 30,
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
(widget.model.amountRegister == '0' ||
|
||||||
|
widget.model.amountRegister == null)
|
||||||
|
? ''
|
||||||
|
: "$headerTitle $totalOwnerVehicles/$totalRegisteredVehicles",
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
style: TextStyle(
|
||||||
|
fontFamily: 'Nunito',
|
||||||
|
fontSize: bodyFontSize,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Expanded _buildBody(BuildContext context) {
|
||||||
|
final noDataFound = FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: "Nenhum veículo encontrado!",
|
||||||
|
enText: "No vehicle found",
|
||||||
|
);
|
||||||
|
return buildPaginatedListView<int, dynamic>(
|
||||||
|
noDataFound,
|
||||||
|
_pagingController,
|
||||||
|
_generateItems,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _generateItems(
|
||||||
|
BuildContext context,
|
||||||
|
dynamic item,
|
||||||
|
int index,
|
||||||
|
) {
|
||||||
|
log('item: $item');
|
||||||
|
final bool? isOwner = item['isOwnerVehicle'];
|
||||||
|
final IconData iconData =
|
||||||
|
isOwner == true ? Symbols.garage : Symbols.directions_car;
|
||||||
|
|
||||||
|
final FreCardIcon? cardIcon = isOwner != null
|
||||||
|
? FreCardIcon(
|
||||||
|
height: 50,
|
||||||
|
width: 100,
|
||||||
|
icon: Icon(iconData, size: 80, opticalSize: 10),
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
final String? tag = item['tag'];
|
||||||
|
final bool containTag = tag.isNotNullAndEmpty;
|
||||||
|
final Map<String, String> labelsHashMap =
|
||||||
|
_generateLabelsHashMap(context, item, tag, containTag);
|
||||||
|
|
||||||
|
final List<Map<String, Color>> statusHashMapList =
|
||||||
|
_generateStatusHashMapList(item);
|
||||||
|
|
||||||
|
Future<void> onTapCardItemAction() async {
|
||||||
|
await _handleCardItemTap(context, cardIcon, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
final statusLinkedHashMap = statusHashMapList
|
||||||
|
.map((map) => LinkedHashMap<String, Color>.from(map))
|
||||||
|
.toList();
|
||||||
|
final length = statusLinkedHashMap.expand((e) => [e.length]);
|
||||||
|
final double itemWidthFactor = length == 1 ? 0.25 : 0.50;
|
||||||
|
|
||||||
|
return CardItemTemplateComponentWidget(
|
||||||
|
icon: cardIcon,
|
||||||
|
labelsHashMap: labelsHashMap,
|
||||||
|
statusHashMap: statusHashMapList,
|
||||||
|
onTapCardItemAction: onTapCardItemAction,
|
||||||
|
itemWidthFactor: itemWidthFactor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> _generateLabelsHashMap(
|
||||||
|
BuildContext context,
|
||||||
|
Map<String, dynamic> item,
|
||||||
|
String? tag,
|
||||||
|
bool containTag,
|
||||||
|
) {
|
||||||
|
final localization = FFLocalizations.of(context);
|
||||||
|
return {
|
||||||
|
'${localization.getVariableText(ptText: "Placa", enText: "License Plate")}:':
|
||||||
|
item['licensePlate'] ?? '',
|
||||||
|
'${localization.getVariableText(ptText: "Modelo", enText: "Model")}:':
|
||||||
|
item['model'] ?? '',
|
||||||
|
'${localization.getVariableText(ptText: "Proprietário", enText: "Owner")}:':
|
||||||
|
item['personName'] ?? '',
|
||||||
|
if (containTag)
|
||||||
|
'${localization.getVariableText(ptText: "Tag", enText: "Tag")}:':
|
||||||
|
tag ?? '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Map<String, Color>> _generateStatusHashMapList(
|
||||||
|
Map<String, dynamic> item) {
|
||||||
|
final statusHashMap = widget.model.generateStatusColorMap(item, false);
|
||||||
|
return statusHashMap != null ? [statusHashMap] : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _handleCardItemTap(
|
||||||
|
BuildContext context,
|
||||||
|
FreCardIcon? cardIcon,
|
||||||
|
Map<String, dynamic> item,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
final dialogContent = widget.model.buildVehicleDetails(
|
||||||
|
icon: cardIcon,
|
||||||
|
item: item,
|
||||||
|
context: context,
|
||||||
|
model: widget.model,
|
||||||
|
);
|
||||||
|
|
||||||
|
await showDialog<bool>(
|
||||||
|
useSafeArea: true,
|
||||||
|
context: context,
|
||||||
|
builder: (context) => Dialog(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: dialogContent,
|
||||||
|
),
|
||||||
|
) //
|
||||||
|
.then((response) async {
|
||||||
|
if (response == true) {
|
||||||
|
_isSnackble = false;
|
||||||
|
_pagingController.refresh();
|
||||||
|
} else {
|
||||||
|
_isSnackble = true;
|
||||||
|
}
|
||||||
|
}) //
|
||||||
|
.whenComplete(() {});
|
||||||
|
} catch (e, s) {
|
||||||
|
DialogUtil.errorDefault(context);
|
||||||
|
LogUtil.requestAPIFailed(
|
||||||
|
"proccessRequest.php", "", "Consulta de Veículos", e, s);
|
||||||
|
safeSetState(() {
|
||||||
|
// _hasData = false;
|
||||||
|
// _loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,54 +1,558 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'dart:developer';
|
||||||
import 'package:hub/components/templates_components/details_component/details_component_widget.dart';
|
|
||||||
import 'package:hub/flutter_flow/flutter_flow_model.dart';
|
|
||||||
import 'package:hub/flutter_flow/internationalization.dart';
|
|
||||||
import 'package:hub/pages/vehicles_on_the_property/vehicles_on_the_property.dart';
|
|
||||||
|
|
||||||
class VehicleModel extends FlutterFlowModel<VehicleOnTheProperty> {
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart';
|
||||||
|
import 'package:hub/components/templates_components/details_component/details_component_widget.dart';
|
||||||
|
import 'package:hub/features/backend/index.dart';
|
||||||
|
import 'package:hub/features/storage/index.dart';
|
||||||
|
import 'package:hub/flutter_flow/index.dart';
|
||||||
|
import 'package:hub/pages/vehicles_on_the_property/vehicles_on_the_property.dart';
|
||||||
|
import 'package:hub/shared/extensions/index.dart';
|
||||||
|
import 'package:hub/shared/utils/index.dart';
|
||||||
|
|
||||||
|
/// [VehicleModel] is a class that contains the business logic of the vehicle page.
|
||||||
|
class VehicleModel extends FlutterFlowModel<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();
|
static VehicleModel? _instance = VehicleModel._internal();
|
||||||
VehicleModel._internal();
|
VehicleModel._internal();
|
||||||
factory VehicleModel() => _instance ?? VehicleModel._internal();
|
factory VehicleModel() => _instance ?? VehicleModel._internal();
|
||||||
static void resetInstance() => _instance = null;
|
static void resetInstance() => _instance = null;
|
||||||
|
|
||||||
dynamic item;
|
final GlobalKey<FormState> registerFormKey = GlobalKey<FormState>();
|
||||||
|
final GlobalKey<FormState> updateFormKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState(BuildContext context) {
|
void initState(BuildContext context) {
|
||||||
|
log('VehicleModel -> initState');
|
||||||
resetInstance();
|
resetInstance();
|
||||||
|
|
||||||
initAsync();
|
initAsync();
|
||||||
|
initializeControllers(context);
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
amountRegister = '0';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void initializeControllers(BuildContext context) {
|
||||||
|
tabBarController = TabController(
|
||||||
|
vsync: Navigator.of(context),
|
||||||
|
length: 2,
|
||||||
|
);
|
||||||
|
|
||||||
|
textFieldFocusLicensePlate = FocusNode();
|
||||||
|
textFieldControllerLicensePlate = TextEditingController();
|
||||||
|
|
||||||
|
textFieldFocusColor = FocusNode();
|
||||||
|
textFieldControllerColor = TextEditingController();
|
||||||
|
|
||||||
|
textFieldFocusModel = FocusNode();
|
||||||
|
textFieldControllerModel = TextEditingController();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {}
|
void dispose() {
|
||||||
|
disposeControllers();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> initAsync() async {}
|
void disposeControllers() {
|
||||||
|
tabBarController.dispose();
|
||||||
|
textFieldFocusLicensePlate!.dispose();
|
||||||
|
textFieldControllerLicensePlate!.dispose();
|
||||||
|
textFieldFocusColor!.dispose();
|
||||||
|
textFieldControllerColor!.dispose();
|
||||||
|
textFieldFocusModel!.dispose();
|
||||||
|
textFieldControllerModel!.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
Widget buildVehicleDetails({
|
Future<void> initAsync() async {
|
||||||
required dynamic item,
|
amountRegister =
|
||||||
required BuildContext context,
|
await StorageHelper().get(LocalsStorageKey.vehicleAmountRegister.key);
|
||||||
required VehicleModel model,
|
autoApproval =
|
||||||
}) {
|
await StorageHelper().get(LocalsStorageKey.vehicleAutoApproval.key);
|
||||||
return DetailsComponentWidget(
|
}
|
||||||
buttons: [],
|
|
||||||
labelsHashMap: Map<String, String>.from({
|
bool isFormValid(BuildContext context, GlobalKey<FormState> formKey) {
|
||||||
if (item['model'] != null && item['model'] != '')
|
if (formKey.currentState == null) return false;
|
||||||
'${FFLocalizations.of(context).getVariableText(ptText: "Modelo", enText: "Model")}:':
|
return formKey.currentState!.validate();
|
||||||
item['model'].toString().toUpperCase(),
|
}
|
||||||
if (item['licensePlate'] != null && item['licensePlate'] != '')
|
}
|
||||||
'${FFLocalizations.of(context).getVariableText(ptText: "Placa", enText: "License Plate")}:':
|
|
||||||
item['licensePlate'].toString().toUpperCase(),
|
/// [_BaseVehiclePage] is a mixin that contains the base logic of the vehicle page.
|
||||||
if (item['color'] != null && item['color'] != '')
|
mixin class _BaseVehiclePage {
|
||||||
'${FFLocalizations.of(context).getVariableText(ptText: "Cor", enText: "Color")}:':
|
int count = 0;
|
||||||
item['color'].toString().toUpperCase(),
|
late final TabController tabBarController;
|
||||||
if (item['personName'] != null && item['personName'] != '')
|
// dynamic item;
|
||||||
'${FFLocalizations.of(context).getVariableText(ptText: "Proprietário", enText: "Owner")}:':
|
late int vehicleId;
|
||||||
item['personName'].toString().toUpperCase(),
|
BuildContext context = navigatorKey.currentContext!;
|
||||||
if (item['tag'] != null && item['tag'] != '')
|
bool isEditing = false;
|
||||||
'${FFLocalizations.of(context).getVariableText(ptText: "Tag", enText: "Tag")}:':
|
ApiCallResponse? vehicleResponse;
|
||||||
item['tag'].toString().toUpperCase(),
|
String? amountRegister = '0';
|
||||||
}),
|
late final String? autoApproval;
|
||||||
statusHashMap: [],
|
|
||||||
|
VoidCallback? onUpdateVehicle;
|
||||||
|
VoidCallback? onRegisterVehicle;
|
||||||
|
VoidCallback? safeSetState;
|
||||||
|
|
||||||
|
FocusNode? textFieldFocusLicensePlate;
|
||||||
|
TextEditingController? textFieldControllerLicensePlate;
|
||||||
|
String? textControllerLicensePlateValidator(
|
||||||
|
BuildContext context, String? value) {
|
||||||
|
final validationMessage = validateField(
|
||||||
|
context, value, 'Placa é obrigatória', 'License Plate is required');
|
||||||
|
if (validationMessage != null) return validationMessage;
|
||||||
|
|
||||||
|
final brazilianPlateRegex = RegExp(r'^[A-Z]{3}\d{4}$');
|
||||||
|
final mercosurPlateRegex = RegExp(r'^[A-Z]{3}\d[A-Z0-9]\d{2}$');
|
||||||
|
|
||||||
|
if (!brazilianPlateRegex.hasMatch(value!) &&
|
||||||
|
!mercosurPlateRegex.hasMatch(value)) {
|
||||||
|
return FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Placa inválida',
|
||||||
|
enText: 'Invalid license plate',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
FocusNode? textFieldFocusColor;
|
||||||
|
TextEditingController? textFieldControllerColor;
|
||||||
|
String? textControllerColorValidator(BuildContext contexnt, String? value) {
|
||||||
|
return validateField(
|
||||||
|
context, value, 'Cor é obrigatória', 'Color is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
FocusNode? textFieldFocusModel;
|
||||||
|
TextEditingController? textFieldControllerModel;
|
||||||
|
String? textControllerModelValidator(BuildContext contexnt, String? value) {
|
||||||
|
return validateField(
|
||||||
|
context, value, 'Modelo é obrigatório', 'Model is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
String? validateField(
|
||||||
|
BuildContext context, String? value, String ptText, String enText) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: ptText,
|
||||||
|
enText: enText,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> switchTab(int index) async {
|
||||||
|
tabBarController.animateTo(index);
|
||||||
|
if (index == 0) await handleEditingChanged(false);
|
||||||
|
safeSetState?.call();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> clearFields() async {
|
||||||
|
textFieldControllerLicensePlate = TextEditingController(text: '');
|
||||||
|
textFieldControllerColor = TextEditingController(text: '');
|
||||||
|
textFieldControllerModel = TextEditingController(text: '');
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> handleEditingChanged(bool editing) async {
|
||||||
|
isEditing = editing;
|
||||||
|
await clearFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setEditForm(dynamic item) async {
|
||||||
|
if (item != null) {
|
||||||
|
log('vehicleId: ${item['vehicleId']}');
|
||||||
|
vehicleId = item['vehicleId'];
|
||||||
|
|
||||||
|
textFieldControllerLicensePlate = TextEditingController(
|
||||||
|
text: item != null ? item['licensePlate'] ?? '' : '');
|
||||||
|
textFieldControllerColor =
|
||||||
|
TextEditingController(text: item != null ? item['color'] ?? '' : '');
|
||||||
|
textFieldControllerModel =
|
||||||
|
TextEditingController(text: item != null ? item['model'] ?? '' : '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> handleVehicleResponse(ApiCallResponse response,
|
||||||
|
String successMessage, String errorMessage) async {
|
||||||
|
if (response.jsonBody['error'] == false) {
|
||||||
|
await DialogUtil.success(context, successMessage).then((_) async {
|
||||||
|
await switchTab(0);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
String errorMsg;
|
||||||
|
try {
|
||||||
|
errorMsg = response.jsonBody['error_msg'];
|
||||||
|
} catch (e) {
|
||||||
|
errorMsg = errorMessage;
|
||||||
|
}
|
||||||
|
await DialogUtil.error(context, errorMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [_VehicleRegisterScreenModel] is a mixin that contains the business logic of the vehicle register page.
|
||||||
|
mixin _VehicleRegisterScreenModel on _BaseVehiclePage {
|
||||||
|
Future<void> registerVehicle() async {
|
||||||
|
final response = await PhpGroup.registerVehicle.call(
|
||||||
|
licensePlate: textFieldControllerLicensePlate!.text,
|
||||||
|
color: textFieldControllerColor!.text,
|
||||||
|
model: textFieldControllerModel!.text,
|
||||||
|
);
|
||||||
|
await handleVehicleResponse(
|
||||||
|
response,
|
||||||
|
FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Veículo cadastrado com sucesso',
|
||||||
|
enText: 'Vehicle registered successfully',
|
||||||
|
),
|
||||||
|
FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Erro ao cadastrar veículo',
|
||||||
|
enText: 'Error registering vehicle',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [_VehicleUpdateScreenModel] is a mixin that contains the business logic of the vehicle update page.
|
||||||
|
mixin _VehicleUpdateScreenModel on _BaseVehiclePage {
|
||||||
|
Future<void> updateVehicle() async {
|
||||||
|
final response = await PhpGroup.updateVehicle.call(
|
||||||
|
licensePlate: textFieldControllerLicensePlate!.text,
|
||||||
|
color: textFieldControllerColor!.text,
|
||||||
|
model: textFieldControllerModel!.text,
|
||||||
|
vehicleId: vehicleId,
|
||||||
|
);
|
||||||
|
await handleVehicleResponse(
|
||||||
|
response,
|
||||||
|
FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Veículo atualizado com sucesso',
|
||||||
|
enText: 'Vehicle updated successfully',
|
||||||
|
),
|
||||||
|
FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Erro ao atualizar veículo',
|
||||||
|
enText: 'Error updating vehicle',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [_VehicleHistoryScreenModel] is a mixin that contains the business logic of the vehicle history page.
|
||||||
|
mixin _VehicleHistoryScreenModel on _BaseVehiclePage {
|
||||||
|
Map<String, Color>? generateStatusColorMap(dynamic uItem, bool isDetail) {
|
||||||
|
if (autoApproval.toBoolean == true) return null;
|
||||||
|
final theme = FlutterFlowTheme.of(context);
|
||||||
|
final localization = FFLocalizations.of(context);
|
||||||
|
|
||||||
|
final status = uItem['status'];
|
||||||
|
final isOwner = uItem['isOwnerVehicle'];
|
||||||
|
|
||||||
|
if (isOwner == null && status == null) return null;
|
||||||
|
|
||||||
|
String byLanguage(String en, String pt) =>
|
||||||
|
localization.getVariableText(enText: en, ptText: pt);
|
||||||
|
|
||||||
|
final vehicleStatusMap = {
|
||||||
|
"ATI": {
|
||||||
|
"text": byLanguage('Active', 'Ativo'),
|
||||||
|
"color": theme.success,
|
||||||
|
},
|
||||||
|
"INA": {
|
||||||
|
"text": byLanguage('Inactive', 'Inativo'),
|
||||||
|
"color": theme.accent2,
|
||||||
|
},
|
||||||
|
"APR_CREATE": {
|
||||||
|
"text": byLanguage('Awaiting Creation', 'Aguardando Criação'),
|
||||||
|
"color": theme.success,
|
||||||
|
},
|
||||||
|
"APR_DELETE": {
|
||||||
|
"text": byLanguage('Awaiting Deletion', 'Aguardando Exclusão'),
|
||||||
|
"color": theme.error,
|
||||||
|
},
|
||||||
|
"APR_UPDATE": {
|
||||||
|
"text": byLanguage('Awaiting Update', 'Aguardando Atualização'),
|
||||||
|
"color": theme.accent2,
|
||||||
|
},
|
||||||
|
"AGU_CHANGE": {
|
||||||
|
"text": byLanguage('Awaiting Change', 'Aguardando Alteração'),
|
||||||
|
"color": theme.warning,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (vehicleStatusMap.containsKey(status)) {
|
||||||
|
final statusMap = {
|
||||||
|
vehicleStatusMap[status]!['text'] as String:
|
||||||
|
vehicleStatusMap[status]!['color'] as Color,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isDetail && isOwner && (status != 'ATI' && status != 'INA')) {
|
||||||
|
statusMap[byLanguage('My Vehicle', 'Meu Veículo')] = theme.accent4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return statusMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
List<FFButtonWidget> generateActionButtons(dynamic item) {
|
||||||
|
final Color iconButtonColor = FlutterFlowTheme.of(context).primaryText;
|
||||||
|
final FFButtonOptions buttonOptions = FFButtonOptions(
|
||||||
|
height: 40,
|
||||||
|
color: FlutterFlowTheme.of(context).primaryBackground,
|
||||||
|
elevation: 0,
|
||||||
|
textStyle: TextStyle(
|
||||||
|
color: FlutterFlowTheme.of(context).primaryText,
|
||||||
|
fontSize: LimitedFontSizeUtil.getNoResizeFont(context, 15),
|
||||||
|
),
|
||||||
|
splashColor: FlutterFlowTheme.of(context).success,
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: FlutterFlowTheme.of(context).primaryBackground,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (item['status'] == null) return [];
|
||||||
|
|
||||||
|
final updateText = FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Editar', enText: 'Edit');
|
||||||
|
final updateIcon = Icon(Icons.edit, color: iconButtonColor);
|
||||||
|
Future updateOnPressed() async {
|
||||||
|
context.pop();
|
||||||
|
isEditing = true;
|
||||||
|
|
||||||
|
await switchTab(1);
|
||||||
|
await setEditForm(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
final cancelText = FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Cancelar', enText: 'Cancel');
|
||||||
|
final cancelIcon = Icon(Icons.close, color: iconButtonColor);
|
||||||
|
Future cancelOnPressed() async {
|
||||||
|
showAlertDialog(
|
||||||
|
context,
|
||||||
|
FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Cancelar Solicitação',
|
||||||
|
enText: 'Cancel Request',
|
||||||
|
),
|
||||||
|
FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Você tem certeza que deseja cancelar essa solicitação?',
|
||||||
|
enText: 'Are you sure you want to delete this request?',
|
||||||
|
),
|
||||||
|
() async => await processCancelRequest(item['status'], item));
|
||||||
|
}
|
||||||
|
|
||||||
|
final deleteText = FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Excluir', enText: 'Delete');
|
||||||
|
final deleteIcon = Icon(Icons.delete, color: iconButtonColor);
|
||||||
|
Future deleteOnPressed() async {
|
||||||
|
showAlertDialog(
|
||||||
|
context,
|
||||||
|
FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Excluir Veículo',
|
||||||
|
enText: 'Delete Vehicle',
|
||||||
|
),
|
||||||
|
FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Você tem certeza que deseja excluir esse veículo?',
|
||||||
|
enText: 'Are you sure you want to delete this vehicle?',
|
||||||
|
),
|
||||||
|
() async => processDeleteRequest(item),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final bool containStatus = item['status'] == null;
|
||||||
|
final bool isOwnerVehicle = item['isOwnerVehicle'];
|
||||||
|
final bool isAGU = item['status'].contains('AGU');
|
||||||
|
final bool isAPR = item['status'].contains('APR');
|
||||||
|
final bool isATI = item['status'].contains('ATI');
|
||||||
|
|
||||||
|
if (containStatus)
|
||||||
|
return [
|
||||||
|
FFButtonWidget(
|
||||||
|
text: deleteText,
|
||||||
|
icon: deleteIcon,
|
||||||
|
onPressed: deleteOnPressed,
|
||||||
|
options: buttonOptions,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
return [
|
||||||
|
if (isAGU && isOwnerVehicle)
|
||||||
|
FFButtonWidget(
|
||||||
|
text: updateText,
|
||||||
|
icon: updateIcon,
|
||||||
|
onPressed: updateOnPressed,
|
||||||
|
options: buttonOptions,
|
||||||
|
),
|
||||||
|
if ((isAPR || isAGU) && (isOwnerVehicle))
|
||||||
|
FFButtonWidget(
|
||||||
|
text: cancelText,
|
||||||
|
icon: cancelIcon,
|
||||||
|
onPressed: cancelOnPressed,
|
||||||
|
options: buttonOptions,
|
||||||
|
),
|
||||||
|
if (isATI && isOwnerVehicle)
|
||||||
|
FFButtonWidget(
|
||||||
|
text: deleteText,
|
||||||
|
icon: deleteIcon,
|
||||||
|
onPressed: deleteOnPressed,
|
||||||
|
options: buttonOptions,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> processDeleteRequest(dynamic item) async {
|
||||||
|
log('processDeleteRequest -> item[$item]');
|
||||||
|
bool result = await PhpGroup.deleteVehicle
|
||||||
|
.call(
|
||||||
|
vehicleId: item['vehicleId'],
|
||||||
|
licensePlate: item['licensePlate'],
|
||||||
|
model: item['model'],
|
||||||
|
color: item['color'],
|
||||||
|
)
|
||||||
|
.then((value) {
|
||||||
|
// ignore: unrelated_type_equality_checks
|
||||||
|
if (value.jsonBody['error'] == true) {
|
||||||
|
final String errorMsg = value.jsonBody['error_msg'];
|
||||||
|
showSnackbarMessenger(
|
||||||
|
context,
|
||||||
|
FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: errorMsg,
|
||||||
|
enText: 'Error deleting vehicle',
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
// ignore: unrelated_type_equality_checks
|
||||||
|
}
|
||||||
|
showSnackbarMessenger(
|
||||||
|
context,
|
||||||
|
FFLocalizations.of(context).getVariableText(
|
||||||
|
enText: 'Success deleting vehicle',
|
||||||
|
ptText: 'Succeso ao excluir veículo',
|
||||||
|
),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}) //
|
||||||
|
.catchError((err, stack) {
|
||||||
|
showSnackbarMessenger(
|
||||||
|
context,
|
||||||
|
FFLocalizations.of(context).getVariableText(
|
||||||
|
enText: 'Error deleting vehicle',
|
||||||
|
ptText: 'Erro ao excluir veículo',
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
context.pop(result);
|
||||||
|
context.pop(result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> processCancelRequest(String status, dynamic item) async {
|
||||||
|
try {
|
||||||
|
final ApiCallResponse value;
|
||||||
|
switch (status) {
|
||||||
|
case 'APR_CREATE':
|
||||||
|
value = await processCancelDeleteRequest(item);
|
||||||
|
break;
|
||||||
|
case 'AGU_CHANGE':
|
||||||
|
value = await processCancelUpdateRequest(item);
|
||||||
|
break;
|
||||||
|
case 'APR_DELETE':
|
||||||
|
value = await processCancelCreateRequest(item);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw ArgumentError('Status inválido: $status');
|
||||||
|
}
|
||||||
|
|
||||||
|
final bool isError = value.jsonBody['error'] == true;
|
||||||
|
final String message = FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: value.jsonBody['error_msg'] ?? 'Erro ao cancelar solicitação',
|
||||||
|
enText:
|
||||||
|
isError ? 'Error canceling request' : 'Success canceling request',
|
||||||
|
);
|
||||||
|
showSnackbarMessenger(context, message, isError);
|
||||||
|
context.pop(!isError);
|
||||||
|
context.pop(!isError);
|
||||||
|
return !isError;
|
||||||
|
} catch (err) {
|
||||||
|
final String errorMessage = FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Erro ao cancelar solicitação',
|
||||||
|
enText: 'Error canceling request',
|
||||||
|
);
|
||||||
|
showSnackbarMessenger(context, errorMessage, true);
|
||||||
|
context.pop(false);
|
||||||
|
context.pop(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<ApiCallResponse> processCancelDeleteRequest(dynamic item) async {
|
||||||
|
return await PhpGroup.deleteVehicle.call(
|
||||||
|
vehicleId: item['vehicleId'],
|
||||||
|
licensePlate: item['licensePlate'],
|
||||||
|
model: item['model'],
|
||||||
|
color: item['color'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<ApiCallResponse> processCancelUpdateRequest(dynamic item) async {
|
||||||
|
return await PhpGroup.deleteVehicle.call(
|
||||||
|
vehicleId: item['vehicleId'],
|
||||||
|
licensePlate: item['licensePlate'],
|
||||||
|
model: item['model'],
|
||||||
|
color: item['color'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<ApiCallResponse> processCancelCreateRequest(dynamic item) async {
|
||||||
|
return await PhpGroup.cancelDelete.call(
|
||||||
|
vehicleId: item['vehicleId'],
|
||||||
|
licensePlate: item['licensePlate'],
|
||||||
|
model: item['model'],
|
||||||
|
color: item['color'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> generateLabelsHashMap(dynamic item) {
|
||||||
|
return {
|
||||||
|
if (item['model'] != null && item['model'] != '')
|
||||||
|
'${FFLocalizations.of(context).getVariableText(ptText: "Modelo", enText: "Model")}:':
|
||||||
|
item['model'].toString().toUpperCase(),
|
||||||
|
if (item['licensePlate'] != null && item['licensePlate'] != '')
|
||||||
|
'${FFLocalizations.of(context).getVariableText(ptText: "Placa", enText: "License Plate")}:':
|
||||||
|
item['licensePlate'].toString().toUpperCase(),
|
||||||
|
if (item['color'] != null && item['color'] != '')
|
||||||
|
'${FFLocalizations.of(context).getVariableText(ptText: "Cor", enText: "Color")}:':
|
||||||
|
item['color'].toString().toUpperCase(),
|
||||||
|
if (item['personName'] != null && item['personName'] != '')
|
||||||
|
'${FFLocalizations.of(context).getVariableText(ptText: "Proprietário", enText: "Owner")}:':
|
||||||
|
item['personName'].toString().toUpperCase(),
|
||||||
|
if (item['tag'] != null && item['tag'] != '')
|
||||||
|
'${FFLocalizations.of(context).getVariableText(ptText: "Tag", enText: "Tag")}:':
|
||||||
|
item['tag'].toString().toUpperCase(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
DetailsComponentWidget buildVehicleDetails({
|
||||||
|
required dynamic item,
|
||||||
|
required BuildContext context,
|
||||||
|
required VehicleModel model,
|
||||||
|
required FreCardIcon? icon,
|
||||||
|
}) {
|
||||||
|
final status = generateStatusColorMap(item, true);
|
||||||
|
final buttons = generateActionButtons(item);
|
||||||
|
final labels = generateLabelsHashMap(item);
|
||||||
|
return DetailsComponentWidget(
|
||||||
|
icon: icon,
|
||||||
|
buttons: buttons,
|
||||||
|
labelsHashMap: labels,
|
||||||
|
statusHashMap: [status],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
part of 'vehicles_on_the_property.dart';
|
||||||
|
|
||||||
|
/// [VehicleRegisterScreen] is a StatefulWidget that displays a form to register a vehicle.
|
||||||
|
|
||||||
|
// ignore: must_be_immutable
|
||||||
|
class VehicleRegisterScreen extends StatefulWidget {
|
||||||
|
VehicleRegisterScreen(this.model, {super.key});
|
||||||
|
late VehicleModel model;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<VehicleRegisterScreen> createState() => _VehicleRegisterScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _VehicleRegisterScreenState extends State<VehicleRegisterScreen> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
_buildHeader(context),
|
||||||
|
_buildBody(context),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Form _buildBody(BuildContext context) {
|
||||||
|
return Form(
|
||||||
|
key: widget.model.registerFormKey,
|
||||||
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
_buildCustomInput(
|
||||||
|
context: context,
|
||||||
|
controller: widget.model.textFieldControllerLicensePlate!,
|
||||||
|
validator: widget.model.textControllerLicensePlateValidator,
|
||||||
|
focusNode: widget.model.textFieldFocusLicensePlate!,
|
||||||
|
labelText: FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Placa', enText: 'License Plate'),
|
||||||
|
hintText: FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Placa', enText: 'License Plate'),
|
||||||
|
suffixIcon: Symbols.format_color_text,
|
||||||
|
inputFormatters: [UpperCaseTextFormatter()],
|
||||||
|
maxLength: 7,
|
||||||
|
),
|
||||||
|
_buildCustomInput(
|
||||||
|
context: context,
|
||||||
|
controller: widget.model.textFieldControllerModel!,
|
||||||
|
validator: widget.model.textControllerModelValidator,
|
||||||
|
focusNode: widget.model.textFieldFocusModel!,
|
||||||
|
labelText: FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Modelo', enText: 'Model'),
|
||||||
|
hintText: FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Ex: Voyage, Ford', enText: 'e.g. Voyage, Ford'),
|
||||||
|
suffixIcon: Symbols.car_repair,
|
||||||
|
inputFormatters: [],
|
||||||
|
),
|
||||||
|
_buildCustomInput(
|
||||||
|
context: context,
|
||||||
|
controller: widget.model.textFieldControllerColor!,
|
||||||
|
validator: widget.model.textControllerColorValidator,
|
||||||
|
focusNode: widget.model.textFieldFocusColor!,
|
||||||
|
labelText: FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Cor', enText: 'Color'),
|
||||||
|
hintText: FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Ex: Preto, Amarelo, Branco',
|
||||||
|
enText: 'e.g. Black, Yellow, White'),
|
||||||
|
suffixIcon: Symbols.palette,
|
||||||
|
inputFormatters: [],
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(70, 20, 70, 30),
|
||||||
|
child: SubmitButtonUtil(
|
||||||
|
labelText: FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Cadastrar', enText: 'Register'),
|
||||||
|
onPressed: widget.model
|
||||||
|
.isFormValid(context, widget.model.registerFormKey)
|
||||||
|
? widget.model.registerVehicle
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildCustomInput({
|
||||||
|
required BuildContext context,
|
||||||
|
required TextEditingController controller,
|
||||||
|
required String? Function(BuildContext, String?) validator,
|
||||||
|
required FocusNode focusNode,
|
||||||
|
required String labelText,
|
||||||
|
required String hintText,
|
||||||
|
required IconData suffixIcon,
|
||||||
|
required final List<TextInputFormatter>? inputFormatters,
|
||||||
|
int maxLength = 80,
|
||||||
|
}) {
|
||||||
|
return CustomInputUtil(
|
||||||
|
controller: controller,
|
||||||
|
validator: (value) => validator(context, value),
|
||||||
|
focusNode: focusNode,
|
||||||
|
labelText: labelText,
|
||||||
|
hintText: hintText,
|
||||||
|
suffixIcon: suffixIcon,
|
||||||
|
haveMaxLength: true,
|
||||||
|
onChanged: (value) => setState(() {}),
|
||||||
|
inputFormatters: inputFormatters,
|
||||||
|
maxLength: maxLength,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Align _buildHeader(BuildContext context) {
|
||||||
|
double limitedHeaderFontSize =
|
||||||
|
LimitedFontSizeUtil.getHeaderFontSize(context);
|
||||||
|
|
||||||
|
return Align(
|
||||||
|
alignment: const AlignmentDirectional(-1.0, 0.0),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.fromSTEB(24.0, 20, 0.0, 15),
|
||||||
|
child: Text(
|
||||||
|
FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText:
|
||||||
|
'Preencha o formulário de cadastro com os dados do seu veículo',
|
||||||
|
enText: 'Fill out the registration form with your vehicle data',
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
style: FlutterFlowTheme.of(context).bodyMedium.override(
|
||||||
|
fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily,
|
||||||
|
letterSpacing: 0.0,
|
||||||
|
useGoogleFonts: GoogleFonts.asMap()
|
||||||
|
.containsKey(FlutterFlowTheme.of(context).bodyMediumFamily),
|
||||||
|
fontSize: limitedHeaderFontSize,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
part of 'vehicles_on_the_property.dart';
|
||||||
|
|
||||||
|
/// [VehicleUpdateScreen] is a StatefulWidget that displays a form to update a vehicle.
|
||||||
|
|
||||||
|
// ignore: must_be_immutable
|
||||||
|
class VehicleUpdateScreen extends StatefulWidget {
|
||||||
|
VehicleUpdateScreen(this.model, {super.key});
|
||||||
|
late VehicleModel model;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<VehicleUpdateScreen> createState() => _VehicleUpdateScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _VehicleUpdateScreenState extends State<VehicleUpdateScreen> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
_buildHeader(context),
|
||||||
|
buildBody(context),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Form buildBody(BuildContext context) {
|
||||||
|
return Form(
|
||||||
|
key: widget.model.updateFormKey,
|
||||||
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
_buildCustomInput(
|
||||||
|
context: context,
|
||||||
|
controller: widget.model.textFieldControllerLicensePlate!,
|
||||||
|
validator: widget.model.textControllerLicensePlateValidator,
|
||||||
|
focusNode: widget.model.textFieldFocusLicensePlate!,
|
||||||
|
labelText: FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Placa', enText: 'License Plate'),
|
||||||
|
hintText: FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Placa', enText: 'License Plate'),
|
||||||
|
suffixIcon: Symbols.format_color_text,
|
||||||
|
inputFormatters: [UpperCaseTextFormatter()],
|
||||||
|
maxLength: 7,
|
||||||
|
),
|
||||||
|
_buildCustomInput(
|
||||||
|
context: context,
|
||||||
|
controller: widget.model.textFieldControllerModel!,
|
||||||
|
validator: widget.model.textControllerModelValidator,
|
||||||
|
focusNode: widget.model.textFieldFocusModel!,
|
||||||
|
labelText: FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Modelo', enText: 'Model'),
|
||||||
|
hintText: FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Ex: Voyage, Ford', enText: 'e.g. Voyage, Ford'),
|
||||||
|
suffixIcon: Symbols.car_repair,
|
||||||
|
inputFormatters: [],
|
||||||
|
),
|
||||||
|
_buildCustomInput(
|
||||||
|
context: context,
|
||||||
|
controller: widget.model.textFieldControllerColor!,
|
||||||
|
validator: widget.model.textControllerColorValidator,
|
||||||
|
focusNode: widget.model.textFieldFocusColor!,
|
||||||
|
labelText: FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Cor', enText: 'Color'),
|
||||||
|
hintText: FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: 'Ex: Preto, Amarelo, Branco',
|
||||||
|
enText: 'e.g. Black, Yellow, White'),
|
||||||
|
suffixIcon: Symbols.palette,
|
||||||
|
inputFormatters: [],
|
||||||
|
),
|
||||||
|
_buildSubmitButton(context),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildHeader(BuildContext context) {
|
||||||
|
return Align(
|
||||||
|
alignment: const AlignmentDirectional(-1.0, 0.0),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.fromSTEB(24.0, 20, 0.0, 15),
|
||||||
|
child: Text(
|
||||||
|
FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText:
|
||||||
|
'Preencha o formulário de alteração com os dados do seu veículo',
|
||||||
|
enText: 'Fill out the update form with your vehicle data',
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
style: FlutterFlowTheme.of(context).bodyMedium.override(
|
||||||
|
fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily,
|
||||||
|
letterSpacing: 0.0,
|
||||||
|
useGoogleFonts: GoogleFonts.asMap()
|
||||||
|
.containsKey(FlutterFlowTheme.of(context).bodyMediumFamily),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildCustomInput({
|
||||||
|
required BuildContext context,
|
||||||
|
required TextEditingController controller,
|
||||||
|
required String? Function(BuildContext, String?) validator,
|
||||||
|
required FocusNode focusNode,
|
||||||
|
required String labelText,
|
||||||
|
required String hintText,
|
||||||
|
required IconData suffixIcon,
|
||||||
|
required List<TextInputFormatter>? inputFormatters,
|
||||||
|
int maxLength = 80,
|
||||||
|
}) {
|
||||||
|
return CustomInputUtil(
|
||||||
|
controller: controller,
|
||||||
|
validator: (value) => validator(context, value),
|
||||||
|
focusNode: focusNode,
|
||||||
|
labelText: labelText,
|
||||||
|
hintText: hintText,
|
||||||
|
suffixIcon: suffixIcon,
|
||||||
|
haveMaxLength: true,
|
||||||
|
onChanged: (value) => setState(() {}),
|
||||||
|
inputFormatters: inputFormatters,
|
||||||
|
maxLength: maxLength,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSubmitButton(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(70, 20, 70, 30),
|
||||||
|
child: SubmitButtonUtil(
|
||||||
|
labelText: FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Salvar', enText: 'Save'),
|
||||||
|
onPressed: widget.model.isFormValid(context, widget.model.updateFormKey)
|
||||||
|
? () => widget.model.updateVehicle()
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,149 +1,184 @@
|
||||||
|
import 'dart:collection';
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:auto_size_text/auto_size_text.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
import 'package:hub/components/atomic_components/shared_components_atoms/custom_input.dart';
|
||||||
|
import 'package:hub/components/atomic_components/shared_components_atoms/submit_button.dart';
|
||||||
|
import 'package:hub/components/atomic_components/shared_components_atoms/tabview.dart';
|
||||||
import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart';
|
import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart';
|
||||||
import 'package:hub/features/backend/index.dart';
|
import 'package:hub/features/backend/index.dart';
|
||||||
import 'package:hub/flutter_flow/flutter_flow_icon_button.dart';
|
import 'package:hub/features/module/index.dart';
|
||||||
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
import 'package:hub/flutter_flow/index.dart';
|
||||||
import 'package:hub/flutter_flow/flutter_flow_util.dart';
|
|
||||||
import 'package:hub/pages/vehicles_on_the_property/vehicle_model.dart';
|
import 'package:hub/pages/vehicles_on_the_property/vehicle_model.dart';
|
||||||
|
import 'package:hub/shared/extensions/index.dart';
|
||||||
|
import 'package:hub/shared/mixins/pageable_mixin.dart';
|
||||||
import 'package:hub/shared/utils/dialog_util.dart';
|
import 'package:hub/shared/utils/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/limited_text_size.dart';
|
||||||
import 'package:hub/shared/utils/log_util.dart';
|
import 'package:hub/shared/utils/log_util.dart';
|
||||||
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
|
||||||
class VehicleOnTheProperty extends StatefulWidget {
|
part 'vehicle_history_screen.dart';
|
||||||
const VehicleOnTheProperty({super.key});
|
part 'vehicle_register_screen.dart';
|
||||||
|
part 'vehicle_update_screen.dart';
|
||||||
|
|
||||||
|
/// [VehiclePage] is a StatefulWidget that displays the vehicle screens.
|
||||||
|
class VehiclePage extends StatefulWidget {
|
||||||
|
const VehiclePage({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_VehicleOnThePropertyState createState() => _VehicleOnThePropertyState();
|
// ignore: library_private_types_in_public_api
|
||||||
|
_VehiclePageState createState() => _VehiclePageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _VehicleOnThePropertyState extends State<VehicleOnTheProperty>
|
class _VehiclePageState extends State<VehiclePage>
|
||||||
with TickerProviderStateMixin {
|
with TickerProviderStateMixin {
|
||||||
late ScrollController _scrollController;
|
|
||||||
|
|
||||||
int _pageNumber = 1;
|
|
||||||
bool _hasData = false;
|
|
||||||
bool _loading = false;
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
late final VehicleModel model;
|
late final VehicleModel _model;
|
||||||
|
|
||||||
late Future<void> _future;
|
|
||||||
List<dynamic> _wrap = [];
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
model = createModel(context, () => VehicleModel());
|
_model = createModel(context, () => VehicleModel());
|
||||||
_future = _fetchVisits();
|
|
||||||
_scrollController = ScrollController()
|
_model.updateOnChange = true;
|
||||||
..addListener(() {
|
_model.onUpdateVehicle = () {
|
||||||
if (_scrollController.position.atEdge &&
|
safeSetState(() {
|
||||||
_scrollController.position.pixels != 0) {
|
_model.clearFields();
|
||||||
_loadMore();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
_model.onRegisterVehicle = () {
|
||||||
|
safeSetState(() {
|
||||||
|
_model.clearFields();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
_model.safeSetState = () {
|
||||||
|
safeSetState(() {});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
// @override
|
||||||
void dispose() {
|
// void dispose() {
|
||||||
_scrollController.dispose();
|
// super.dispose();
|
||||||
super.dispose();
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
late final limitedHeaderTextSize =
|
final backgroundColor = FlutterFlowTheme.of(context).primaryBackground;
|
||||||
LimitedFontSizeUtil.getHeaderFontSize(context);
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: FlutterFlowTheme.of(context).primaryBackground,
|
backgroundColor: backgroundColor,
|
||||||
appBar: _appBar(context),
|
appBar: _buildHeader(context),
|
||||||
body: Column(
|
body: _buildBody(context),
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
if (_hasData == false && _pageNumber <= 1 && _loading == false)
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
children: [
|
|
||||||
Center(
|
|
||||||
child: Text(
|
|
||||||
FFLocalizations.of(context).getVariableText(
|
|
||||||
ptText: "Nenhum veículo encontrado!",
|
|
||||||
enText: "No vehicle found",
|
|
||||||
),
|
|
||||||
style: TextStyle(
|
|
||||||
fontFamily: 'Nunito',
|
|
||||||
fontSize: limitedHeaderTextSize,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
else if (_hasData == true || _pageNumber >= 1)
|
|
||||||
Expanded(
|
|
||||||
child: FutureBuilder<void>(
|
|
||||||
future: _future,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
return ListView.builder(
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: const BouncingScrollPhysics(),
|
|
||||||
controller: _scrollController,
|
|
||||||
itemCount: _wrap.length + 1,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
if (index == 0) {
|
|
||||||
// Add your item here
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(right: 30, top: 10),
|
|
||||||
child: Text(
|
|
||||||
'',
|
|
||||||
textAlign: TextAlign.right,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
final item = _wrap[index - 1];
|
|
||||||
return _item(context, item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
if (_hasData == true && _loading == true)
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(top: 15, bottom: 15),
|
|
||||||
child: Center(
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(
|
|
||||||
FlutterFlowTheme.of(context).primary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
].addToStart(const SizedBox(height: 0)),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
PreferredSizeWidget _appBar(BuildContext context) {
|
/// [Body] of the page.
|
||||||
|
FutureBuilder<bool> _buildBody(BuildContext context) {
|
||||||
|
Widget progressIndicator() {
|
||||||
|
return CircularProgressIndicator(
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(
|
||||||
|
FlutterFlowTheme.of(context).primary,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return FutureBuilder<bool>(
|
||||||
|
future: _initializeModule(),
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
|
return progressIndicator();
|
||||||
|
} else if (snapshot.hasError) {
|
||||||
|
return progressIndicator();
|
||||||
|
} else if (snapshot.hasData && snapshot.data == true) {
|
||||||
|
return _buildVehicleManager(context);
|
||||||
|
} else {
|
||||||
|
return _buildVehicleHistory(context);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<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]) {
|
||||||
|
bool isFirst = _model.tabBarController.index == 0;
|
||||||
|
|
||||||
|
if (_model.isEditing & isFirst) {
|
||||||
|
_model.handleEditingChanged(false);
|
||||||
|
}
|
||||||
|
if (isFirst) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildVehicleHistory(BuildContext context) {
|
||||||
|
return VehicleHistoryScreen(_model);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildVehicleManager(BuildContext context) {
|
||||||
|
final vehicleHistoryScreenLabel = FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Consultar', enText: 'History');
|
||||||
|
final vehicleRegisterScreenLabel = FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Cadastrar', enText: 'Register');
|
||||||
|
final vehicleUpdateScreenLabel = FFLocalizations.of(context)
|
||||||
|
.getVariableText(ptText: 'Editar', enText: 'Edit');
|
||||||
|
|
||||||
|
return TabViewUtil(
|
||||||
|
context: context,
|
||||||
|
model: _model,
|
||||||
|
labelTab1: vehicleHistoryScreenLabel,
|
||||||
|
labelTab2: _model.isEditing
|
||||||
|
? vehicleUpdateScreenLabel
|
||||||
|
: vehicleRegisterScreenLabel,
|
||||||
|
controller: _model.tabBarController,
|
||||||
|
widget1: VehicleHistoryScreen(_model),
|
||||||
|
widget2: _model.isEditing
|
||||||
|
? VehicleUpdateScreen(_model)
|
||||||
|
: VehicleRegisterScreen(_model),
|
||||||
|
onEditingChanged: onEditingChanged,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// -----------------------------------
|
||||||
|
/// [Header] of the page.
|
||||||
|
PreferredSizeWidget _buildHeader(BuildContext context) {
|
||||||
|
final theme = FlutterFlowTheme.of(context);
|
||||||
|
final backgroundColor = theme.primaryBackground;
|
||||||
|
final primaryText = theme.primaryText;
|
||||||
|
final title = FFLocalizations.of(context)
|
||||||
|
.getVariableText(enText: 'Vehicles', ptText: 'Veículos');
|
||||||
|
final titleStyle = theme.headlineMedium.override(
|
||||||
|
fontFamily: theme.headlineMediumFamily,
|
||||||
|
color: primaryText,
|
||||||
|
fontSize: 16.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
letterSpacing: 0.0,
|
||||||
|
useGoogleFonts:
|
||||||
|
GoogleFonts.asMap().containsKey(theme.headlineMediumFamily),
|
||||||
|
);
|
||||||
|
final backButton = _backButton(context, theme);
|
||||||
|
|
||||||
return AppBar(
|
return AppBar(
|
||||||
backgroundColor: FlutterFlowTheme.of(context).primaryBackground,
|
backgroundColor: backgroundColor,
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
title: Text(
|
title: Text(title, style: titleStyle),
|
||||||
FFLocalizations.of(context)
|
leading: backButton,
|
||||||
.getVariableText(enText: 'Vehicles', ptText: 'Veículos'),
|
|
||||||
style: FlutterFlowTheme.of(context).headlineMedium.override(
|
|
||||||
fontFamily: FlutterFlowTheme.of(context).headlineMediumFamily,
|
|
||||||
color: FlutterFlowTheme.of(context).primaryText,
|
|
||||||
fontSize: 16.0,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
letterSpacing: 0.0,
|
|
||||||
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
|
||||||
FlutterFlowTheme.of(context).headlineMediumFamily),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
leading: _backButton(context, FlutterFlowTheme.of(context)),
|
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
elevation: 0.0,
|
elevation: 0.0,
|
||||||
actions: [],
|
actions: [],
|
||||||
|
@ -151,131 +186,22 @@ class _VehicleOnThePropertyState extends State<VehicleOnTheProperty>
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _backButton(BuildContext context, FlutterFlowTheme theme) {
|
Widget _backButton(BuildContext context, FlutterFlowTheme theme) {
|
||||||
|
final Icon icon = Icon(
|
||||||
|
Icons.keyboard_arrow_left,
|
||||||
|
color: theme.primaryText,
|
||||||
|
size: 30.0,
|
||||||
|
);
|
||||||
|
onPressed() => Navigator.of(context).pop();
|
||||||
return FlutterFlowIconButton(
|
return FlutterFlowIconButton(
|
||||||
key: ValueKey<String>('BackNavigationAppBar'),
|
key: ValueKey<String>('BackNavigationAppBar'),
|
||||||
borderColor: Colors.transparent,
|
borderColor: Colors.transparent,
|
||||||
borderRadius: 30.0,
|
borderRadius: 30.0,
|
||||||
borderWidth: 1.0,
|
borderWidth: 1.0,
|
||||||
buttonSize: 60.0,
|
buttonSize: 60.0,
|
||||||
icon: Icon(
|
icon: icon,
|
||||||
Icons.keyboard_arrow_left,
|
onPressed: onPressed,
|
||||||
color: theme.primaryText,
|
|
||||||
size: 30.0,
|
|
||||||
),
|
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ApiCallResponse?> _fetchVisits() async {
|
/// -----------------------------------
|
||||||
try {
|
|
||||||
setState(() => _loading = true);
|
|
||||||
|
|
||||||
var response = await FreAccessWSGlobal.getVehiclesByProperty
|
|
||||||
.call(_pageNumber.toString());
|
|
||||||
|
|
||||||
final List<dynamic> vehicles = response.jsonBody['vehicles'] ?? [];
|
|
||||||
safeSetState(() => count = response.jsonBody['total_rows'] ?? 0);
|
|
||||||
|
|
||||||
if (vehicles.isNotEmpty) {
|
|
||||||
setState(() {
|
|
||||||
_wrap.addAll(vehicles);
|
|
||||||
_hasData = true;
|
|
||||||
_loading = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
_showNoMoreDataSnackBar(context);
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
_hasData = false;
|
|
||||||
_loading = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
return null;
|
|
||||||
} catch (e, s) {
|
|
||||||
DialogUtil.errorDefault(context);
|
|
||||||
LogUtil.requestAPIFailed(
|
|
||||||
"proccessRequest.php", "", "Consulta de Veículo", e, s);
|
|
||||||
setState(() {
|
|
||||||
_hasData = false;
|
|
||||||
_loading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _loadMore() {
|
|
||||||
if (_hasData == true) {
|
|
||||||
_pageNumber++;
|
|
||||||
|
|
||||||
_future = _fetchVisits();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _showNoMoreDataSnackBar(BuildContext context) {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text(
|
|
||||||
FFLocalizations.of(context).getVariableText(
|
|
||||||
ptText: "Não há mais dados.",
|
|
||||||
enText: "No more data.",
|
|
||||||
),
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: LimitedFontSizeUtil.getBodyFontSize(context),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
duration: const Duration(seconds: 3),
|
|
||||||
backgroundColor: FlutterFlowTheme.of(context).primary,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _item(BuildContext context, dynamic uItem) {
|
|
||||||
return CardItemTemplateComponentWidget(
|
|
||||||
imagePath: null,
|
|
||||||
labelsHashMap: {
|
|
||||||
'${FFLocalizations.of(context).getVariableText(ptText: "Placa", enText: "License Plate")}:':
|
|
||||||
uItem['licensePlate'] ?? '',
|
|
||||||
'${FFLocalizations.of(context).getVariableText(ptText: "Modelo", enText: "Model")}:':
|
|
||||||
uItem['model'] ?? '',
|
|
||||||
'${FFLocalizations.of(context).getVariableText(ptText: "Tag", enText: "Tag")}:':
|
|
||||||
uItem['tag'] ?? '',
|
|
||||||
},
|
|
||||||
statusHashMap: [],
|
|
||||||
onTapCardItemAction: () async {
|
|
||||||
await showDialog(
|
|
||||||
useSafeArea: true,
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return Dialog(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: model.buildVehicleDetails(
|
|
||||||
item: uItem,
|
|
||||||
context: context,
|
|
||||||
model: model,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
).whenComplete(() {
|
|
||||||
safeSetState(() {
|
|
||||||
_pageNumber = 1;
|
|
||||||
_wrap = [];
|
|
||||||
_future = _fetchVisits()
|
|
||||||
.then((value) => value!.jsonBody['vehicles'] ?? []);
|
|
||||||
});
|
|
||||||
}).catchError((e, s) {
|
|
||||||
DialogUtil.errorDefault(context);
|
|
||||||
LogUtil.requestAPIFailed(
|
|
||||||
"proccessRequest.php", "", "Consulta de Veículos", e, s);
|
|
||||||
safeSetState(() {
|
|
||||||
_hasData = false;
|
|
||||||
_loading = false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
||||||
import 'package:hub/flutter_flow/internationalization.dart';
|
import 'package:hub/flutter_flow/internationalization.dart';
|
||||||
import 'package:hub/pages/vehicles_on_the_property/vehicles_on_the_property.dart';
|
import 'package:hub/pages/vehicles_on_the_property/vehicles_on_the_property.dart';
|
||||||
|
|
||||||
class VisitsModel extends FlutterFlowModel<VehicleOnTheProperty> {
|
class VisitsModel extends FlutterFlowModel<VehiclePage> {
|
||||||
static VisitsModel? _instance;
|
static VisitsModel? _instance;
|
||||||
|
|
||||||
VisitsModel._internal({this.onRefresh});
|
VisitsModel._internal({this.onRefresh});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
extension StringNullableExtensions on String? {
|
extension StringNullableExtensions on String? {
|
||||||
bool toBoolean() {
|
bool get toBoolean {
|
||||||
if (this == null) return false;
|
if (this == null) return false;
|
||||||
return this!.toLowerCase() == 'true';
|
return this!.toLowerCase() == 'true';
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,16 @@ extension StringNullableExtensions on String? {
|
||||||
if (this == '') return true;
|
if (this == '') return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get isNotNullAndEmpty {
|
||||||
|
if (this == null) return false;
|
||||||
|
if (this == '') return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension StringExtensions on String {
|
extension StringExtensions on String {
|
||||||
bool toBoolean() {
|
bool get toBoolean {
|
||||||
return toLowerCase() == 'true';
|
return toLowerCase() == 'true';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hub/flutter_flow/index.dart';
|
||||||
|
import 'package:hub/shared/utils/limited_text_size.dart';
|
||||||
|
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||||
|
|
||||||
|
extension PagedListViewExtension<PageKeyType, ItemType>
|
||||||
|
on PagedSliverList<PageKeyType, ItemType> {}
|
||||||
|
|
||||||
|
mixin Pageable<T extends StatefulWidget> on State<T> {
|
||||||
|
Expanded buildPaginatedListView<X, Y>(
|
||||||
|
String noDataFound,
|
||||||
|
PagingController<X, Y> pg,
|
||||||
|
Widget Function(BuildContext, Y, int) itemBuilder) {
|
||||||
|
final theme = FlutterFlowTheme.of(context);
|
||||||
|
|
||||||
|
return Expanded(
|
||||||
|
child: RefreshIndicator(
|
||||||
|
backgroundColor: theme.primaryBackground,
|
||||||
|
color: theme.primary,
|
||||||
|
onRefresh: () async => pg.refresh(),
|
||||||
|
child: PagedListView<X, Y>(
|
||||||
|
pagingController: pg,
|
||||||
|
builderDelegate: PagedChildBuilderDelegate<Y>(
|
||||||
|
animateTransitions: true,
|
||||||
|
|
||||||
|
itemBuilder: (context, item, index) =>
|
||||||
|
itemBuilder(context, item, index),
|
||||||
|
// noMoreItemsIndicatorBuilder: ,
|
||||||
|
newPageProgressIndicatorBuilder: (context) =>
|
||||||
|
buildLoadingIndicator(context),
|
||||||
|
firstPageProgressIndicatorBuilder: (context) =>
|
||||||
|
buildLoadingIndicator(context),
|
||||||
|
noItemsFoundIndicatorBuilder: (context) =>
|
||||||
|
buildNoDataFound(context, noDataFound),
|
||||||
|
// firstPageErrorIndicatorBuilder: (context) => const Placeholder(),
|
||||||
|
// newPageErrorIndicatorBuilder: (context) => const Placeholder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> fetchPage({
|
||||||
|
required Future<(bool, dynamic)> Function() dataProvider,
|
||||||
|
required void Function(dynamic data) onDataAvailable,
|
||||||
|
required void Function(dynamic data) onDataUnavailable,
|
||||||
|
required void Function(Object error, StackTrace stackTrace) onFetchError,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
final (bool isDataAvailable, dynamic data) = await dataProvider();
|
||||||
|
if (isDataAvailable) {
|
||||||
|
onDataAvailable(data);
|
||||||
|
} else {
|
||||||
|
onDataUnavailable(data);
|
||||||
|
}
|
||||||
|
} catch (error, stackTrace) {
|
||||||
|
onFetchError(error, stackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showNoMoreDataSnackBar(BuildContext context) {
|
||||||
|
final message = FFLocalizations.of(context).getVariableText(
|
||||||
|
ptText: "Não há mais dados.",
|
||||||
|
enText: "No more data.",
|
||||||
|
);
|
||||||
|
|
||||||
|
showSnackbarMessenger(context, message, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildNoDataFound(BuildContext context, String title) {
|
||||||
|
final headerFontSize = LimitedFontSizeUtil.getHeaderFontSize(context);
|
||||||
|
// final bodyFontSize = LimitedFontSizeUtil.getBodyFontSize(context);
|
||||||
|
return Expanded(
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontFamily: 'Nunito',
|
||||||
|
fontSize: headerFontSize,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildLoadingIndicator(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 15),
|
||||||
|
child: Center(
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(
|
||||||
|
FlutterFlowTheme.of(context).primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import 'package:hub/features/module/index.dart';
|
||||||
|
import 'package:hub/flutter_flow/index.dart';
|
||||||
|
import 'package:hub/shared/utils/datetime_util.dart';
|
||||||
|
|
||||||
|
class LicenseUtil {
|
||||||
|
static Future<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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,8 +3,9 @@ name: hub
|
||||||
description: . # Descrição do projeto (adicione mais detalhes se necessário)
|
description: . # Descrição do projeto (adicione mais detalhes se necessário)
|
||||||
publish_to: "none" # Destino de publicação
|
publish_to: "none" # Destino de publicação
|
||||||
|
|
||||||
# Versão do aplicativo
|
publish_to: "none"
|
||||||
version: 1.3.5+24
|
|
||||||
|
version: 1.4.0+27
|
||||||
|
|
||||||
# Restrições de versão do SDK Dart
|
# Restrições de versão do SDK Dart
|
||||||
environment:
|
environment:
|
||||||
|
@ -113,7 +114,9 @@ dependencies:
|
||||||
url_launcher_platform_interface: 2.3.2
|
url_launcher_platform_interface: 2.3.2
|
||||||
permission_handler: ^11.3.1
|
permission_handler: ^11.3.1
|
||||||
awesome_notifications: ^0.10.0
|
awesome_notifications: ^0.10.0
|
||||||
app_tracking_transparency: ^2.0.6
|
app_tracking_transparency: ^2.0.6+1
|
||||||
|
# dio: ^5.7.0
|
||||||
|
# crypto: ^3.0.5
|
||||||
freezed_annotation: ^2.4.4
|
freezed_annotation: ^2.4.4
|
||||||
package_info_plus: ^8.1.1
|
package_info_plus: ^8.1.1
|
||||||
sliver_tools: ^0.2.12
|
sliver_tools: ^0.2.12
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Define the base URL for the API endpoint
|
||||||
|
BASE_URL="https://freaccess.com.br/freaccess/processRequest.php"
|
||||||
|
|
||||||
|
# Define common parameters
|
||||||
|
DEV_UUID="6b7b81849c115a8a"
|
||||||
|
USER_UUID="678aa05b0c2154.50583237"
|
||||||
|
CLI_ID="7"
|
||||||
|
ACTIVITY="getVehiclesByProperty"
|
||||||
|
PAGE_SIZE="10"
|
||||||
|
|
||||||
|
# Function to perform the HTTP request
|
||||||
|
perform_request() {
|
||||||
|
local page=$1
|
||||||
|
http --form POST "$BASE_URL" \
|
||||||
|
devUUID="$DEV_UUID" \
|
||||||
|
userUUID="$USER_UUID" \
|
||||||
|
cliID="$CLI_ID" \
|
||||||
|
atividade="$ACTIVITY" \
|
||||||
|
page="$page" \
|
||||||
|
pageSize="$PAGE_SIZE" \
|
||||||
|
--check-status \
|
||||||
|
--ignore-stdin \
|
||||||
|
--timeout=10
|
||||||
|
}
|
||||||
|
|
||||||
|
# Perform requests for pages 1 and 2
|
||||||
|
perform_request 1
|
||||||
|
perform_request 2
|
Loading…
Reference in New Issue