608 lines
19 KiB
Dart
608 lines
19 KiB
Dart
import 'dart:developer';
|
|
|
|
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;
|
|
|
|
@override
|
|
void initState(BuildContext context) {
|
|
resetInstance();
|
|
initAsync();
|
|
tabBarController = TabController(
|
|
vsync: Navigator.of(context),
|
|
length: 2,
|
|
);
|
|
|
|
textFieldFocusLicensePlate = FocusNode();
|
|
textFieldControllerLicensePlate = TextEditingController();
|
|
|
|
textFieldFocusColor = FocusNode();
|
|
textFieldControllerColor = TextEditingController();
|
|
|
|
textFieldFocusModel = FocusNode();
|
|
textFieldControllerModel = TextEditingController();
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
|
amountRegister = '0';
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
tabBarController.dispose();
|
|
textFieldFocusLicensePlate!.dispose();
|
|
textFieldControllerLicensePlate!.dispose();
|
|
textFieldFocusColor!.dispose();
|
|
textFieldControllerColor!.dispose();
|
|
textFieldFocusModel!.dispose();
|
|
textFieldControllerModel!.dispose();
|
|
}
|
|
|
|
final GlobalKey<FormState> registerFormKey = GlobalKey<FormState>();
|
|
final GlobalKey<FormState> updateFormKey = GlobalKey<FormState>();
|
|
|
|
Future<void> initAsync() async {
|
|
|
|
amountRegister = await StorageHelper().get(LocalsStorageKey.vehicleAmountRegister.key);
|
|
}
|
|
|
|
bool isFormValid(BuildContext context) {
|
|
if (registerFormKey.currentState == null) return false;
|
|
return registerFormKey.currentState!.validate();
|
|
}
|
|
}
|
|
|
|
/// [_BaseVehiclePage] is a mixin that contains the base logic of the vehicle page.
|
|
mixin class _BaseVehiclePage {
|
|
int count = 0;
|
|
late final VehicleModel model;
|
|
late final TabController tabBarController;
|
|
// dynamic item;
|
|
BuildContext context = navigatorKey.currentContext!;
|
|
bool isEditing = false;
|
|
String? vehicleId;
|
|
ApiCallResponse? vehicleResponse;
|
|
String? amountRegister = '0';
|
|
|
|
VoidCallback? onUpdateVehicle;
|
|
VoidCallback? onRegisterVehicle;
|
|
VoidCallback? safeSetState;
|
|
|
|
FocusNode? textFieldFocusLicensePlate;
|
|
TextEditingController? textFieldControllerLicensePlate;
|
|
String? textControllerLicensePlateValidator(
|
|
BuildContext context, String? value) {
|
|
if (value == null || value.isEmpty) {
|
|
return FFLocalizations.of(context).getVariableText(
|
|
ptText: 'Placa é obrigatória',
|
|
enText: 'License Plate is required',
|
|
);
|
|
}
|
|
|
|
// (ABC-1234)
|
|
final brazilianPlateRegex = RegExp(r'^[A-Z]{3}\d{4}$');
|
|
// (ABC1D23)
|
|
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) {
|
|
if (value == null || value.isEmpty) {
|
|
return FFLocalizations.of(context).getVariableText(
|
|
ptText: 'Cor é obrigatória',
|
|
enText: 'Color is required',
|
|
);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
FocusNode? textFieldFocusModel;
|
|
TextEditingController? textFieldControllerModel;
|
|
String? textControllerModelValidator(BuildContext contexnt, String? value) {
|
|
if (value == null || value.isEmpty) {
|
|
return FFLocalizations.of(context).getVariableText(
|
|
ptText: 'Modelo é obrigatório',
|
|
enText: 'Model is required',
|
|
);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
void switchTab(int index) {
|
|
tabBarController.animateTo(index);
|
|
if (index == 0) handleEditingChanged(false);
|
|
safeSetState?.call();
|
|
}
|
|
|
|
void clearFields() async {
|
|
textFieldControllerLicensePlate = TextEditingController(text: '');
|
|
textFieldControllerColor = TextEditingController(text: '');
|
|
textFieldControllerModel = TextEditingController(text: '');
|
|
}
|
|
|
|
void handleEditingChanged(bool editing) {
|
|
isEditing = editing;
|
|
clearFields();
|
|
}
|
|
|
|
void setEditForm(dynamic item) {
|
|
if (item != null) {
|
|
vehicleId = item['vehicleId'];
|
|
log("setEditForm ");
|
|
log("setEditForm -> ${item.toString()}");
|
|
|
|
textFieldControllerLicensePlate = TextEditingController(
|
|
text: item != null ? item['licensePlate'] ?? '' : '');
|
|
textFieldControllerColor =
|
|
TextEditingController(text: item != null ? item['color'] ?? '' : '');
|
|
textFieldControllerModel =
|
|
TextEditingController(text: item != null ? item['model'] ?? '' : '');
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// [_VehicleRegisterScreenModel] is a mixin that contains the business logic of the vehicle register page.
|
|
mixin _VehicleRegisterScreenModel on _BaseVehiclePage {
|
|
Future<void> registerVehicle() async {
|
|
final response = await PhpGroup.registerVehicle.call(
|
|
licensePlate: textFieldControllerLicensePlate!.text,
|
|
color: textFieldControllerColor!.text,
|
|
model: textFieldControllerModel!.text,
|
|
);
|
|
if (response.jsonBody['error'] == false) {
|
|
await DialogUtil.success(
|
|
context,
|
|
FFLocalizations.of(context).getVariableText(
|
|
ptText: 'Veículo cadastrado com sucesso',
|
|
enText: 'Vehicle registered successfully',
|
|
)).then((_) async {
|
|
switchTab(0);
|
|
});
|
|
} else {
|
|
String errorMessage;
|
|
try {
|
|
errorMessage = response.jsonBody['message'];
|
|
} catch (e) {
|
|
errorMessage = FFLocalizations.of(context).getVariableText(
|
|
ptText: 'Erro ao cadastrar veículo',
|
|
enText: 'Error registering vehicle',
|
|
);
|
|
}
|
|
await DialogUtil.error(context, errorMessage);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// [_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!,
|
|
);
|
|
if (response.jsonBody['error'] == false) {
|
|
await DialogUtil.success(
|
|
context,
|
|
FFLocalizations.of(context).getVariableText(
|
|
ptText: 'Veículo atualizado com sucesso',
|
|
enText: 'Vehicle updated successfully',
|
|
)).then((_) async {
|
|
switchTab(0);
|
|
});
|
|
} else {
|
|
String errorMessage;
|
|
try {
|
|
errorMessage = response.jsonBody['message'];
|
|
} catch (e) {
|
|
errorMessage = FFLocalizations.of(context).getVariableText(
|
|
ptText: 'Erro ao atualizar veículo',
|
|
enText: 'Error updating vehicle',
|
|
);
|
|
}
|
|
await DialogUtil.error(context, errorMessage);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// [_VehicleHistoryScreenModel] is a mixin that contains the business logic of the vehicle history page.
|
|
mixin _VehicleHistoryScreenModel on _BaseVehiclePage {
|
|
|
|
Future<Map<String, Color>?> generateStatusColorMap(dynamic uItem, int count) async {
|
|
final autoApproval = await StorageHelper().get(LocalsStorageKey.vehicleAutoApproval.key);
|
|
if(autoApproval.toBoolean == true) return null;
|
|
|
|
final theme = FlutterFlowTheme.of(context);
|
|
final localization = FFLocalizations.of(context);
|
|
|
|
|
|
byLanguage(en, pt) => localization.getVariableText(enText: en, ptText: pt);
|
|
|
|
|
|
final preFixStatusMap = {
|
|
"ATI": {
|
|
"text": '',
|
|
"color": theme.success,
|
|
},
|
|
"INA": {
|
|
"text": '',
|
|
"color": theme.accent2,
|
|
},
|
|
|
|
"APR_CREATE": {
|
|
"text": byLanguage('Awaiting', 'Aguardando'),
|
|
"color": theme.success,
|
|
},
|
|
"APR_DELETE": {
|
|
"text": byLanguage('Awaiting', 'Aguardando'),
|
|
"color": theme.error,
|
|
},
|
|
"APR_UPDATE": {
|
|
"text": byLanguage('Awaiting', 'Aguardando'),
|
|
"color": theme.warning,
|
|
},
|
|
"AGU_CHANGE": {
|
|
"text": byLanguage('Awaiting', 'Aguardando'),
|
|
"color": theme.accent2,
|
|
},
|
|
};
|
|
final vehicleStatusMap = {
|
|
"ATI": {
|
|
"text": byLanguage('Active', 'Ativo'),
|
|
"color": theme.success,
|
|
},
|
|
"INA": {
|
|
"text": byLanguage('Inactive', 'Inativo'),
|
|
"color": theme.accent2,
|
|
},
|
|
|
|
"APR_CREATE": {
|
|
"text": byLanguage('Creation', 'Criação'),
|
|
"color": theme.success,
|
|
},
|
|
"APR_DELETE": {
|
|
"text": byLanguage('Deletion', 'Exclusão'),
|
|
"color": theme.error,
|
|
},
|
|
"APR_UPDATE": {
|
|
"text": byLanguage('Update', 'Atualização'),
|
|
"color": theme.warning,
|
|
},
|
|
"AGU_CHANGE": {
|
|
"text": byLanguage('Correction', 'Correção'),
|
|
"color": theme.accent2,
|
|
},
|
|
};
|
|
final ownerStatusMap = {
|
|
true: {
|
|
"text": byLanguage('My Vehicle', 'Meu Veículo'),
|
|
"color": theme.primaryText,
|
|
},
|
|
false: {
|
|
"text": byLanguage('', ''),
|
|
"color": theme.accent2,
|
|
},
|
|
};
|
|
|
|
final status = uItem['status'];
|
|
final isOwner = uItem['isOwnerVehicle'];
|
|
|
|
if (vehicleStatusMap.containsKey(status)) {
|
|
if(count > 1) return {
|
|
preFixStatusMap[status]!['text'] as String: preFixStatusMap[status]!['color'] as Color,
|
|
vehicleStatusMap[status]!['text'] as String: vehicleStatusMap[status]!['color'] as Color,
|
|
// if (ownerStatusMap.containsKey(isOwner))
|
|
// ownerStatusMap[isOwner]!['text'] as String: ownerStatusMap[isOwner]!['color'] as Color
|
|
};
|
|
else return {
|
|
"${preFixStatusMap[status]!['text']} ${vehicleStatusMap[status]!['text']}": vehicleStatusMap[status]!['color'] as Color
|
|
};
|
|
}
|
|
return {};
|
|
}
|
|
|
|
Future<List<FFButtonWidget>?> generateActionButtons(dynamic item) async {
|
|
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,
|
|
),
|
|
);
|
|
|
|
final updateText = FFLocalizations.of(context)
|
|
.getVariableText(ptText: 'Editar', enText: 'Edit');
|
|
final updateIcon = Icon(Icons.edit, color: iconButtonColor);
|
|
Future updateOnPressed() async {
|
|
context.pop();
|
|
isEditing = true;
|
|
|
|
switchTab(1);
|
|
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 => await 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<void> processDeleteRequest(dynamic item) async {
|
|
log('processDeleteRequest -> item[$item]');
|
|
return await PhpGroup.deleteVehicle.call(
|
|
vehicleId: item['vehicleId'],
|
|
licensePlate: item['licensePlate'],
|
|
model: item['model'],
|
|
color: item['color'],
|
|
)
|
|
.then((value) {
|
|
|
|
|
|
context.pop(value);
|
|
context.pop(value);
|
|
|
|
// ignore: unrelated_type_equality_checks
|
|
if (value.jsonBody['error'] == true) {
|
|
final String errorMsg = value.jsonBody['error_msg'];
|
|
return showSnackbar(
|
|
context,
|
|
FFLocalizations.of(context).getVariableText(
|
|
ptText: errorMsg,
|
|
enText: 'Error deleting vehicle',
|
|
),
|
|
true,
|
|
);
|
|
// ignore: unrelated_type_equality_checks
|
|
}
|
|
return showSnackbar(
|
|
context,
|
|
FFLocalizations.of(context).getVariableText(
|
|
enText: 'Success deleting vehicle',
|
|
ptText: 'Succeso ao excluir veículo',
|
|
),
|
|
false,
|
|
);
|
|
|
|
}).catchError((err, stack) {
|
|
context.pop();
|
|
return showSnackbar(
|
|
context,
|
|
FFLocalizations.of(context).getVariableText(
|
|
enText: 'Error deleting vehicle',
|
|
ptText: 'Erro ao excluir veículo',
|
|
),
|
|
true,
|
|
);
|
|
});
|
|
}
|
|
|
|
Future<void> processCancelRequest(String status, dynamic item) async {
|
|
late final ApiCallResponse value;
|
|
try {
|
|
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:
|
|
break;
|
|
}
|
|
|
|
context.pop(value);
|
|
context.pop(value);
|
|
|
|
if (value.jsonBody['error'] == true) {
|
|
final String errorMsg = value.jsonBody['error_msg'] ;
|
|
return showSnackbar(
|
|
context,
|
|
FFLocalizations.of(context).getVariableText(
|
|
ptText: errorMsg,
|
|
enText: 'Error canceling request',
|
|
),
|
|
true,
|
|
);
|
|
}
|
|
return showSnackbar(
|
|
context,
|
|
FFLocalizations.of(context).getVariableText(
|
|
enText: 'Success canceling request',
|
|
ptText: 'Succeso ao cancelar solicitação',
|
|
),
|
|
false,
|
|
);
|
|
|
|
} catch (err) {
|
|
context.pop();
|
|
return showSnackbar(
|
|
context,
|
|
FFLocalizations.of(context).getVariableText(
|
|
enText: 'Error canceling request',
|
|
ptText: 'Erro ao cancelar solicitação',
|
|
),
|
|
true,
|
|
);
|
|
}
|
|
}
|
|
|
|
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.deleteVehicle.call(
|
|
vehicleId: item['vehicleId'],
|
|
licensePlate: item['licensePlate'],
|
|
model: item['model'],
|
|
color: item['color'],
|
|
);
|
|
}
|
|
|
|
Future<Map<String, String>> generateLabelsHashMap(dynamic item) async {
|
|
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(),
|
|
};
|
|
}
|
|
|
|
Future<Widget> buildVehicleDetails({
|
|
required dynamic item,
|
|
required BuildContext context,
|
|
required VehicleModel model,
|
|
required FreCardIcon? icon,
|
|
}) async {
|
|
|
|
final status = await generateStatusColorMap(item, 1);
|
|
final buttons = await generateActionButtons(item);
|
|
final labels = await generateLabelsHashMap(item);
|
|
return DetailsComponentWidget(
|
|
icon: icon,
|
|
buttons: buttons,
|
|
labelsHashMap: labels,
|
|
statusHashMap: [status],
|
|
);
|
|
}
|
|
}
|