flutter-freaccess-hub/lib/pages/vehicles_on_the_property/vehicle_model.dart

603 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 = {
"APR_CREATE": {
"text": byLanguage('Awaiting', 'Aguardando'),
"color": theme.warning,
},
"APR_DELETE": {
"text": byLanguage('Awaiting', 'Aguardando'),
"color": theme.warning,
},
"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.warning,
},
"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 {
if (preFixStatusMap.containsKey(status))
preFixStatusMap[status]!['text'] as String: preFixStatusMap[status]!['color'] as Color,
vehicleStatusMap[status]!['text'] as String: vehicleStatusMap[status]!['color'] as Color,
// if (ownerStatusMap.containsKey(isOwner) && (status != 'ATI' || status != 'INA'))
// ownerStatusMap[isOwner]!['text'] as String: ownerStatusMap[isOwner]!['color'] as Color
};
if(status == 'ATI' || status == 'INA') return {
vehicleStatusMap[status]!['text'] as String: vehicleStatusMap[status]!['color'] as Color,
};
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],
);
}
}