diff --git a/lib/backend/api_requests/api_calls.dart b/lib/backend/api_requests/api_calls.dart index 19540ca5..b79db3e0 100644 --- a/lib/backend/api_requests/api_calls.dart +++ b/lib/backend/api_requests/api_calls.dart @@ -61,13 +61,14 @@ class DeletePet { String? devUUID = '', String? userUUID = '', String? cliID = '', - String? petID = '', + String? atividade = 'excluirPet', + int? petID = 0, }) async { final baseUrl = PhpGroup.getBaseUrl(); return ApiManager.instance.makeApiCall( callName: 'deletePet', - apiUrl: '$baseUrl/deletePet.php', + apiUrl: '$baseUrl/processRequest.php', callType: ApiCallType.POST, headers: { 'Content-Type': 'application/x-www-form-urlencoded', @@ -76,7 +77,8 @@ class DeletePet { 'devUUID': devUUID, 'userUUID': userUUID, 'cliID': cliID, - 'id': petID, + 'atividade': atividade, + 'petId': petID, }, bodyType: BodyType.X_WWW_FORM_URL_ENCODED, returnBody: true, @@ -94,7 +96,8 @@ class UpdatePet { String? devUUID = '', String? userUUID = '', String? cliID = '', - String? petID = '', + String? atividade = 'atualizarPet', + int? petID = 0, String? image = '', String? name = '', String? species = '', @@ -109,7 +112,7 @@ class UpdatePet { return ApiManager.instance.makeApiCall( callName: 'updatePet', - apiUrl: '$baseUrl/updatePet.php', + apiUrl: '$baseUrl/processRequest.php', callType: ApiCallType.POST, headers: { 'Content-Type': 'application/x-www-form-urlencoded', @@ -118,13 +121,14 @@ class UpdatePet { 'devUUID': devUUID, 'userUUID': userUUID, 'cliID': cliID, + 'atividade': atividade, 'id': petID, 'image': image, 'name': name, 'species': species, 'breed': breed, 'color': color, - 'birthdayDate': birthdayDate, + 'birthdayDate': ValidatorUtil.toISO8601USA('dd/MM/yyyy', birthdayDate!), 'gender': gender, 'size': size, 'notes': notes, @@ -145,14 +149,15 @@ class GetPets { String? devUUID = '', String? userUUID = '', String? cliID = '', - String? page = '', - String? pageSize = '', + String? atividade = 'consultaPets', + int? page = 0, + int? pageSize = 0, }) async { final baseUrl = PhpGroup.getBaseUrl(); return ApiManager.instance.makeApiCall( callName: 'getPets', - apiUrl: '$baseUrl/getPets.php', + apiUrl: '$baseUrl/processRequest.php', callType: ApiCallType.POST, headers: { 'Content-Type': 'application/x-www-form-urlencoded', @@ -161,6 +166,7 @@ class GetPets { 'devUUID': devUUID, 'userUUID': userUUID, 'cliID': cliID, + 'atividade': atividade, 'page': page, 'pageSize': pageSize, }, @@ -179,13 +185,14 @@ class GetPetPhoto { String? devUUID = '', String? userUUID = '', String? cliID = '', - String? petID = '', + String? atividade = 'consultaFotoPet', + int? petId = 0, }) async { final baseUrl = PhpGroup.getBaseUrl(); return ApiManager.instance.makeApiCall( callName: 'getPetPhoto', - apiUrl: '$baseUrl/getPetPhoto.php', + apiUrl: '$baseUrl/getImage.php', callType: ApiCallType.POST, headers: { 'Content-Type': 'application/x-www-form-urlencoded', @@ -193,15 +200,16 @@ class GetPetPhoto { params: { 'devUUID': devUUID, 'userUUID': userUUID, + 'atividade': atividade, 'cliID': cliID, - 'petId': petID, + 'petId': petId, }, - bodyType: BodyType.X_WWW_FORM_URL_ENCODED, + bodyType: BodyType.BLOB, returnBody: true, encodeBodyUtf8: false, decodeUtf8: false, cache: false, - alwaysAllowBody: false, + isStreamingApi: false, ); } } @@ -242,7 +250,8 @@ class RegisterPet { 'breed': breed, if (color != '') 'color': color, if (birthdayDate != '') - 'birthdayDate': ValidatorUtil.toISO8601('dd/MM/yyyy', birthdayDate!), + 'birthdayDate': + ValidatorUtil.toISO8601USA('dd/MM/yyyy', birthdayDate!), 'gender': gender, 'size': size, if (notes != '') 'notes': notes, diff --git a/lib/backend/api_requests/api_manager.dart b/lib/backend/api_requests/api_manager.dart index e9574b6b..57c421d5 100644 --- a/lib/backend/api_requests/api_manager.dart +++ b/lib/backend/api_requests/api_manager.dart @@ -29,6 +29,7 @@ enum BodyType { TEXT, X_WWW_FORM_URL_ENCODED, MULTIPART, + BLOB, } class ApiCallOptions extends Equatable { @@ -133,13 +134,18 @@ class ApiCallResponse { http.Response response, bool returnBody, bool decodeUtf8, + BodyType? bodyType, ) { dynamic jsonBody; try { - final responseBody = decodeUtf8 && returnBody - ? const Utf8Decoder().convert(response.bodyBytes) - : response.body; - jsonBody = returnBody ? json.decode(responseBody) : null; + if (bodyType == BodyType.BLOB) { + jsonBody = response.bodyBytes; // Armazenar os bytes diretamente + } else { + final responseBody = decodeUtf8 && returnBody + ? const Utf8Decoder().convert(response.bodyBytes) + : response.body; + jsonBody = returnBody ? json.decode(responseBody) : null; + } } catch (_) {} return ApiCallResponse( jsonBody, @@ -194,6 +200,7 @@ class ApiManager { bool decodeUtf8, bool isStreamingApi, { http.Client? client, + BodyType? bodyType, // Adicionado para verificar o tipo de corpo }) async { if (params.isNotEmpty) { final specifier = @@ -218,7 +225,8 @@ class ApiManager { : (client != null ? client.delete : http.delete); final response = await makeRequest(Uri.parse(apiUrl), headers: toStringMap(headers)); - return ApiCallResponse.fromHttpResponse(response, returnBody, decodeUtf8); + return ApiCallResponse.fromHttpResponse( + response, returnBody, decodeUtf8, bodyType); // Passar bodyType } static Future requestWithBody( @@ -259,7 +267,7 @@ class ApiManager { if (bodyType == BodyType.MULTIPART) { return multipartRequest(type, apiUrl, headers, params, returnBody, - decodeUtf8, alwaysAllowBody); + decodeUtf8, alwaysAllowBody, bodyType); } final requestFn = { @@ -270,7 +278,8 @@ class ApiManager { }[type]!; final response = await requestFn(Uri.parse(apiUrl), headers: toStringMap(headers), body: postBody); - return ApiCallResponse.fromHttpResponse(response, returnBody, decodeUtf8); + return ApiCallResponse.fromHttpResponse( + response, returnBody, decodeUtf8, bodyType); } static Future multipartRequest( @@ -281,6 +290,7 @@ class ApiManager { bool returnBody, bool decodeUtf8, bool alwaysAllowBody, + BodyType? bodyType, ) async { assert( {ApiCallType.POST, ApiCallType.PUT, ApiCallType.PATCH}.contains(type) || @@ -321,7 +331,8 @@ class ApiManager { nonFileParams.forEach((key, value) => request.fields[key] = value); final response = await http.Response.fromStream(await request.send()); - return ApiCallResponse.fromHttpResponse(response, returnBody, decodeUtf8); + return ApiCallResponse.fromHttpResponse( + response, returnBody, decodeUtf8, bodyType); } static MediaType? _getMediaType(String? filename) { @@ -362,6 +373,10 @@ class ApiManager { contentType = 'multipart/form-data'; postBody = params; break; + case BodyType.BLOB: + contentType = 'application/octet-stream'; + postBody = body; + break; case BodyType.NONE: case null: break; diff --git a/lib/backend/api_requests/api_service.dart b/lib/backend/api_requests/api_service.dart new file mode 100644 index 00000000..f0d6d54e --- /dev/null +++ b/lib/backend/api_requests/api_service.dart @@ -0,0 +1,69 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; + +class ApiService { + static const _baseUrl = 'https://freaccess.com.br/freaccess'; + + static final Dio _dio = Dio(BaseOptions( + baseUrl: _baseUrl, + // headers: {'Authorization': 'Bearer $_apiKey'}, + )); + + static Future makeApiCall({ + required String endpoint, + required Map body, + bool isMultipart = false, + Uint8List? file, + Uint8List? blob, + }) async { + try { + Response response; + if (isMultipart && file != null) { + FormData formData = FormData.fromMap(body); + formData.files.add(MapEntry('file', MultipartFile.fromBytes(file))); + response = await _dio.post(endpoint, data: formData); + } else if (blob != null) { + // Convert the blob to base64 string + String base64Image = base64Encode(blob); + + // Set the request headers + Map headers = { + 'Content-Type': 'application/json', + }; + + // Create the request body as JSON + Map requestBody = { + 'image': base64Image, + }; + + response = await _dio.post( + endpoint, + data: jsonEncode(requestBody), + options: Options(headers: headers), + ); + } else { + // Set the request headers + Map headers = { + 'Content-Type': 'application/json', + }; + + response = await _dio.post( + endpoint, + data: jsonEncode(body), + options: Options(headers: headers), + ); + } + debugPrint('API call to $endpoint successful'); + debugPrint('Api call body: $body'); + debugPrint('Response: ${response.data}'); + debugPrint('Response headers: ${response.headers}'); + debugPrint('Response status: ${response.statusCode}'); + return response; + } catch (e) { + rethrow; + } + } +} diff --git a/lib/components/atomic_components/shared_components_atoms/media_upload_button.dart b/lib/components/atomic_components/shared_components_atoms/media_upload_button.dart index ff0277c8..5f92f128 100644 --- a/lib/components/atomic_components/shared_components_atoms/media_upload_button.dart +++ b/lib/components/atomic_components/shared_components_atoms/media_upload_button.dart @@ -5,7 +5,6 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:hub/flutter_flow/flutter_flow_theme.dart'; import 'package:hub/flutter_flow/flutter_flow_widgets.dart'; -import 'package:hub/flutter_flow/internationalization.dart'; import 'package:hub/flutter_flow/upload_data.dart'; import 'package:hub/flutter_flow/uploaded_file.dart'; @@ -13,12 +12,14 @@ class MediaUploadButtonUtil extends StatefulWidget { final Function(FFUploadedFile) onUploadComplete; bool isUploading; final String labelText; + FFUploadedFile? uploadedFiles; MediaUploadButtonUtil( {Key? key, required this.onUploadComplete, required this.isUploading, - required this.labelText}) + required this.labelText, + this.uploadedFiles}) : super(key: key); @override @@ -26,12 +27,9 @@ class MediaUploadButtonUtil extends StatefulWidget { } class _MediaUploadButtonUtilState extends State { - FFUploadedFile _uploadedFiles = FFUploadedFile(bytes: Uint8List.fromList([])); - @override void initState() { super.initState(); - _uploadedFiles = FFUploadedFile(bytes: Uint8List.fromList([])); } @override @@ -40,7 +38,7 @@ class _MediaUploadButtonUtilState extends State { padding: const EdgeInsetsDirectional.fromSTEB(24.0, 0.0, 24.0, 0.0), child: Builder( builder: (context) { - if ((_uploadedFiles.bytes?.isNotEmpty ?? false)) { + if ((widget.uploadedFiles != null)) { return InkWell( splashColor: Colors.transparent, focusColor: Colors.transparent, @@ -49,15 +47,15 @@ class _MediaUploadButtonUtilState extends State { onTap: () async { setState(() { widget.isUploading = false; - _uploadedFiles = + widget.uploadedFiles = FFUploadedFile(bytes: Uint8List.fromList([])); - widget.onUploadComplete(_uploadedFiles); + widget.onUploadComplete(widget.uploadedFiles!); }); }, child: ClipRRect( borderRadius: BorderRadius.circular(8.0), child: Image.memory( - _uploadedFiles.bytes ?? Uint8List.fromList([]), + widget.uploadedFiles!.bytes ?? Uint8List.fromList([]), width: 300.0, height: 200.0, fit: BoxFit.cover, @@ -105,9 +103,9 @@ class _MediaUploadButtonUtilState extends State { if (selectedUploadedFiles.length == selectedMedia.length) { setState(() { - _uploadedFiles = selectedUploadedFiles.first; + widget.uploadedFiles = selectedUploadedFiles.first; }); - widget.onUploadComplete(_uploadedFiles); + widget.onUploadComplete(widget.uploadedFiles!); showUploadMessage(context, 'Success!'); } else { diff --git a/lib/components/templates_components/details_component/details_component_action.dart b/lib/components/templates_components/details_component/details_component_action.dart index 447fc265..fb4d4aed 100644 --- a/lib/components/templates_components/details_component/details_component_action.dart +++ b/lib/components/templates_components/details_component/details_component_action.dart @@ -1,5 +1,8 @@ +import 'dart:developer'; + import 'package:flutter/material.dart'; import 'package:hub/actions/actions.dart'; +import 'package:hub/backend/api_requests/api_calls.dart'; import 'package:hub/components/templates_components/details_component/details_component_widget.dart'; import 'package:hub/flutter_flow/flutter_flow_theme.dart'; import 'package:hub/flutter_flow/flutter_flow_util.dart'; @@ -8,15 +11,14 @@ import 'package:hub/flutter_flow/nav/nav.dart'; import 'package:hub/shared/utils/validator_util.dart'; import 'package:share_plus/share_plus.dart'; -Widget buildDetails( - dynamic visitaWrapItem, +Widget buildVisitDetails( + dynamic item, BuildContext context, Future Function(BuildContext, int, int, String, String)? changeStatusAction) { return DetailsComponentWidget( buttons: [ - if (getStatus(visitaWrapItem['VAW_STATUS']) == - status.active) // REJECT ACTION + if (getStatus(item['VAW_STATUS']) == status.active) // REJECT ACTION FFButtonWidget( text: FFLocalizations.of(context).getVariableText( ptText: 'Cancelar', @@ -37,10 +39,10 @@ Widget buildDetails( await changeStatusAction ?.call( context, - int.parse(visitaWrapItem['VAW_DESTINO']), - int.parse(visitaWrapItem['VAW_ID']), - visitaWrapItem['VAW_CHAVE'] ?? '', - visitaWrapItem['VTE_DOCUMENTO'] ?? '', + int.parse(item['VAW_DESTINO']), + int.parse(item['VAW_ID']), + item['VAW_CHAVE'] ?? '', + item['VTE_DOCUMENTO'] ?? '', ) .then((value) { // Navigator.pop(context, value); @@ -93,8 +95,7 @@ Widget buildDetails( // borderRadius: 12, ), ), - if (getStatus(visitaWrapItem['VAW_STATUS']) != - status.active) // RECALL ACTION + if (getStatus(item['VAW_STATUS']) != status.active) // RECALL ACTION FFButtonWidget( text: FFLocalizations.of(context).getVariableText( ptText: 'Reagendar', @@ -106,10 +107,10 @@ Widget buildDetails( context.pop(); context.pushNamed('scheduleCompleteVisitPage', extra: { - 'dropdownValue1': visitaWrapItem['MOT_DESCRICAO'], - 'dropdownValue2': visitaWrapItem['NAC_DESCRICAO'], - 'visitorJsonList': [visitaWrapItem], - 'visitorStrList': visitaWrapItem['VTE_DOCUMENTO'], + 'dropdownValue1': item['MOT_DESCRICAO'], + 'dropdownValue2': item['NAC_DESCRICAO'], + 'visitorJsonList': [item], + 'visitorStrList': item['VTE_DOCUMENTO'], }); }, options: FFButtonOptions( @@ -127,8 +128,7 @@ Widget buildDetails( // borderRadius: 12, ), ), - if (getStatus(visitaWrapItem['VAW_STATUS']) == - status.active) // SHARE ACTION + if (getStatus(item['VAW_STATUS']) == status.active) // SHARE ACTION FFButtonWidget( text: FFLocalizations.of(context).getVariableText( ptText: 'Compartilhar', @@ -137,13 +137,13 @@ Widget buildDetails( icon: const Icon(Icons.share), onPressed: () async { Share.share(''' -Olá, \*${visitaWrapItem['VTE_NOME']}\*! Você foi convidado para \*${AppState().local}\*. +Olá, \*${item['VTE_NOME']}\*! Você foi convidado para \*${AppState().local}\*. \*Validade do Convite\*: -- Início: ${visitaWrapItem['VAW_DTINICIO']} -- Fim: ${visitaWrapItem['VAW_DTFIM']} +- Início: ${item['VAW_DTINICIO']} +- Fim: ${item['VAW_DTFIM']} -URL do Convite: https://visita.freaccess.com.br/${visitaWrapItem['VAW_ID']}/${AppState().cliUUID}/${visitaWrapItem['VAW_CHAVE']} +URL do Convite: https://visita.freaccess.com.br/${item['VAW_ID']}/${AppState().cliUUID}/${item['VAW_CHAVE']} '''); }, options: FFButtonOptions( @@ -164,59 +164,57 @@ URL do Convite: https://visita.freaccess.com.br/${visitaWrapItem['VAW_ID']}/${Ap ], labelsHashMap: Map.from({ '${FFLocalizations.of(context).getVariableText(ptText: "Nome", enText: "Name")}:': - visitaWrapItem['VTE_NOME'] ?? '', + item['VTE_NOME'] ?? '', '${FFLocalizations.of(context).getVariableText(ptText: "Inicio", enText: "Start")}:': - visitaWrapItem['VAW_DTINICIO'] != '' && - visitaWrapItem['VAW_DTINICIO'] != null + item['VAW_DTINICIO'] != '' && item['VAW_DTINICIO'] != null ? ValidatorUtil.toLocalDateTime( - 'yyyy-MM-dd HH:mm:ss', visitaWrapItem['VAW_DTINICIO']) + 'yyyy-MM-dd HH:mm:ss', item['VAW_DTINICIO']) : '', '${FFLocalizations.of(context).getVariableText(ptText: "Fim", enText: "End")}:': - visitaWrapItem['VAW_DTFIM'] != '' && - visitaWrapItem['VAW_DTFIM'] != null + item['VAW_DTFIM'] != '' && item['VAW_DTFIM'] != null ? ValidatorUtil.toLocalDateTime( - 'yyyy-MM-dd HH:mm:ss', visitaWrapItem['VAW_DTFIM']) + 'yyyy-MM-dd HH:mm:ss', item['VAW_DTFIM']) : '', }), imagePath: - 'https://freaccess.com.br/freaccess/getImage.php?cliID=${AppState().cliUUID}&atividade=getFoto&Documento=${visitaWrapItem['VTE_DOCUMENTO'] ?? ''}&tipo=E', + 'https://freaccess.com.br/freaccess/getImage.php?cliID=${AppState().cliUUID}&atividade=getFoto&Documento=${item['VTE_DOCUMENTO'] ?? ''}&tipo=E', statusHashMap: [ - if (getStatus(visitaWrapItem['VAW_STATUS']) == status.active) + if (getStatus(item['VAW_STATUS']) == status.active) Map.from({ FFLocalizations.of(context).getVariableText( ptText: 'Ativo', enText: 'Active', ): FlutterFlowTheme.of(context).warning, }), - if (getStatus(visitaWrapItem['VAW_STATUS']) == status.unknown) + if (getStatus(item['VAW_STATUS']) == status.unknown) Map.from({ FFLocalizations.of(context).getVariableText( ptText: 'Pendente', enText: 'Pending', ): FlutterFlowTheme.of(context).alternate, }), - if (getStatus(visitaWrapItem['VAW_STATUS']) == status.canceled) + if (getStatus(item['VAW_STATUS']) == status.canceled) Map.from({ FFLocalizations.of(context).getVariableText( ptText: 'Cancelado', enText: 'Canceled', ): FlutterFlowTheme.of(context).error, }), - if (getStatus(visitaWrapItem['VAW_STATUS']) == status.finished) + if (getStatus(item['VAW_STATUS']) == status.finished) Map.from({ FFLocalizations.of(context).getVariableText( ptText: 'Finalizado', enText: 'Finished', ): FlutterFlowTheme.of(context).success, }), - if (getStatus(visitaWrapItem['VAW_STATUS']) == status.blocked) + if (getStatus(item['VAW_STATUS']) == status.blocked) Map.from({ FFLocalizations.of(context).getVariableText( ptText: 'Bloqueado', enText: 'Blocked', ): FlutterFlowTheme.of(context).error, }), - if (getStatus(visitaWrapItem['VAW_STATUS']) == status.inactive) + if (getStatus(item['VAW_STATUS']) == status.inactive) Map.from({ FFLocalizations.of(context).getVariableText( ptText: 'Inativo', @@ -226,3 +224,178 @@ URL do Convite: https://visita.freaccess.com.br/${visitaWrapItem['VAW_ID']}/${Ap ], ); } + +Widget buildPetDetails( + dynamic item, + BuildContext context, + Future Function(BuildContext, int, int, String, String)? + changeStatusAction) { + return DetailsComponentWidget( + buttons: [ + // EDIT ACTION + FFButtonWidget( + text: FFLocalizations.of(context).getVariableText( + ptText: 'Editar', + enText: 'Edit', + ), + icon: const Icon(Icons.edit), + onPressed: () async { + context.pop(); + context.pop(); + + context.pushNamed('petsPage', extra: { + 'pet': item, + }); + }, + options: FFButtonOptions( + width: 130, + height: 40, + color: FlutterFlowTheme.of(context).primaryBackground, + elevation: 0, + textStyle: TextStyle( + color: FlutterFlowTheme.of(context).primaryText, + ), + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).primaryBackground, + width: 1, + ), + // borderRadius: 12, + ), + ), + + // DELETE ACTION + FFButtonWidget( + text: FFLocalizations.of(context).getVariableText( + ptText: 'Excluir', + enText: 'Delete', + ), + icon: const Icon(Icons.close), + onPressed: () async { + showAlertDialog( + context, + FFLocalizations.of(context).getVariableText( + ptText: 'Excluir Pet', + enText: 'Delete Pet', + ), + FFLocalizations.of(context).getVariableText( + ptText: 'Você tem certeza que deseja excluir esse pet?', + enText: 'Are you sure you want to delete this pet?', + ), () async { + int id = item['id']; + await PhpGroup.deletePet + .call( + userUUID: AppState().userUUID, + devUUID: AppState().devUUID, + cliID: AppState().cliUUID, + petID: id, + ) + .then((value) { + // Navigator.pop(context, value); + context.pop(value); + context.pop(value); + + if (value == false) { + showSnackbar( + context, + FFLocalizations.of(context).getVariableText( + ptText: 'Erro ao excluir pet', + enText: 'Error deleting pet', + ), + true, + ); + } else if (value == true) { + showSnackbar( + context, + FFLocalizations.of(context).getVariableText( + enText: 'Success deleting pet', + ptText: 'Succeso ao excluir pet', + ), + false, + ); + } + }).catchError((err, stack) { + context.pop(); + showSnackbar( + context, + FFLocalizations.of(context).getVariableText( + enText: 'Error deleting pet', + ptText: 'Erro ao excluir pet', + ), + true, + ); + }); + }); + }, + options: FFButtonOptions( + width: 130, + height: 40, + color: FlutterFlowTheme.of(context).primaryBackground, + elevation: 0, + textStyle: TextStyle( + color: FlutterFlowTheme.of(context).primaryText, + ), + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).primaryBackground, + width: 1, + ), + // borderRadius: 12, + ), + ), + ], + // 'MIN', 'PEQ', 'MED', 'GRA', 'GIG' + labelsHashMap: Map.from({ + if (item['species'] != null && item['species'] != '') + '${FFLocalizations.of(context).getVariableText(ptText: "Espécie", enText: "Species")}:': + item['species'].toString().toUpperCase(), + if (item['breed'] != null && item['breed'] != '') + '${FFLocalizations.of(context).getVariableText(ptText: "Raça", enText: "Breed")}:': + item['breed'].toString().toUpperCase(), + if (item['color'] != null && item['color'] != '') + '${FFLocalizations.of(context).getVariableText(ptText: "Cor", enText: "Color")}:': + item['color'].toString().toUpperCase(), + if (item['birthdayDate'] != null && item['birthdayDate'] != '') + '${FFLocalizations.of(context).getVariableText(ptText: "Data de Nascimento", enText: "Date of Birth")}:': + ValidatorUtil.formatDateTimePicker(item['birthdayDate']), + if (item['gender'] != null && item['gender'] != '') + '${FFLocalizations.of(context).getVariableText(ptText: "Gênero", enText: "Gender")}:': + item['gender'] == 'MAC' + ? FFLocalizations.of(context) + .getVariableText(ptText: 'MACHO', enText: 'MALE') + : FFLocalizations.of(context) + .getVariableText(enText: 'FEMALE', ptText: 'FÊMEA'), + if (item['size'] != null && item['size'] != '') + '${FFLocalizations.of(context).getVariableText(ptText: "Porte", enText: "Size")}:': + item['size'] == 'MIN' + ? FFLocalizations.of(context) + .getVariableText(ptText: 'MINI', enText: 'MINI') + : item['size'] == 'PEQ' + ? FFLocalizations.of(context) + .getVariableText(ptText: 'PEQUENO', enText: 'SMALL') + : item['size'] == 'MED' + ? FFLocalizations.of(context) + .getVariableText(ptText: 'MÉDIO', enText: 'MEDIUM') + : item['size'] == 'GRD' + ? FFLocalizations.of(context).getVariableText( + ptText: 'GRANDE', enText: 'LARGE') + : item['size'] == 'GIG' + ? FFLocalizations.of(context).getVariableText( + ptText: 'GIGANTE', enText: 'GIANT') + : '', + if (item['notes'] != null && item['notes'] != '') + '${FFLocalizations.of(context).getVariableText(ptText: "Observação", enText: "Notes")}:': + item['notes'] ?? '', + }), + imagePath: + 'https://freaccess.com.br/freaccess/getImage.php?devUUID=${AppState().devUUID}&userUUID=${AppState().userUUID}&cliID=${AppState().cliUUID}&atividade=consultaFotoPet&petId=${item['id'] ?? ''}', + statusHashMap: [ + if (item['gender'] == "MAC") + Map.from({ + item['name']: Color(0xFF094CB0), + }), + if (item['gender'] == "FEM") + Map.from({ + item['name']: Color(0xFFE463E7), + }), + ], + ); +} diff --git a/lib/flutter_flow/flutter_flow_theme.dart b/lib/flutter_flow/flutter_flow_theme.dart index a924cb38..9bc83617 100644 --- a/lib/flutter_flow/flutter_flow_theme.dart +++ b/lib/flutter_flow/flutter_flow_theme.dart @@ -5,7 +5,6 @@ import 'package:google_fonts/google_fonts.dart'; import 'package:shared_preferences/shared_preferences.dart'; - const kThemeModeKey = '__theme_mode__'; SharedPreferences? _prefs; @@ -620,4 +619,4 @@ extension TextStyleHelper on TextStyle { height: lineHeight, shadows: shadows, ); -} \ No newline at end of file +} diff --git a/lib/flutter_flow/nav/nav.dart b/lib/flutter_flow/nav/nav.dart index e5c7cc70..eabaae56 100644 --- a/lib/flutter_flow/nav/nav.dart +++ b/lib/flutter_flow/nav/nav.dart @@ -201,7 +201,16 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) => GoRouter( FFRoute( name: 'petsPage', path: '/petsPage', - builder: (context, params) => PetsPageWidget(), + builder: (context, params) { + final pet = params.getParam( + 'pet', + ParamType.JSON, + ); + + return PetsPageWidget( + pet: pet, + ); + }, ), // FFRoute( // name: 'settingsPage', diff --git a/lib/main.dart b/lib/main.dart index cf5d100e..cc6223f8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -79,6 +79,7 @@ class _AppState extends State { Future.delayed(const Duration(milliseconds: 1000), () => setState(() => _appStateNotifier.stopShowingSplashImage())); } + // // Future showCustomTrackingDialog(BuildContext context) async { // await showDialog( @@ -115,8 +116,6 @@ class _AppState extends State { // log("UUID de Publicidade: $uuid"); // } - - void setLocale(String language) { setState(() => _locale = createLocale(language)); FFLocalizations.storeLocale(language); diff --git a/lib/pages/pets_page/pets_history_screen.dart b/lib/pages/pets_page/pets_history_screen.dart new file mode 100644 index 00000000..a1055f4d --- /dev/null +++ b/lib/pages/pets_page/pets_history_screen.dart @@ -0,0 +1,259 @@ +import 'package:flutter/material.dart'; +import 'package:hub/actions/actions.dart'; +import 'package:hub/backend/api_requests/api_calls.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_action.dart'; +import 'package:hub/flutter_flow/flutter_flow_theme.dart'; +import 'package:hub/flutter_flow/flutter_flow_util.dart'; +import 'package:hub/pages/schedule_complete_visit_page/schedule_complete_visit_page_model.dart'; +import 'package:hub/shared/utils/dialog_util.dart'; +import 'package:hub/shared/utils/log_util.dart'; +import 'package:hub/shared/utils/validator_util.dart'; + +class PetsHistoryScreen extends StatefulWidget { + PetsHistoryScreen({Key? key}) : super(key: key); + + @override + _PetsHistoryScreenState createState() => _PetsHistoryScreenState(); +} + +class _PetsHistoryScreenState extends State + with TickerProviderStateMixin { + late ScrollController _scrollController; + int _pageNumber = 1; + final int _pageSize = 10; + bool _hasData = false; + bool _loading = false; + + late Future _petsFuture; + List _petsWrap = []; + + @override + void initState() { + super.initState(); + _petsFuture = _fetchVisits(); + + _scrollController = ScrollController() + ..addListener(() { + if (_scrollController.position.atEdge && + _scrollController.position.pixels != 0) { + _loadMore(); + } + }); + } + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + + Future _fetchVisits() async { + try { + setState(() => _loading = true); + + var response = await PhpGroup.getPets.call( + devUUID: AppState().devUUID, + userUUID: AppState().userUUID, + cliID: AppState().cliUUID, + atividade: 'consultaPets', + pageSize: _pageSize, + page: _pageNumber, + ); + + final List pets = response.jsonBody['pets'] ?? []; + + if (pets != null && pets.isNotEmpty) { + setState(() { + _petsWrap.addAll(pets); + _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 Pets", e, s); + setState(() { + _hasData = false; + _loading = false; + }); + } + } + + void _loadMore() { + if (_hasData == true) { + _pageNumber++; + _petsFuture = _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."), + ), + duration: const Duration(seconds: 3), + backgroundColor: FlutterFlowTheme.of(context).primary, + ), + ); + } + + @override + Widget build(BuildContext context) { + return 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 Pet encontrado!", + enText: "No pets found")), + ) + ], + ), + ) + else if (_hasData == true || _pageNumber >= 1) + Expanded( + child: FutureBuilder( + future: _petsFuture, + builder: (context, snapshot) { + return ListView.builder( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + controller: _scrollController, + itemCount: _petsWrap.length, + itemBuilder: (context, index) { + final item = _petsWrap[index]; + return _item(context, item); + }); + }, + )), + if (_hasData == true && _loading == true) + Container( + padding: const EdgeInsets.only(top: 15, bottom: 15), + child: Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + FlutterFlowTheme.of(context).primary, + ), + ), + ), + ) + ].addToStart(const SizedBox(height: 0)), + ); + } + + Widget _item(BuildContext context, dynamic uItem) { + return CardItemTemplateComponentWidget( + imagePath: + 'https://freaccess.com.br/freaccess/getImage.php?devUUID=${AppState().devUUID}&userUUID=${AppState().userUUID}&cliID=${AppState().cliUUID}&atividade=consultaFotoPet&petId=${uItem['id'] ?? ''}', + labelsHashMap: { + '${FFLocalizations.of(context).getVariableText(ptText: "Nome", enText: "Name")}:': + uItem['name'] ?? '', + '${FFLocalizations.of(context).getVariableText(ptText: "Espécie", enText: "Species")}:': + uItem['species'] ?? '', + '${FFLocalizations.of(context).getVariableText(ptText: "Raça", enText: "Breed")}:': + uItem['breed'] ?? '', + }, + statusHashMap: [ + if (uItem['size'] == "MIN") + { + FFLocalizations.of(context).getVariableText( + ptText: 'Mini', + enText: 'Mini', + ): FlutterFlowTheme.of(context).accent4, + }, + if (uItem['size'] == "PEQ") + { + FFLocalizations.of(context).getVariableText( + ptText: 'Pequeno', + enText: 'Small', + ): FlutterFlowTheme.of(context).accent4, + }, + if (uItem['size'] == "MED") + { + FFLocalizations.of(context).getVariableText( + ptText: 'Medio', + enText: 'Medium', + ): FlutterFlowTheme.of(context).accent4, + }, + if (uItem['size'] == "GRA") + { + FFLocalizations.of(context).getVariableText( + ptText: 'Grande', + enText: 'Big', + ): FlutterFlowTheme.of(context).accent4, + }, + if (uItem['size'] == "GIG") + { + FFLocalizations.of(context).getVariableText( + ptText: 'Gigante', + enText: 'Giant', + ): FlutterFlowTheme.of(context).accent4, + }, + if (uItem['gender'] == "MAC") + { + FFLocalizations.of(context).getVariableText( + ptText: 'Macho', enText: 'Male'): Color(0xFF094CB0), + }, + if (uItem['gender'] == "FEM") + { + FFLocalizations.of(context).getVariableText( + ptText: 'Femêa', + enText: 'Female', + ): Color(0xFFE463E7), + } + ], + onTapCardItemAction: () async { + await showDialog( + useSafeArea: true, + context: context, + builder: (context) { + return Dialog( + alignment: Alignment.center, + child: buildPetDetails( + uItem, + context, + changeStatusAction, + ), + ); + }, + ).whenComplete(() { + safeSetState(() { + _pageNumber = 1; + _petsWrap = []; + _petsFuture = _fetchVisits(); + }); + }).catchError((e, s) { + DialogUtil.errorDefault(context); + LogUtil.requestAPIFailed( + "proccessRequest.php", "", "Consulta de Pets", e, s); + safeSetState(() { + _hasData = false; + _loading = false; + }); + }); + }, + ); + } +} diff --git a/lib/pages/pets_page/pets_page_model.dart b/lib/pages/pets_page/pets_page_model.dart index 3ecffee0..ec33c4dc 100644 --- a/lib/pages/pets_page/pets_page_model.dart +++ b/lib/pages/pets_page/pets_page_model.dart @@ -13,13 +13,12 @@ class PetsPageModel extends FlutterFlowModel { late final TabController tabBarController; ApiCallResponse? petsResponse; - + int? petId; BuildContext? buildContext; // Controller para o Upload de Arquivos bool isDataUploading = false; - FFUploadedFile uploadedLocalFile = - FFUploadedFile(bytes: Uint8List.fromList([])); + FFUploadedFile? uploadedLocalFile; String? imgBase64; // Controller para o DropDown @@ -148,7 +147,7 @@ class PetsPageModel extends FlutterFlowModel { // Validador do formulário bool isFormValid(BuildContext context) { - if (uploadedLocalFile.bytes?.isEmpty ?? true) { + if (uploadedLocalFile == null) { return false; } if (textControllerName.text.isEmpty || @@ -175,34 +174,68 @@ class PetsPageModel extends FlutterFlowModel { return true; } + Future updatePet() async { + await PhpGroup.updatePet + .call( + cliID: AppState().cliUUID, + devUUID: AppState().devUUID, + userUUID: AppState().userUUID, + petID: petId, + image: await actions.convertImageFileToBase64( + uploadedLocalFile!, + ), + birthdayDate: textControllerData!.text, + color: textControllerColor!.text, + breed: textControllerRace!.text, + species: textControllerSpecies!.text, + name: textControllerName!.text, + gender: dropDownValue1!, + notes: textControllerObservation!.text, + size: dropDownValue2!, + ) + .then((response) { + log(response.jsonBody.toString()); + if (response.jsonBody['error'] == true) { + DialogUtil.error(buildContext!, + jsonDecode(response.jsonBody['error_msg'])[0]['message']); + } + }).catchError((error) { + log(error.toString()); + DialogUtil.errorDefault(buildContext!); + }); + } + Future registerPet() async { - if (isFormValid == true) { - await PhpGroup.registerPet - .call( - cliID: AppState().cliUUID, - devUUID: AppState().devUUID, - userUUID: AppState().userUUID, - image: await actions.convertImageFileToBase64( - uploadedLocalFile, - ), - // birthdayDate: textControllerData!.text, - color: textControllerColor!.text, - breed: textControllerRace!.text, - species: textControllerSpecies!.text, - name: textControllerName!.text, - gender: dropDownValue1!, - size: dropDownValue2!, - notes: textControllerObservation!.text, - ) - .then((response) { - if (response.jsonBody['error'] == true) - log('Erro ao registrar pet: ${response.jsonBody}'); - return DialogUtil.errorDefault(buildContext!); - return true; - }).catchError((error) { - log(error.toString()); - return DialogUtil.errorDefault(buildContext!); - }); - } + await PhpGroup.registerPet + .call( + cliID: AppState().cliUUID, + devUUID: AppState().devUUID, + userUUID: AppState().userUUID, + image: await actions.convertImageFileToBase64( + uploadedLocalFile!, + ), + birthdayDate: textControllerData!.text, + color: textControllerColor!.text, + breed: textControllerRace!.text, + species: textControllerSpecies!.text, + name: textControllerName!.text, + gender: dropDownValue1!, + size: dropDownValue2!, + notes: textControllerObservation!.text, + ) + .then((response) { + if (response.jsonBody['error'] == true) { + DialogUtil.error(buildContext!, + jsonDecode(response.jsonBody['error_msg'])[0]['message']); + } + DialogUtil.success( + buildContext!, + FFLocalizations.of(buildContext!).getVariableText( + enText: 'Pet successfully registered', + ptText: 'Pet cadastrado com sucesso', + )); + }).catchError((error) { + DialogUtil.errorDefault(buildContext!); + }); } } diff --git a/lib/pages/pets_page/pets_page_widget.dart b/lib/pages/pets_page/pets_page_widget.dart index bf2069d5..7818e0a5 100644 --- a/lib/pages/pets_page/pets_page_widget.dart +++ b/lib/pages/pets_page/pets_page_widget.dart @@ -1,12 +1,16 @@ +import 'dart:convert'; import 'dart:developer'; import 'package:easy_debounce/easy_debounce.dart'; import 'package:flutter/material.dart'; +import 'package:crypto/crypto.dart' as crypto; import 'package:flutter/services.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:http/http.dart'; import 'package:hub/backend/api_requests/api_calls.dart'; +import 'package:hub/backend/api_requests/api_service.dart'; import 'package:hub/components/atomic_components/shared_components_atoms/appbar.dart'; import 'package:hub/components/atomic_components/shared_components_atoms/custom_datepicker.dart'; @@ -15,6 +19,7 @@ import 'package:hub/components/atomic_components/shared_components_atoms/custom_ import 'package:hub/components/atomic_components/shared_components_atoms/media_upload_button.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/custom_code/actions/convert_to_upload_file.dart'; import 'package:hub/flutter_flow/flutter_flow_theme.dart'; import 'package:hub/flutter_flow/flutter_flow_util.dart'; @@ -23,6 +28,7 @@ import 'package:hub/flutter_flow/form_field_controller.dart'; import 'package:hub/flutter_flow/internationalization.dart'; import 'package:hub/flutter_flow/nav/nav.dart'; import 'package:hub/flutter_flow/upload_data.dart'; +import 'package:hub/pages/pets_page/pets_history_screen.dart'; import 'package:hub/pages/pets_page/pets_page_model.dart'; import 'package:hub/shared/utils/dialog_util.dart'; @@ -31,7 +37,12 @@ import 'package:sqflite/sqflite.dart'; import '/custom_code/actions/index.dart' as actions; class PetsPageWidget extends StatefulWidget { - PetsPageWidget({super.key}); + dynamic pet; + + PetsPageWidget({ + super.key, + this.pet, + }); @override State createState() => _PetsPageWidgetState(); @@ -41,39 +52,7 @@ class _PetsPageWidgetState extends State with SingleTickerProviderStateMixin { late PetsPageModel _model; final _formKey = GlobalKey(); - - Future registerPet() async { - await PhpGroup.registerPet - .call( - cliID: AppState().cliUUID, - devUUID: AppState().devUUID, - userUUID: AppState().userUUID, - image: await actions.convertImageFileToBase64( - _model.uploadedLocalFile, - ), - birthdayDate: _model.textControllerData!.text, - color: _model.textControllerColor!.text, - breed: _model.textControllerRace!.text, - species: _model.textControllerSpecies!.text, - name: _model.textControllerName!.text, - gender: _model.dropDownValue1!, - size: _model.dropDownValue2!, - notes: _model.textControllerObservation!.text, - ) - .then((response) { - log('aqui é pá'); - if (response.jsonBody['error'] == true) { - DialogUtil.error( - context, jsonDecode(response.jsonBody['error_msg'])[0]['message']); - - log('aqui é trum'); - } - }).catchError((error) { - log('aqui é pum'); - - DialogUtil.errorDefault(context); - }); - } + bool isEditing = false; @override void initState() { @@ -81,24 +60,54 @@ class _PetsPageWidgetState extends State _model = PetsPageModel(); _model.tabBarController = TabController(length: 2, vsync: this); - _model.textControllerName ??= TextEditingController(); + widget.pet != null ? isEditing = true : isEditing = false; + + // _handleUploadComplete(actions.convertToUploadFile()) + + if (widget.pet != null) { + int petId = widget.pet['id']; + _model.petId = petId; + + (() async { + Response response = await get(Uri.parse( + 'https://freaccess.com.br/freaccess/getImage.php?devUUID=${AppState().devUUID}&userUUID=${AppState().userUUID}&cliID=${AppState().cliUUID}&atividade=consultaFotoPet&petId=$petId')); + String base64 = base64Encode(response.bodyBytes); + FFUploadedFile uploadedFile = await convertToUploadFile(base64); + _handleUploadComplete(uploadedFile); + })(); + } + + _model.textControllerName ??= TextEditingController( + text: widget.pet != null ? widget.pet['name'] : ''); _model.textFieldFocusName ??= FocusNode(); - _model.textControllerSpecies ??= TextEditingController(); + _model.textControllerSpecies ??= TextEditingController( + text: widget.pet != null ? widget.pet['species'] : ''); _model.textFieldFocusSpecies ??= FocusNode(); - _model.textControllerRace ??= TextEditingController(); + _model.textControllerRace ??= TextEditingController( + text: widget.pet != null ? widget.pet['breed'] : ''); _model.textFieldFocusRace ??= FocusNode(); - _model.textControllerColor ??= TextEditingController(); + _model.textControllerColor ??= TextEditingController( + text: widget.pet != null ? widget.pet['color'] : ''); _model.textFieldFocusColor ??= FocusNode(); - _model.textControllerData ??= TextEditingController(); + _model.textControllerData ??= TextEditingController( + text: widget.pet != null + ? ValidatorUtil.formatDateTimePicker(widget.pet['birthdayDate']) + : ''); _model.textFieldFocusData ??= FocusNode(); - _model.textControllerObservation ??= TextEditingController(); + _model.textControllerObservation ??= TextEditingController( + text: widget.pet != null ? widget.pet['notes'] : ''); _model.textFieldFocusObservation ??= FocusNode(); + if (widget.pet != null) { + _model.dropDownValue1 ??= widget.pet['gender'] ?? ''; + _model.dropDownValue2 ??= widget.pet['size'] ?? ''; + } + _model.dropDownValueController1 ??= FormFieldController(_model.dropDownValue1 ??= ''); @@ -113,6 +122,7 @@ class _PetsPageWidgetState extends State } void _handleUploadComplete(FFUploadedFile uploadedFile) { + log('Chamou o handleUploadComplete'); setState(() { _model.uploadedLocalFile = uploadedFile; }); @@ -120,6 +130,7 @@ class _PetsPageWidgetState extends State @override Widget build(BuildContext context) { + log('Chamou o build'); _model.buildContext = context; return Scaffold( appBar: _buildAppBar(context), @@ -135,11 +146,14 @@ class _PetsPageWidgetState extends State return TabViewUtil( context: context, model: _model, - labelTab1: 'Cadastrar', - labelTab2: 'Consultar', + labelTab1: FFLocalizations.of(context) + .getVariableText(ptText: 'Cadastrar', enText: 'Register'), + labelTab2: FFLocalizations.of(context) + .getVariableText(ptText: 'Consultar', enText: 'History'), controller: _model.tabBarController, - widget1: _buildRegisterForm(context), - widget2: const Center(child: Text('Consultar')), + widget1: + isEditing ? _buildEditForm(context) : _buildRegisterForm(context), + widget2: PetsHistoryScreen(), ); } @@ -209,7 +223,8 @@ class _PetsPageWidgetState extends State hintText: FFLocalizations.of(context).getVariableText( ptText: 'Espécie', enText: 'Species'), suffixIcon: Icons.pest_control, - haveMaxLength: false, + haveMaxLength: true, + maxLength: 80, ), ), Padding( @@ -224,7 +239,8 @@ class _PetsPageWidgetState extends State hintText: FFLocalizations.of(context) .getVariableText(ptText: 'Raça', enText: 'Race'), suffixIcon: Icons.pets, - haveMaxLength: false, + haveMaxLength: true, + maxLength: 80, ), ), Padding( @@ -239,7 +255,467 @@ class _PetsPageWidgetState extends State hintText: FFLocalizations.of(context) .getVariableText(ptText: 'Cor', enText: 'Color'), suffixIcon: Icons.invert_colors, - haveMaxLength: false, + haveMaxLength: true, + maxLength: 80, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 0, 15), + child: Row( + mainAxisSize: MainAxisSize.max, + children: [ + SizedBox( + width: MediaQuery.of(context).size.width, + height: 60.0, + child: Stack( + children: [ + Padding( + padding: const EdgeInsetsDirectional.fromSTEB( + 24.0, 0.0, 24.0, 0.0), + child: TextFormField( + controller: _model.textControllerData, + focusNode: _model.textFieldFocusData, + readOnly: true, + autovalidateMode: + AutovalidateMode.onUserInteraction, + autofocus: false, + obscureText: false, + decoration: InputDecoration( + isDense: true, + 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), + ), + hintText: FFLocalizations.of(context) + .getVariableText( + ptText: 'Data de Nascimento', + enText: 'Date of Birth', + ), + 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), + lineHeight: 1.0, + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context) + .customColor6, + width: 0.5, + ), + borderRadius: + BorderRadius.circular(10.0), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context) + .primary, + width: 0.5, + ), + borderRadius: + BorderRadius.circular(10.0), + ), + errorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context) + .error, + width: 0.5, + ), + borderRadius: + BorderRadius.circular(10.0), + ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide( + color: FlutterFlowTheme.of(context) + .error, + width: 0.5, + ), + borderRadius: + BorderRadius.circular(10.0), + ), + suffixIcon: Icon( + Icons.date_range, + color: FlutterFlowTheme.of(context) + .accent1, + ), + ), + style: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: + FlutterFlowTheme.of(context) + .bodyMediumFamily, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap() + .containsKey( + FlutterFlowTheme.of(context) + .bodyMediumFamily), + lineHeight: 1.8, + ), + textAlign: TextAlign.start, + validator: _model + .textControllerDataValidator + .asValidator(context), + ), + ), + Padding( + padding: const EdgeInsetsDirectional.fromSTEB( + 24.0, 0.0, 24.0, 0.0), + child: InkWell( + splashColor: Colors.transparent, + focusColor: Colors.transparent, + hoverColor: Colors.transparent, + highlightColor: Colors.transparent, + onTap: () async { + final pickedDate = await showDatePicker( + context: context, + initialDate: getCurrentTimestamp, + firstDate: DateTime(1990), + lastDate: DateTime.now(), + builder: (context, child) { + return wrapInMaterialDatePickerTheme( + context, + child!, + headerBackgroundColor: + FlutterFlowTheme.of(context) + .primary, + headerForegroundColor: + FlutterFlowTheme.of(context) + .info, + headerTextStyle: + FlutterFlowTheme.of(context) + .headlineLarge + .override( + fontFamily: FlutterFlowTheme + .of(context) + .headlineLargeFamily, + fontSize: 32.0, + letterSpacing: 0.0, + fontWeight: + FontWeight.w600, + useGoogleFonts: GoogleFonts + .asMap() + .containsKey( + FlutterFlowTheme.of( + context) + .headlineLargeFamily), + ), + pickerBackgroundColor: + FlutterFlowTheme.of(context) + .primaryBackground, + pickerForegroundColor: + FlutterFlowTheme.of(context) + .primaryText, + selectedDateTimeBackgroundColor: + FlutterFlowTheme.of(context) + .primary, + selectedDateTimeForegroundColor: + FlutterFlowTheme.of(context) + .info, + actionButtonForegroundColor: + FlutterFlowTheme.of(context) + .primaryText, + iconSize: 24.0, + ); + }, + ); + + if (pickedDate != null) { + setState(() { + log('Chamou o setState da datinha'); + + _model.selectedDate = DateTime( + pickedDate.year, + pickedDate.month, + pickedDate.day, + ); + + log(_model.selectedDate.toString()); + _model.textControllerData = + TextEditingController( + text: dateTimeFormat( + 'dd/MM/yyyy', + _model.selectedDate, + locale: FFLocalizations.of(context) + .languageCode, + )); + log(_model.textControllerData.text); + _model.textControllerData?.selection = + TextSelection.collapsed( + offset: _model.textControllerData! + .text.length, + ); + }); + } + }, + child: Container( + width: double.infinity, + height: 80.0, + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(10.0), + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + Align( + alignment: const AlignmentDirectional(-1.0, 0.0), + child: Padding( + padding: const EdgeInsetsDirectional.fromSTEB( + 24.0, 0, 0.0, 15), + child: Text( + FFLocalizations.of(context).getVariableText( + ptText: 'Selecione as opções disponíveis', + enText: 'Select the available options', + ), + textAlign: TextAlign.start, + style: FlutterFlowTheme.of(context) + .bodySmall + .override( + fontFamily: FlutterFlowTheme.of(context) + .bodySmallFamily, + letterSpacing: 0.0, + fontWeight: FontWeight.w600, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context) + .bodyMediumFamily), + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 0, 15), + child: CustomSelect( + options: const ['MAC', 'FEM'], + controller: _model.dropDownValueController1 ??= + FormFieldController( + _model.dropDownValue1 ??= ''), + isRequired: true, + changed: (val) => safeSetState(() { + _model.dropDownValue1 = val; + }), + dropDownValue: _model.dropDownValue1, + optionsLabel: [ + FFLocalizations.of(context).getVariableText( + ptText: 'Macho', enText: 'Male'), + FFLocalizations.of(context).getVariableText( + ptText: 'Fêmea', enText: 'Female') + ], + hintText: FFLocalizations.of(context).getVariableText( + ptText: 'Selecione o gênero do Pet', + enText: 'Select the gender of the Pet')), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 0, 15), + child: CustomSelect( + options: const ['MIN', 'PEQ', 'MED', 'GRA', 'GIG'], + controller: _model.dropDownValueController2 ??= + FormFieldController( + _model.dropDownValue2 ??= ''), + isRequired: true, + changed: (val) => safeSetState(() { + _model.dropDownValue2 = val; + }), + optionsLabel: [ + FFLocalizations.of(context).getVariableText( + ptText: 'Mini', enText: 'Mini'), + FFLocalizations.of(context).getVariableText( + ptText: 'Pequeno', enText: 'Small'), + FFLocalizations.of(context).getVariableText( + ptText: 'Médio', enText: 'Medium'), + FFLocalizations.of(context).getVariableText( + ptText: 'Grande', enText: 'Big'), + FFLocalizations.of(context).getVariableText( + ptText: 'Gigante', enText: 'Giant'), + ], + hintText: FFLocalizations.of(context).getVariableText( + ptText: 'Selecione o porte do Pet', + enText: 'Select the size of the Pet')), + ), + Align( + alignment: const AlignmentDirectional(-1.0, 0.0), + child: Padding( + padding: const EdgeInsetsDirectional.fromSTEB( + 24.0, 0, 0.0, 15), + child: Text( + FFLocalizations.of(context).getVariableText( + ptText: + 'Você tem alguma observação sobre o seu Pet?', + enText: + 'Do you have any observations about your Pet?', + ), + textAlign: TextAlign.start, + style: FlutterFlowTheme.of(context) + .bodySmall + .override( + fontFamily: FlutterFlowTheme.of(context) + .bodySmallFamily, + letterSpacing: 0.0, + fontWeight: FontWeight.w600, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context) + .bodyMediumFamily), + ), + ), + ), + ), + CustomInputUtil( + controller: _model.textControllerObservation, + validator: _model.textControllerObservationValidator + .asValidator(context), + focusNode: _model.textFieldFocusObservation, + labelText: FFLocalizations.of(context).getVariableText( + ptText: 'Escreva as suas observações aqui...', + enText: 'Write your observations here...'), + hintText: FFLocalizations.of(context).getVariableText( + ptText: 'Escreva as suas observações aqui...', + enText: 'Write your observations here...'), + suffixIcon: Icons.text_fields, + haveMaxLength: true, + maxLength: 80, + ), + Padding( + padding: const EdgeInsets.fromLTRB(70, 20, 70, 30), + child: SubmitButtonUtil( + labelText: FFLocalizations.of(context) + .getVariableText( + ptText: 'Cadastrar', enText: 'Register'), + onPressed: _model.isFormValid(context) + ? _model.registerPet + : null), + ), + ])), + ], + ), + ); + } + + Widget _buildEditForm(BuildContext context) { + return SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Align( + alignment: const AlignmentDirectional(-1.0, 0.0), + child: Padding( + padding: const EdgeInsetsDirectional.fromSTEB(24.0, 20, 0.0, 15), + child: Text( + FFLocalizations.of(context).getVariableText( + ptText: 'Preencha o formulário com os dados do seu Pet', + enText: 'Fill out the form with your Pet\'s 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), + ), + ), + ), + ), + Form( + key: _formKey, + autovalidateMode: AutovalidateMode.onUserInteraction, + child: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(0, 10, 0, 20), + child: MediaUploadButtonUtil( + onUploadComplete: _handleUploadComplete, + isUploading: _model.isDataUploading, + uploadedFiles: _model.uploadedLocalFile, + labelText: FFLocalizations.of(context).getVariableText( + ptText: 'Clique para adicionar a foto de seu Pet', + enText: 'Click to add your Pet\'s photo'), + ), + ), + CustomInputUtil( + controller: _model.textControllerName, + validator: _model.textControllerNameValidator + .asValidator(context), + focusNode: _model.textFieldFocusName, + labelText: FFLocalizations.of(context) + .getVariableText(ptText: 'Nome', enText: 'Name'), + hintText: FFLocalizations.of(context) + .getVariableText(ptText: 'Nome', enText: 'Name'), + suffixIcon: Icons.person, + haveMaxLength: true, + maxLength: 80, + ), + Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 0, 15), + child: CustomInputUtil( + controller: _model.textControllerSpecies, + validator: _model.textControllerSpeciesValidator + .asValidator(context), + focusNode: _model.textFieldFocusSpecies, + labelText: FFLocalizations.of(context).getVariableText( + ptText: 'Espécie', enText: 'Species'), + hintText: FFLocalizations.of(context).getVariableText( + ptText: 'Espécie', enText: 'Species'), + suffixIcon: Icons.pest_control, + haveMaxLength: true, + maxLength: 80, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 0, 15), + child: CustomInputUtil( + controller: _model.textControllerRace, + validator: _model.textControllerRaceValidator + .asValidator(context), + focusNode: _model.textFieldFocusRace, + labelText: FFLocalizations.of(context) + .getVariableText(ptText: 'Raça', enText: 'Race'), + hintText: FFLocalizations.of(context) + .getVariableText(ptText: 'Raça', enText: 'Race'), + suffixIcon: Icons.pets, + haveMaxLength: true, + maxLength: 80, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 0, 15), + child: CustomInputUtil( + controller: _model.textControllerColor, + validator: _model.textControllerColorValidator + .asValidator(context), + focusNode: _model.textFieldFocusColor, + labelText: FFLocalizations.of(context) + .getVariableText(ptText: 'Cor', enText: 'Color'), + hintText: FFLocalizations.of(context) + .getVariableText(ptText: 'Cor', enText: 'Color'), + suffixIcon: Icons.invert_colors, + haveMaxLength: true, + maxLength: 80, ), ), Padding( @@ -587,9 +1063,10 @@ class _PetsPageWidgetState extends State child: SubmitButtonUtil( labelText: FFLocalizations.of(context) .getVariableText( - ptText: 'Cadastrar', enText: 'Register'), - onPressed: - _model.isFormValid(context) ? registerPet : null), + ptText: 'Salvar', enText: 'Save'), + onPressed: _model.isFormValid(context) + ? _model.updatePet + : null), ), ])), ], diff --git a/lib/pages/visit_history_page/visit_history_page_widget.dart b/lib/pages/visit_history_page/visit_history_page_widget.dart index 89a083a4..94e39606 100644 --- a/lib/pages/visit_history_page/visit_history_page_widget.dart +++ b/lib/pages/visit_history_page/visit_history_page_widget.dart @@ -236,7 +236,7 @@ class _VisitHistoryWidgetState extends State builder: (context) { return Dialog( alignment: Alignment.center, - child: buildDetails( + child: buildVisitDetails( visitaWrapItem, context, changeStatusAction, diff --git a/lib/shared/utils/validator_util.dart b/lib/shared/utils/validator_util.dart index 926930ea..662aaa39 100644 --- a/lib/shared/utils/validator_util.dart +++ b/lib/shared/utils/validator_util.dart @@ -20,10 +20,22 @@ class ValidatorUtil { } static String toISO8601(String format, String value) { + log('value: $value'); DateFormat dateFormat = DateFormat(format); DateTime dateTime = dateFormat.parse(value); - return dateTime.toIso8601String(); + return dateTime.toIso8601String() + 'Z'; + } + + static String toISO8601USA(String format, String value) { + log('value: $value'); + DateFormat dateFormat = DateFormat(format); + DateTime dateTime = dateFormat.parse(value); + String date = dateTime.toIso8601String() + 'Z'; + date = date.substring(0, 11) + '03:00:00.000Z'; + log('date: $date'); + + return date; } static String toLocalDateTime(String format, String value) { @@ -32,4 +44,13 @@ class ValidatorUtil { return DateFormat('dd/MM/yyyy HH:mm:ss').format(dateTime); } + + static String formatDateTimePicker(String dateTime) { + log('dateTime: $dateTime'); + List parts = dateTime.split(' '); + String datePart = parts[0]; + List dateParts = datePart.split('-'); + String formattedDate = '${dateParts[2]}/${dateParts[1]}/${dateParts[0]}'; + return formattedDate; + } } diff --git a/pubspec.lock b/pubspec.lock index 7ed3b0b7..34dfa4d3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -186,7 +186,7 @@ packages: source: hosted version: "0.3.4+2" crypto: - dependency: transitive + dependency: "direct main" description: name: crypto sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27 @@ -241,6 +241,22 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.1" + dio: + dependency: "direct main" + description: + name: dio + sha256: "5598aa796bbf4699afd5c67c0f5f6e2ed542afc956884b9cd58c306966efc260" + url: "https://pub.dev" + source: hosted + version: "5.7.0" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8" + url: "https://pub.dev" + source: hosted + version: "2.0.0" dropdown_button2: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 4f66d5e2..e5d8b014 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -97,6 +97,8 @@ dependencies: firebase_crashlytics: ^4.0.1 awesome_notifications: ^0.9.3+1 app_tracking_transparency: ^2.0.6 + dio: ^5.7.0 + crypto: ^3.0.5 dependency_overrides: http: 1.2.1