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/material.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/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/details_component/details_component_widget.dart';
|
||||
import 'package:hub/features/backend/api_requests/index.dart';
|
||||
import 'package:hub/features/local/index.dart';
|
||||
import 'package:hub/features/menu/index.dart';
|
||||
|
@ -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';
|
||||
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:flutter_web_plugins/url_strategy.dart';
|
||||
|
||||
|
@ -27,7 +31,6 @@ import 'app_test.dart';
|
|||
import 'fuzzer/fuzzer.dart';
|
||||
|
||||
import 'package:patrol_finders/patrol_finders.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
export 'package:flutter_test/flutter_test.dart';
|
||||
export 'package:patrol/patrol.dart';
|
||||
|
||||
|
@ -56,6 +59,7 @@ part 'storage_test.dart';
|
|||
part 'utils_test.dart';
|
||||
|
||||
part 'welcome_test.dart';
|
||||
part 'vehicle_test.dart';
|
||||
|
||||
late PatrolTester $;
|
||||
|
||||
|
@ -83,5 +87,9 @@ void main() {
|
|||
|
||||
LocalsTest.setLocal();
|
||||
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/shared/utils/limited_text_size.dart';
|
||||
|
||||
class UpperCaseTextFormatter extends TextInputFormatter {
|
||||
@override
|
||||
TextEditingValue formatEditUpdate(
|
||||
TextEditingValue oldValue, TextEditingValue newValue) {
|
||||
return TextEditingValue(
|
||||
text: newValue.text.toUpperCase(), selection: newValue.selection);
|
||||
}
|
||||
}
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class CustomInputUtil extends StatefulWidget {
|
||||
final TextEditingController? controller;
|
||||
|
@ -20,22 +29,25 @@ class CustomInputUtil extends StatefulWidget {
|
|||
final String? Function(String?)? validator;
|
||||
final bool haveMaxLength;
|
||||
final void Function(String)? onChanged;
|
||||
final List<TextInputFormatter>? inputFormatters;
|
||||
|
||||
CustomInputUtil(
|
||||
{super.key,
|
||||
this.controller,
|
||||
required this.labelText,
|
||||
required this.hintText,
|
||||
required this.suffixIcon,
|
||||
this.autoFocus = false,
|
||||
required this.focusNode,
|
||||
this.onChanged,
|
||||
this.textInputAction = TextInputAction.next,
|
||||
this.keyboardType = TextInputType.text,
|
||||
this.maxLength = 80,
|
||||
this.validator,
|
||||
this.obscureText,
|
||||
required this.haveMaxLength});
|
||||
CustomInputUtil({
|
||||
super.key,
|
||||
this.controller,
|
||||
required this.labelText,
|
||||
required this.hintText,
|
||||
required this.suffixIcon,
|
||||
this.autoFocus = false,
|
||||
required this.focusNode,
|
||||
this.onChanged,
|
||||
this.textInputAction = TextInputAction.next,
|
||||
this.keyboardType = TextInputType.text,
|
||||
this.maxLength = 80,
|
||||
this.validator,
|
||||
this.obscureText,
|
||||
this.inputFormatters,
|
||||
required this.haveMaxLength,
|
||||
});
|
||||
|
||||
@override
|
||||
State<CustomInputUtil> createState() => _CustomInputUtilState();
|
||||
|
@ -152,6 +164,7 @@ class _CustomInputUtilState extends State<CustomInputUtil> {
|
|||
keyboardType: widget.keyboardType,
|
||||
inputFormatters: [
|
||||
LengthLimitingTextInputFormatter(widget.maxLength),
|
||||
if (widget.inputFormatters != null) ...widget.inputFormatters!
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
|
@ -10,7 +10,7 @@ class TabViewUtil extends StatelessWidget {
|
|||
String labelTab1;
|
||||
String labelTab2;
|
||||
final TabController controller;
|
||||
final Function(bool) onEditingChanged;
|
||||
final Function([bool]) onEditingChanged;
|
||||
Widget widget1;
|
||||
Widget widget2;
|
||||
|
||||
|
@ -49,15 +49,17 @@ class TabViewUtil extends StatelessWidget {
|
|||
padding: const EdgeInsets.all(4.0),
|
||||
tabs: [
|
||||
Tab(
|
||||
key: ValueKey('TabView_Tab1'),
|
||||
text: labelTab1,
|
||||
),
|
||||
Tab(
|
||||
key: ValueKey('TabView_Tab2'),
|
||||
text: labelTab2,
|
||||
),
|
||||
],
|
||||
controller: controller,
|
||||
onTap: (i) async {
|
||||
if (i == 1) onEditingChanged(false);
|
||||
onEditingChanged();
|
||||
[() async {}, () async {}][i]();
|
||||
},
|
||||
),
|
||||
|
|
|
@ -209,6 +209,7 @@ class _BottomArrowLinkedLocalsComponentWidgetState
|
|||
return CardItemTemplateComponentWidget(
|
||||
key: ValueKey<String>(local['CLI_NOME']),
|
||||
imagePath: _imagePath(local),
|
||||
icon: null,
|
||||
labelsHashMap: _labelsHashMap(local),
|
||||
statusHashMap: [_statusHashMap(local)],
|
||||
onTapCardItemAction: () async {
|
||||
|
|
|
@ -12,18 +12,74 @@ import 'card_item_template_component_model.dart';
|
|||
|
||||
export 'card_item_template_component_model.dart';
|
||||
|
||||
class FreCardIcon extends StatelessWidget {
|
||||
final double height;
|
||||
final double width;
|
||||
final Icon icon;
|
||||
|
||||
const FreCardIcon({
|
||||
super.key,
|
||||
required this.height,
|
||||
required this.width,
|
||||
required this.icon,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: height,
|
||||
width: width,
|
||||
child: icon,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FreCardPin extends StatelessWidget {
|
||||
final double height;
|
||||
final double width;
|
||||
final Color color;
|
||||
|
||||
const FreCardPin({
|
||||
super.key,
|
||||
required this.height,
|
||||
required this.width,
|
||||
required this.color,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Container(
|
||||
height: height,
|
||||
width: width,
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CardItemTemplateComponentWidget extends StatefulWidget {
|
||||
const CardItemTemplateComponentWidget({
|
||||
super.key,
|
||||
required this.labelsHashMap,
|
||||
required this.statusHashMap,
|
||||
required this.imagePath,
|
||||
this.imagePath,
|
||||
this.icon,
|
||||
this.pin,
|
||||
this.itemWidthFactor = 0.25,
|
||||
required this.onTapCardItemAction,
|
||||
});
|
||||
|
||||
final Map<String, String>? labelsHashMap;
|
||||
final List<Map<String, Color>?> statusHashMap;
|
||||
final String? imagePath;
|
||||
final FreCardIcon? icon;
|
||||
final FreCardPin? pin;
|
||||
final double itemWidthFactor;
|
||||
final Future Function()? onTapCardItemAction;
|
||||
|
||||
@override
|
||||
|
@ -125,9 +181,24 @@ class _CardItemTemplateComponentWidgetState
|
|||
);
|
||||
}
|
||||
|
||||
Widget _generateIcon() {
|
||||
return Column(
|
||||
children: [
|
||||
widget.icon!,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _generatePin() {
|
||||
return widget.pin!;
|
||||
}
|
||||
|
||||
List<Widget> _generateStatus() {
|
||||
double limitedBodyTextSize = LimitedFontSizeUtil.getBodyFontSize(context);
|
||||
|
||||
return statusLinkedHashMap.expand((statusLinked) {
|
||||
log('statusHashMap: ${statusLinked.length}');
|
||||
|
||||
return statusLinked.entries.map((entry) {
|
||||
final text = entry.key;
|
||||
final color = entry.value;
|
||||
|
@ -136,7 +207,7 @@ class _CardItemTemplateComponentWidgetState
|
|||
message: text,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(5),
|
||||
width: MediaQuery.of(context).size.width * 0.25,
|
||||
width: MediaQuery.of(context).size.width * widget.itemWidthFactor,
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
|
@ -162,38 +233,50 @@ class _CardItemTemplateComponentWidgetState
|
|||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
if (constraints.maxWidth > 360) {
|
||||
return Row(
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
..._generateLabels(),
|
||||
SizedBox(height: 3),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 4,
|
||||
children: _generateStatus(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [if (widget.pin != null) _generatePin()]),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
..._generateLabels(),
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 4,
|
||||
children: _generateStatus(),
|
||||
),
|
||||
]
|
||||
.addToEnd(const SizedBox(height: 5))
|
||||
.divide(const SizedBox(height: 3))
|
||||
.addToStart(const SizedBox(height: 5)),
|
||||
),
|
||||
]
|
||||
.addToEnd(const SizedBox(height: 5))
|
||||
.divide(const SizedBox(height: 1))
|
||||
.addToStart(const SizedBox(height: 5)),
|
||||
),
|
||||
),
|
||||
if (widget.icon != null) _generateIcon(),
|
||||
if (widget.imagePath != null) _generateImage()
|
||||
]
|
||||
.addToEnd(const SizedBox(width: 10))
|
||||
.addToStart(const SizedBox(width: 10)),
|
||||
),
|
||||
if (widget.imagePath != null) _generateImage(),
|
||||
]
|
||||
.addToEnd(const SizedBox(width: 10))
|
||||
.addToStart(const SizedBox(width: 10)),
|
||||
].addToStart(SizedBox(height: 5)).addToEnd(SizedBox(height: 5)),
|
||||
);
|
||||
} else {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (widget.imagePath != null) _generateImage(),
|
||||
if (widget.icon != null) _generateIcon(),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Column(
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:collection';
|
|||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart';
|
||||
import 'package:hub/components/templates_components/details_component/details_component_model.dart';
|
||||
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
||||
import 'package:hub/flutter_flow/flutter_flow_util.dart';
|
||||
|
@ -14,6 +15,7 @@ class DetailsComponentWidget extends StatefulWidget {
|
|||
required this.labelsHashMap,
|
||||
required this.statusHashMap,
|
||||
this.imagePath,
|
||||
this.icon,
|
||||
this.onTapCardItemAction,
|
||||
required this.buttons,
|
||||
});
|
||||
|
@ -23,6 +25,7 @@ class DetailsComponentWidget extends StatefulWidget {
|
|||
final String? imagePath;
|
||||
final Future Function()? onTapCardItemAction;
|
||||
final List<Widget>? buttons;
|
||||
final FreCardIcon? icon;
|
||||
|
||||
@override
|
||||
State<DetailsComponentWidget> createState() => _DetailsComponentWidgetState();
|
||||
|
@ -64,225 +67,232 @@ class _DetailsComponentWidgetState extends State<DetailsComponentWidget> {
|
|||
// CachedNetworkImage.evictFromCache(widget.imagePath ?? '');
|
||||
final double limitedBodyFontSize =
|
||||
LimitedFontSizeUtil.getBodyFontSize(context);
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Container(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width,
|
||||
maxHeight: MediaQuery.of(context).size.height,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: FlutterFlowTheme.of(context).primaryBackground,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(25.0)),
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: MediaQuery.of(context).size.height * 0.02),
|
||||
if (widget.imagePath != null && widget.imagePath != '')
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width * 0.3,
|
||||
height: MediaQuery.of(context).size.width * 0.3,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: CachedNetworkImage(
|
||||
fadeInDuration: const Duration(milliseconds: 100),
|
||||
fadeOutDuration: const Duration(milliseconds: 100),
|
||||
imageUrl: widget.imagePath!,
|
||||
fit: BoxFit.cover,
|
||||
useOldImageOnUrlChange: true,
|
||||
),
|
||||
return Container(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width,
|
||||
maxHeight: MediaQuery.of(context).size.height,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: FlutterFlowTheme.of(context).primaryBackground,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(25.0)),
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: MediaQuery.of(context).size.height * 0.02),
|
||||
if (widget.imagePath != null && widget.imagePath != '')
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width * 0.3,
|
||||
height: MediaQuery.of(context).size.width * 0.3,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
SizedBox(height: MediaQuery.of(context).size.height * 0.03),
|
||||
Row(
|
||||
children: statusLinkedHashMap.expand((linkedHashMap) {
|
||||
return linkedHashMap.entries
|
||||
.map((MapEntry<String, Color> item) {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: MediaQuery.of(context).size.width * 0.05,
|
||||
),
|
||||
child: TextFormField(
|
||||
autofocus: false,
|
||||
canRequestFocus: false,
|
||||
readOnly: true,
|
||||
obscureText: false,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
borderSide: BorderSide(
|
||||
color: item.value,
|
||||
),
|
||||
),
|
||||
filled: true,
|
||||
fillColor: item.value,
|
||||
labelText: item.key,
|
||||
labelStyle: FlutterFlowTheme.of(context)
|
||||
.labelMedium
|
||||
.override(
|
||||
fontFamily: FlutterFlowTheme.of(context)
|
||||
.labelMediumFamily,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: FlutterFlowTheme.of(context).info,
|
||||
letterSpacing: 0.0,
|
||||
useGoogleFonts:
|
||||
GoogleFonts.asMap().containsKey(
|
||||
FlutterFlowTheme.of(context)
|
||||
.labelMediumFamily,
|
||||
),
|
||||
fontSize: limitedBodyFontSize,
|
||||
),
|
||||
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,
|
||||
child: CachedNetworkImage(
|
||||
fadeInDuration: const Duration(milliseconds: 100),
|
||||
fadeOutDuration: const Duration(milliseconds: 100),
|
||||
imageUrl: widget.imagePath!,
|
||||
fit: BoxFit.cover,
|
||||
useOldImageOnUrlChange: true,
|
||||
),
|
||||
),
|
||||
if (widget.icon != null && widget.icon != '')
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width * 0.3,
|
||||
height: MediaQuery.of(context).size.width * 0.3,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: widget.icon!,
|
||||
),
|
||||
SizedBox(height: MediaQuery.of(context).size.height * 0.03),
|
||||
Row(
|
||||
children: statusLinkedHashMap.expand((linkedHashMap) {
|
||||
return linkedHashMap.entries
|
||||
.map((MapEntry<String, Color> item) {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: MediaQuery.of(context).size.width * 0.05,
|
||||
),
|
||||
child: TextFormField(
|
||||
autofocus: false,
|
||||
canRequestFocus: false,
|
||||
readOnly: true,
|
||||
initialValue: item.key,
|
||||
obscureText: false,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
borderSide: BorderSide(
|
||||
color: item.value,
|
||||
),
|
||||
),
|
||||
style: FlutterFlowTheme.of(context)
|
||||
.bodyMedium
|
||||
filled: true,
|
||||
fillColor: item.value,
|
||||
// labelText: item.key,
|
||||
labelStyle: FlutterFlowTheme.of(context)
|
||||
.labelMedium
|
||||
.override(
|
||||
fontFamily: FlutterFlowTheme.of(context)
|
||||
.bodyMediumFamily,
|
||||
.labelMediumFamily,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: FlutterFlowTheme.of(context).info,
|
||||
letterSpacing: 0.0,
|
||||
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
||||
FlutterFlowTheme.of(context).bodyMediumFamily,
|
||||
FlutterFlowTheme.of(context)
|
||||
.labelMediumFamily,
|
||||
),
|
||||
fontSize: limitedBodyFontSize,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: null,
|
||||
keyboardType: TextInputType.name,
|
||||
validator: _model.textController1Validator
|
||||
.asValidator(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).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,
|
||||
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,
|
||||
),
|
||||
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:
|
||||
style: FlutterFlowTheme.of(context)
|
||||
.labelMedium
|
||||
.override(
|
||||
fontFamily: FlutterFlowTheme.of(context)
|
||||
.labelMediumFamily,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: FlutterFlowTheme.of(context).info,
|
||||
letterSpacing: 0.0,
|
||||
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
||||
FlutterFlowTheme.of(context).labelMediumFamily,
|
||||
color: FlutterFlowTheme.of(context).primaryText,
|
||||
letterSpacing: 0.0,
|
||||
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
||||
FlutterFlowTheme.of(context).labelMediumFamily,
|
||||
),
|
||||
fontSize: limitedBodyFontSize,
|
||||
),
|
||||
),
|
||||
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
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: null,
|
||||
keyboardType: TextInputType.name,
|
||||
validator: _model.textController1Validator
|
||||
.asValidator(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
}).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),
|
||||
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),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -34,6 +34,7 @@ class RegisiterVistorTemplateComponentWidget extends StatefulWidget {
|
|||
class _RegisiterVistorTemplateComponentWidgetState
|
||||
extends State<RegisiterVistorTemplateComponentWidget> {
|
||||
late RegisiterVistorTemplateComponentModel _model;
|
||||
final bool _isLoading = false;
|
||||
final scaffoldKey = GlobalKey<ScaffoldState>();
|
||||
|
||||
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
|
||||
GetLicense getLicense = GetLicense();
|
||||
static GetProvSchedules getProvSchedules = GetProvSchedules();
|
||||
static RegisterVehicle registerVehicle = RegisterVehicle();
|
||||
static UpdateVehicle updateVehicle = UpdateVehicle();
|
||||
static DeleteVehicle deleteVehicle = DeleteVehicle();
|
||||
static CancelDeleteVehicle cancelDelete = CancelDeleteVehicle();
|
||||
}
|
||||
|
||||
class CancelDeleteVehicle {
|
||||
Future<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 GetDocuments getDocuments = GetDocuments();
|
||||
}
|
||||
|
|
|
@ -496,11 +496,11 @@ class ApiManager {
|
|||
result = ApiCallResponse(null, {}, -1, exception: e);
|
||||
}
|
||||
|
||||
log('API Call: $callName');
|
||||
log('URL: $apiUrl');
|
||||
log('Headers: $headers');
|
||||
log('Params: $params');
|
||||
log('Response: ${result.jsonBody}');
|
||||
print('API Call: $callName');
|
||||
print('URL: $apiUrl');
|
||||
print('Headers: $headers');
|
||||
print('Params: $params');
|
||||
print('Response: ${result.jsonBody}');
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,12 +46,15 @@ class _AccessHistoryState extends State<AccessHistoryScreen> {
|
|||
selectedTypeSubject.listen((value) {});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_model = createModel(context, () => AcessHistoryPageModel());
|
||||
_accessFuture = fetchAccessHistoryService();
|
||||
|
||||
|
||||
_scrollController = ScrollController()
|
||||
..addListener(() {
|
||||
if (_scrollController.position.atEdge &&
|
||||
|
|
|
@ -80,7 +80,6 @@ class LocalsRepositoryImpl implements LocalsRepository {
|
|||
}
|
||||
} else {
|
||||
log('_handleLocal -> Local selected');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -115,6 +115,9 @@ class LocalUtil {
|
|||
.set(LocalsStorageKey.whatsapp.key, jsonBody['whatsapp'] ?? false);
|
||||
await StorageHelper().set(
|
||||
LocalsStorageKey.provisional.key, jsonBody['provisional'] ?? false);
|
||||
await StorageHelper().set(LocalsStorageKey.vehicleAutoApproval.key,
|
||||
jsonBody['vehicleAutoApproval'] ?? false);
|
||||
|
||||
await StorageHelper().set(
|
||||
LocalsStorageKey.pets.key,
|
||||
jsonBody['pet'] ?? false,
|
||||
|
@ -141,6 +144,13 @@ class LocalUtil {
|
|||
jsonBody['petAmountRegister']?.toString().isEmpty ?? true
|
||||
? '0'
|
||||
: jsonBody['petAmountRegister'].toString());
|
||||
|
||||
await StorageHelper().set(
|
||||
LocalsStorageKey.vehicleAmountRegister.key,
|
||||
jsonBody['vehicleAmountRegister']?.toString().isEmpty ?? true
|
||||
? '0'
|
||||
: jsonBody['vehicleAmountRegister'].toString());
|
||||
|
||||
await StorageHelper().set(ProfileStorageKey.userName.key,
|
||||
jsonBody['visitado']['VDO_NOME'] ?? '');
|
||||
await StorageHelper().set(ProfileStorageKey.userEmail.key,
|
||||
|
|
|
@ -17,10 +17,6 @@ abstract class MenuLocalDataSource {
|
|||
|
||||
Future<void> handleMenu(EnumMenuItem item, EnumDisplay display, MenuEntry opt,
|
||||
List<MenuItem?> entries);
|
||||
|
||||
Future<bool> processStartDate(String startDate, MenuEntry entry);
|
||||
|
||||
Future<bool> processExpirationDate(String expirationDate, MenuEntry entry);
|
||||
}
|
||||
|
||||
class MenuLocalDataSourceImpl implements MenuLocalDataSource {
|
||||
|
@ -92,30 +88,4 @@ class MenuLocalDataSourceImpl implements MenuLocalDataSource {
|
|||
log('Error processing display for module ${opt.key}: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> processStartDate(String startDate, MenuEntry opt) async {
|
||||
try {
|
||||
if (startDate.isEmpty) return true;
|
||||
final start = DateTime.tryParse(startDate);
|
||||
if (start == null) return false;
|
||||
return DateTime.now().isAfter(start);
|
||||
} catch (e) {
|
||||
log('Error processing start date for module ${opt.key}: $e');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> processExpirationDate(
|
||||
String expirationDate, MenuEntry opt) async {
|
||||
try {
|
||||
if (expirationDate.isEmpty) return false;
|
||||
final expiration = DateTime.tryParse(expirationDate);
|
||||
return expiration != null && DateTime.now().isAfter(expiration);
|
||||
} catch (e) {
|
||||
log('Error processing expiration date for module ${opt.key}: $e');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:hub/features/menu/index.dart';
|
|||
import 'package:hub/features/module/index.dart';
|
||||
import 'package:hub/features/storage/index.dart';
|
||||
import 'package:hub/flutter_flow/custom_functions.dart';
|
||||
import 'package:hub/shared/utils/datetime_util.dart';
|
||||
|
||||
class MenuRepositoryImpl implements MenuRepository {
|
||||
final MenuLocalDataSource menuDataSource = MenuLocalDataSourceImpl();
|
||||
|
@ -25,10 +26,9 @@ class MenuRepositoryImpl implements MenuRepository {
|
|||
final display = EnumDisplay.fromString(licenseMap['display']);
|
||||
final startDate = licenseMap['startDate'] ?? '';
|
||||
final expirationDate = licenseMap['expirationDate'] ?? '';
|
||||
final isStarted =
|
||||
await menuDataSource.processStartDate(startDate, entry);
|
||||
final isStarted = await DateTimeUtil.processStartDate(startDate);
|
||||
final isExpired =
|
||||
await menuDataSource.processExpirationDate(expirationDate, entry);
|
||||
await DateTimeUtil.processExpirationDate(expirationDate);
|
||||
if (isStarted && !isExpired) {
|
||||
await menuDataSource.handleMenu(menuItem, display, entry, entries);
|
||||
}
|
||||
|
|
|
@ -76,6 +76,16 @@ class MenuEntry implements BaseModule {
|
|||
route: '/residentsOnThePropertyPage',
|
||||
types: [MenuEntryType.Property],
|
||||
),
|
||||
// MenuEntry(
|
||||
// key: 'FRE-HUB-VEHICLES-MANAGER',
|
||||
// icon: Icons.directions_car,
|
||||
// name: FFLocalizations.of(navigatorKey.currentContext!).getVariableText(
|
||||
// ptText: 'Veículos',
|
||||
// enText: 'Vehicles',
|
||||
// ),
|
||||
// route: '/vehiclesOnThePropertyPage',
|
||||
// types: [MenuEntryType.Property],
|
||||
// ),
|
||||
MenuEntry(
|
||||
key: 'FRE-HUB-VEHICLES',
|
||||
icon: Icons.directions_car,
|
||||
|
|
|
@ -94,7 +94,7 @@ class LicenseLocalDataSourceImpl implements LicenseLocalDataSource {
|
|||
Future<bool> isNewVersion() async {
|
||||
final String? reponse =
|
||||
await StorageHelper().get(LocalsStorageKey.isNewVersion.key);
|
||||
final bool isNewVersion = reponse.toBoolean();
|
||||
final bool isNewVersion = reponse.toBoolean;
|
||||
return isNewVersion;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ enum LicenseKeys {
|
|||
access('FRE-HUB-ACCESS'),
|
||||
openedVisits('FRE-HUB-OPENED-VISITS'),
|
||||
vehicles('FRE-HUB-VEHICLES'),
|
||||
vehiclesManager('FRE-HUB-VEHICLES-MANAGER'),
|
||||
residents('FRE-HUB-RESIDENTS'),
|
||||
about('FRE-HUB-ABOUT-SYSTEM'),
|
||||
pets('FRE-HUB-PETS'),
|
||||
|
@ -63,7 +64,7 @@ class License {
|
|||
static Future<String> _precessWpp() async {
|
||||
final bool whatsapp = await StorageHelper()
|
||||
.get(LocalsStorageKey.whatsapp.key)
|
||||
.then((v) => v.toBoolean());
|
||||
.then((v) => v.toBoolean);
|
||||
if (whatsapp)
|
||||
return ModuleStatus.active.key;
|
||||
else
|
||||
|
@ -73,7 +74,7 @@ class License {
|
|||
static Future<String> _processProvisional() async {
|
||||
final bool provisional = await StorageHelper()
|
||||
.get(LocalsStorageKey.provisional.key)
|
||||
.then((v) => v.toBoolean());
|
||||
.then((v) => v.toBoolean);
|
||||
if (provisional)
|
||||
return ModuleStatus.active.key;
|
||||
else
|
||||
|
@ -83,7 +84,7 @@ class License {
|
|||
static Future<String> _processPets() async {
|
||||
final bool pets = await StorageHelper()
|
||||
.get(LocalsStorageKey.pets.key)
|
||||
.then((v) => v.toBoolean());
|
||||
.then((v) => v.toBoolean);
|
||||
if (pets)
|
||||
return ModuleStatus.active.key;
|
||||
else
|
||||
|
@ -163,6 +164,13 @@ class License {
|
|||
startDate: '',
|
||||
quantity: 0,
|
||||
),
|
||||
Module(
|
||||
key: LicenseKeys.vehicles.value,
|
||||
display: ModuleStatus.inactive.key,
|
||||
expirationDate: '',
|
||||
startDate: '',
|
||||
quantity: 0,
|
||||
),
|
||||
Module(
|
||||
key: LicenseKeys.residents.value,
|
||||
display: isNewVersionWithModule
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
import 'package:app_links/app_links.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hub/features/storage/index.dart';
|
||||
|
|
|
@ -34,7 +34,9 @@ enum LocalsStorageKey implements DatabaseStorageKey {
|
|||
panic('fre_panic'),
|
||||
person('fre_person'),
|
||||
requestOSNotification('fre_requestOSnotification'),
|
||||
isNewVersion('fre_isNewVersion');
|
||||
isNewVersion('fre_isNewVersion'),
|
||||
vehicleAutoApproval('fre_vehicleAutoApproval'),
|
||||
vehicleAmountRegister('fre_vehicleAmountRegister');
|
||||
|
||||
final String key;
|
||||
|
||||
|
|
|
@ -529,7 +529,7 @@ void setAppLanguage(BuildContext context, String language) =>
|
|||
void setDarkModeSetting(BuildContext context, ThemeMode themeMode) =>
|
||||
App.of(context).setThemeMode(themeMode);
|
||||
|
||||
void showSnackbar(
|
||||
void showSnackbarMessenger(
|
||||
BuildContext context,
|
||||
String message,
|
||||
bool error, {
|
||||
|
@ -537,38 +537,47 @@ void showSnackbar(
|
|||
int duration = 4,
|
||||
}) {
|
||||
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Row(
|
||||
children: [
|
||||
if (loading)
|
||||
Padding(
|
||||
padding: const EdgeInsetsDirectional.only(end: 10.0),
|
||||
child: SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(
|
||||
color: FlutterFlowTheme.of(context).info,
|
||||
),
|
||||
ScaffoldMessenger.of(context)
|
||||
.showSnackBar(showSnackbar(context, message, error));
|
||||
}
|
||||
|
||||
SnackBar showSnackbar(
|
||||
BuildContext context,
|
||||
String message,
|
||||
bool error, {
|
||||
bool loading = false,
|
||||
int duration = 4,
|
||||
}) {
|
||||
return SnackBar(
|
||||
content: Row(
|
||||
children: [
|
||||
if (loading)
|
||||
Padding(
|
||||
padding: const EdgeInsetsDirectional.only(end: 10.0),
|
||||
child: SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(
|
||||
color: FlutterFlowTheme.of(context).info,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
message,
|
||||
style: TextStyle(
|
||||
color: FlutterFlowTheme.of(context).info,
|
||||
fontSize: LimitedFontSizeUtil.getBodyFontSize(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
duration: Duration(seconds: duration),
|
||||
backgroundColor: error
|
||||
? FlutterFlowTheme.of(context).error
|
||||
: FlutterFlowTheme.of(context).success,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
Text(
|
||||
message,
|
||||
style: TextStyle(
|
||||
color: FlutterFlowTheme.of(context).info,
|
||||
fontSize: LimitedFontSizeUtil.getBodyFontSize(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
duration: Duration(seconds: duration),
|
||||
backgroundColor: error
|
||||
? FlutterFlowTheme.of(context).error
|
||||
: FlutterFlowTheme.of(context).success,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -198,7 +198,7 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) {
|
|||
FFRoute(
|
||||
name: 'vehiclesOnThePropertyPage',
|
||||
path: '/vehiclesOnThePropertyPage',
|
||||
builder: (context, params) => const VehicleOnTheProperty()),
|
||||
builder: (context, params) => const VehiclePage()),
|
||||
FFRoute(
|
||||
name: 'receptionPage',
|
||||
path: '/receptionPage',
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:app_tracking_transparency/app_tracking_transparency.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||
|
@ -29,7 +27,8 @@ Future<void> initializeApp() async {
|
|||
Future<void> _initializeTracking() async {
|
||||
log('Requesting tracking authorization...');
|
||||
await AppTrackingTransparency.requestTrackingAuthorization();
|
||||
log('Tracking authorization requested');
|
||||
|
||||
print('Tracking authorization requested');
|
||||
}
|
||||
|
||||
Future<void> _initializeFirebase() async {
|
||||
|
@ -51,12 +50,14 @@ void _initializeUrlStrategy() {
|
|||
}
|
||||
|
||||
Future<void> _initializeSystemSettings() async {
|
||||
log('Initializing System Settings...');
|
||||
final crashlyticsInstance = FirebaseCrashlytics.instance;
|
||||
print('Initializing System Settings...');
|
||||
|
||||
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
|
||||
final crashlyticsInstance = FirebaseCrashlytics.instance;
|
||||
|
||||
if (kDebugMode) {
|
||||
log('Debug mode');
|
||||
print('Debug mode');
|
||||
await crashlyticsInstance.setCrashlyticsCollectionEnabled(false);
|
||||
} else {
|
||||
log('Release mode');
|
||||
|
||||
|
@ -72,10 +73,22 @@ Future<void> _initializeSystemSettings() async {
|
|||
// }
|
||||
|
||||
await crashlyticsInstance.setCrashlyticsCollectionEnabled(true);
|
||||
// if (crashlyticsInstance.isCrashlyticsCollectionEnabled) {
|
||||
FlutterError.onError = crashlyticsInstance.recordFlutterError;
|
||||
log('Crashlytics enabled');
|
||||
// }
|
||||
if (crashlyticsInstance.isCrashlyticsCollectionEnabled) {
|
||||
// Configura o tratamento de erros não capturados
|
||||
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) {
|
||||
if (message != null || message != '') {
|
||||
showSnackbar(
|
||||
showSnackbarMessenger(
|
||||
context,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
enText: 'Successfully resolved visit',
|
||||
|
@ -308,7 +308,7 @@ class _LiberationHistoryWidgetState extends State<LiberationHistoryWidget> {
|
|||
false,
|
||||
);
|
||||
} else {
|
||||
showSnackbar(context, message, true);
|
||||
showSnackbarMessenger(context, message, true);
|
||||
}
|
||||
}).whenComplete(() {
|
||||
safeSetState(() {
|
||||
|
|
|
@ -478,7 +478,7 @@ class PetsPageModel extends FlutterFlowModel<PetsPageWidget> {
|
|||
context.pop(value);
|
||||
|
||||
if (value == false) {
|
||||
showSnackbar(
|
||||
showSnackbarMessenger(
|
||||
context,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Erro ao excluir pet',
|
||||
|
@ -487,7 +487,7 @@ class PetsPageModel extends FlutterFlowModel<PetsPageWidget> {
|
|||
true,
|
||||
);
|
||||
} else if (value == true) {
|
||||
showSnackbar(
|
||||
showSnackbarMessenger(
|
||||
context,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
enText: 'Success deleting pet',
|
||||
|
@ -498,7 +498,7 @@ class PetsPageModel extends FlutterFlowModel<PetsPageWidget> {
|
|||
}
|
||||
}).catchError((err, stack) {
|
||||
context.pop();
|
||||
showSnackbar(
|
||||
showSnackbarMessenger(
|
||||
context,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
enText: 'Error deleting pet',
|
||||
|
|
|
@ -86,9 +86,9 @@ class _PetsPageWidgetState extends State<PetsPageWidget>
|
|||
);
|
||||
}
|
||||
|
||||
void onEditingChanged(bool value) {
|
||||
void onEditingChanged([bool? value]) {
|
||||
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:google_fonts/google_fonts.dart';
|
||||
|
||||
|
|
|
@ -472,7 +472,7 @@ class ScheduleCompleteVisitPageModel
|
|||
context.pop(value);
|
||||
|
||||
if (value == false) {
|
||||
showSnackbar(
|
||||
showSnackbarMessenger(
|
||||
context,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
enText: 'Error blocking visit',
|
||||
|
@ -481,7 +481,7 @@ class ScheduleCompleteVisitPageModel
|
|||
true,
|
||||
);
|
||||
} else if (value == true) {
|
||||
showSnackbar(
|
||||
showSnackbarMessenger(
|
||||
context,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
enText: 'Success canceling visit',
|
||||
|
@ -492,7 +492,7 @@ class ScheduleCompleteVisitPageModel
|
|||
}
|
||||
}).catchError((err, stack) {
|
||||
context.pop();
|
||||
showSnackbar(
|
||||
showSnackbarMessenger(
|
||||
context,
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
enText: 'Error blocking visit',
|
||||
|
|
|
@ -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 'package:hub/components/templates_components/details_component/details_component_widget.dart';
|
||||
import 'package:hub/flutter_flow/flutter_flow_model.dart';
|
||||
import 'package:hub/flutter_flow/internationalization.dart';
|
||||
import 'package:hub/pages/vehicles_on_the_property/vehicles_on_the_property.dart';
|
||||
import 'dart:developer';
|
||||
|
||||
class VehicleModel extends FlutterFlowModel<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();
|
||||
VehicleModel._internal();
|
||||
factory VehicleModel() => _instance ?? VehicleModel._internal();
|
||||
static void resetInstance() => _instance = null;
|
||||
|
||||
dynamic item;
|
||||
final GlobalKey<FormState> registerFormKey = GlobalKey<FormState>();
|
||||
final GlobalKey<FormState> updateFormKey = GlobalKey<FormState>();
|
||||
|
||||
@override
|
||||
void initState(BuildContext context) {
|
||||
log('VehicleModel -> initState');
|
||||
resetInstance();
|
||||
|
||||
initAsync();
|
||||
initializeControllers(context);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
amountRegister = '0';
|
||||
});
|
||||
}
|
||||
|
||||
void initializeControllers(BuildContext context) {
|
||||
tabBarController = TabController(
|
||||
vsync: Navigator.of(context),
|
||||
length: 2,
|
||||
);
|
||||
|
||||
textFieldFocusLicensePlate = FocusNode();
|
||||
textFieldControllerLicensePlate = TextEditingController();
|
||||
|
||||
textFieldFocusColor = FocusNode();
|
||||
textFieldControllerColor = TextEditingController();
|
||||
|
||||
textFieldFocusModel = FocusNode();
|
||||
textFieldControllerModel = TextEditingController();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {}
|
||||
void dispose() {
|
||||
disposeControllers();
|
||||
}
|
||||
|
||||
Future<void> initAsync() async {}
|
||||
void disposeControllers() {
|
||||
tabBarController.dispose();
|
||||
textFieldFocusLicensePlate!.dispose();
|
||||
textFieldControllerLicensePlate!.dispose();
|
||||
textFieldFocusColor!.dispose();
|
||||
textFieldControllerColor!.dispose();
|
||||
textFieldFocusModel!.dispose();
|
||||
textFieldControllerModel!.dispose();
|
||||
}
|
||||
|
||||
Widget buildVehicleDetails({
|
||||
required dynamic item,
|
||||
required BuildContext context,
|
||||
required VehicleModel model,
|
||||
}) {
|
||||
return DetailsComponentWidget(
|
||||
buttons: [],
|
||||
labelsHashMap: Map<String, String>.from({
|
||||
if (item['model'] != null && item['model'] != '')
|
||||
'${FFLocalizations.of(context).getVariableText(ptText: "Modelo", enText: "Model")}:':
|
||||
item['model'].toString().toUpperCase(),
|
||||
if (item['licensePlate'] != null && item['licensePlate'] != '')
|
||||
'${FFLocalizations.of(context).getVariableText(ptText: "Placa", enText: "License Plate")}:':
|
||||
item['licensePlate'].toString().toUpperCase(),
|
||||
if (item['color'] != null && item['color'] != '')
|
||||
'${FFLocalizations.of(context).getVariableText(ptText: "Cor", enText: "Color")}:':
|
||||
item['color'].toString().toUpperCase(),
|
||||
if (item['personName'] != null && item['personName'] != '')
|
||||
'${FFLocalizations.of(context).getVariableText(ptText: "Proprietário", enText: "Owner")}:':
|
||||
item['personName'].toString().toUpperCase(),
|
||||
if (item['tag'] != null && item['tag'] != '')
|
||||
'${FFLocalizations.of(context).getVariableText(ptText: "Tag", enText: "Tag")}:':
|
||||
item['tag'].toString().toUpperCase(),
|
||||
}),
|
||||
statusHashMap: [],
|
||||
Future<void> initAsync() async {
|
||||
amountRegister =
|
||||
await StorageHelper().get(LocalsStorageKey.vehicleAmountRegister.key);
|
||||
autoApproval =
|
||||
await StorageHelper().get(LocalsStorageKey.vehicleAutoApproval.key);
|
||||
}
|
||||
|
||||
bool isFormValid(BuildContext context, GlobalKey<FormState> formKey) {
|
||||
if (formKey.currentState == null) return false;
|
||||
return formKey.currentState!.validate();
|
||||
}
|
||||
}
|
||||
|
||||
/// [_BaseVehiclePage] is a mixin that contains the base logic of the vehicle page.
|
||||
mixin class _BaseVehiclePage {
|
||||
int count = 0;
|
||||
late final TabController tabBarController;
|
||||
// dynamic item;
|
||||
late int vehicleId;
|
||||
BuildContext context = navigatorKey.currentContext!;
|
||||
bool isEditing = false;
|
||||
ApiCallResponse? vehicleResponse;
|
||||
String? amountRegister = '0';
|
||||
late final String? autoApproval;
|
||||
|
||||
VoidCallback? onUpdateVehicle;
|
||||
VoidCallback? onRegisterVehicle;
|
||||
VoidCallback? safeSetState;
|
||||
|
||||
FocusNode? textFieldFocusLicensePlate;
|
||||
TextEditingController? textFieldControllerLicensePlate;
|
||||
String? textControllerLicensePlateValidator(
|
||||
BuildContext context, String? value) {
|
||||
final validationMessage = validateField(
|
||||
context, value, 'Placa é obrigatória', 'License Plate is required');
|
||||
if (validationMessage != null) return validationMessage;
|
||||
|
||||
final brazilianPlateRegex = RegExp(r'^[A-Z]{3}\d{4}$');
|
||||
final mercosurPlateRegex = RegExp(r'^[A-Z]{3}\d[A-Z0-9]\d{2}$');
|
||||
|
||||
if (!brazilianPlateRegex.hasMatch(value!) &&
|
||||
!mercosurPlateRegex.hasMatch(value)) {
|
||||
return FFLocalizations.of(context).getVariableText(
|
||||
ptText: 'Placa inválida',
|
||||
enText: 'Invalid license plate',
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
FocusNode? textFieldFocusColor;
|
||||
TextEditingController? textFieldControllerColor;
|
||||
String? textControllerColorValidator(BuildContext contexnt, String? value) {
|
||||
return validateField(
|
||||
context, value, 'Cor é obrigatória', 'Color is required');
|
||||
}
|
||||
|
||||
FocusNode? textFieldFocusModel;
|
||||
TextEditingController? textFieldControllerModel;
|
||||
String? textControllerModelValidator(BuildContext contexnt, String? value) {
|
||||
return validateField(
|
||||
context, value, 'Modelo é obrigatório', 'Model is required');
|
||||
}
|
||||
|
||||
String? validateField(
|
||||
BuildContext context, String? value, String ptText, String enText) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return FFLocalizations.of(context).getVariableText(
|
||||
ptText: ptText,
|
||||
enText: enText,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<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/services.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:hub/components/atomic_components/shared_components_atoms/custom_input.dart';
|
||||
import 'package:hub/components/atomic_components/shared_components_atoms/submit_button.dart';
|
||||
import 'package:hub/components/atomic_components/shared_components_atoms/tabview.dart';
|
||||
import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart';
|
||||
import 'package:hub/features/backend/index.dart';
|
||||
import 'package:hub/flutter_flow/flutter_flow_icon_button.dart';
|
||||
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
||||
import 'package:hub/flutter_flow/flutter_flow_util.dart';
|
||||
import 'package:hub/features/module/index.dart';
|
||||
import 'package:hub/flutter_flow/index.dart';
|
||||
import 'package:hub/pages/vehicles_on_the_property/vehicle_model.dart';
|
||||
import 'package:hub/shared/extensions/index.dart';
|
||||
import 'package:hub/shared/mixins/pageable_mixin.dart';
|
||||
import 'package:hub/shared/utils/dialog_util.dart';
|
||||
import 'package:hub/shared/utils/license_util.dart';
|
||||
import 'package:hub/shared/utils/limited_text_size.dart';
|
||||
import 'package:hub/shared/utils/log_util.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
|
||||
class VehicleOnTheProperty extends StatefulWidget {
|
||||
const VehicleOnTheProperty({super.key});
|
||||
part 'vehicle_history_screen.dart';
|
||||
part 'vehicle_register_screen.dart';
|
||||
part 'vehicle_update_screen.dart';
|
||||
|
||||
/// [VehiclePage] is a StatefulWidget that displays the vehicle screens.
|
||||
class VehiclePage extends StatefulWidget {
|
||||
const VehiclePage({super.key});
|
||||
|
||||
@override
|
||||
_VehicleOnThePropertyState createState() => _VehicleOnThePropertyState();
|
||||
// ignore: library_private_types_in_public_api
|
||||
_VehiclePageState createState() => _VehiclePageState();
|
||||
}
|
||||
|
||||
class _VehicleOnThePropertyState extends State<VehicleOnTheProperty>
|
||||
class _VehiclePageState extends State<VehiclePage>
|
||||
with TickerProviderStateMixin {
|
||||
late ScrollController _scrollController;
|
||||
|
||||
int _pageNumber = 1;
|
||||
bool _hasData = false;
|
||||
bool _loading = false;
|
||||
int count = 0;
|
||||
late final VehicleModel model;
|
||||
|
||||
late Future<void> _future;
|
||||
List<dynamic> _wrap = [];
|
||||
late final VehicleModel _model;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
model = createModel(context, () => VehicleModel());
|
||||
_future = _fetchVisits();
|
||||
_scrollController = ScrollController()
|
||||
..addListener(() {
|
||||
if (_scrollController.position.atEdge &&
|
||||
_scrollController.position.pixels != 0) {
|
||||
_loadMore();
|
||||
}
|
||||
_model = createModel(context, () => VehicleModel());
|
||||
|
||||
_model.updateOnChange = true;
|
||||
_model.onUpdateVehicle = () {
|
||||
safeSetState(() {
|
||||
_model.clearFields();
|
||||
});
|
||||
};
|
||||
_model.onRegisterVehicle = () {
|
||||
safeSetState(() {
|
||||
_model.clearFields();
|
||||
});
|
||||
};
|
||||
_model.safeSetState = () {
|
||||
safeSetState(() {});
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
// @override
|
||||
// void dispose() {
|
||||
// super.dispose();
|
||||
// }
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
late final limitedHeaderTextSize =
|
||||
LimitedFontSizeUtil.getHeaderFontSize(context);
|
||||
|
||||
final backgroundColor = FlutterFlowTheme.of(context).primaryBackground;
|
||||
return Scaffold(
|
||||
backgroundColor: FlutterFlowTheme.of(context).primaryBackground,
|
||||
appBar: _appBar(context),
|
||||
body: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
if (_hasData == false && _pageNumber <= 1 && _loading == false)
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
FFLocalizations.of(context).getVariableText(
|
||||
ptText: "Nenhum veículo encontrado!",
|
||||
enText: "No vehicle found",
|
||||
),
|
||||
style: TextStyle(
|
||||
fontFamily: 'Nunito',
|
||||
fontSize: limitedHeaderTextSize,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
else if (_hasData == true || _pageNumber >= 1)
|
||||
Expanded(
|
||||
child: FutureBuilder<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)),
|
||||
),
|
||||
backgroundColor: backgroundColor,
|
||||
appBar: _buildHeader(context),
|
||||
body: _buildBody(context),
|
||||
);
|
||||
}
|
||||
|
||||
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(
|
||||
backgroundColor: FlutterFlowTheme.of(context).primaryBackground,
|
||||
backgroundColor: backgroundColor,
|
||||
automaticallyImplyLeading: false,
|
||||
title: Text(
|
||||
FFLocalizations.of(context)
|
||||
.getVariableText(enText: 'Vehicles', ptText: 'Veículos'),
|
||||
style: FlutterFlowTheme.of(context).headlineMedium.override(
|
||||
fontFamily: FlutterFlowTheme.of(context).headlineMediumFamily,
|
||||
color: FlutterFlowTheme.of(context).primaryText,
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
letterSpacing: 0.0,
|
||||
useGoogleFonts: GoogleFonts.asMap().containsKey(
|
||||
FlutterFlowTheme.of(context).headlineMediumFamily),
|
||||
),
|
||||
),
|
||||
leading: _backButton(context, FlutterFlowTheme.of(context)),
|
||||
title: Text(title, style: titleStyle),
|
||||
leading: backButton,
|
||||
centerTitle: true,
|
||||
elevation: 0.0,
|
||||
actions: [],
|
||||
|
@ -151,131 +186,22 @@ class _VehicleOnThePropertyState extends State<VehicleOnTheProperty>
|
|||
}
|
||||
|
||||
Widget _backButton(BuildContext context, FlutterFlowTheme theme) {
|
||||
final Icon icon = Icon(
|
||||
Icons.keyboard_arrow_left,
|
||||
color: theme.primaryText,
|
||||
size: 30.0,
|
||||
);
|
||||
onPressed() => Navigator.of(context).pop();
|
||||
return FlutterFlowIconButton(
|
||||
key: ValueKey<String>('BackNavigationAppBar'),
|
||||
borderColor: Colors.transparent,
|
||||
borderRadius: 30.0,
|
||||
borderWidth: 1.0,
|
||||
buttonSize: 60.0,
|
||||
icon: Icon(
|
||||
Icons.keyboard_arrow_left,
|
||||
color: theme.primaryText,
|
||||
size: 30.0,
|
||||
),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
icon: icon,
|
||||
onPressed: onPressed,
|
||||
);
|
||||
}
|
||||
|
||||
Future<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/pages/vehicles_on_the_property/vehicles_on_the_property.dart';
|
||||
|
||||
class VisitsModel extends FlutterFlowModel<VehicleOnTheProperty> {
|
||||
class VisitsModel extends FlutterFlowModel<VehiclePage> {
|
||||
static VisitsModel? _instance;
|
||||
|
||||
VisitsModel._internal({this.onRefresh});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'dart:ui';
|
||||
|
||||
extension StringNullableExtensions on String? {
|
||||
bool toBoolean() {
|
||||
bool get toBoolean {
|
||||
if (this == null) return false;
|
||||
return this!.toLowerCase() == 'true';
|
||||
}
|
||||
|
@ -11,10 +11,16 @@ extension StringNullableExtensions on String? {
|
|||
if (this == '') return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool get isNotNullAndEmpty {
|
||||
if (this == null) return false;
|
||||
if (this == '') return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension StringExtensions on String {
|
||||
bool toBoolean() {
|
||||
bool get toBoolean {
|
||||
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)
|
||||
publish_to: "none" # Destino de publicação
|
||||
|
||||
# Versão do aplicativo
|
||||
version: 1.3.5+24
|
||||
publish_to: "none"
|
||||
|
||||
version: 1.4.0+27
|
||||
|
||||
# Restrições de versão do SDK Dart
|
||||
environment:
|
||||
|
@ -113,7 +114,9 @@ dependencies:
|
|||
url_launcher_platform_interface: 2.3.2
|
||||
permission_handler: ^11.3.1
|
||||
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
|
||||
package_info_plus: ^8.1.1
|
||||
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