diff --git a/assets/fonts/SFPRODISPLAYBOLD.OTF b/assets/fonts/SFPRODISPLAYBOLD.OTF deleted file mode 100644 index 025b25c2..00000000 Binary files a/assets/fonts/SFPRODISPLAYBOLD.OTF and /dev/null differ diff --git a/assets/fonts/SFPRODISPLAYBOLD.otf b/assets/fonts/SFPRODISPLAYBOLD.otf deleted file mode 100644 index 025b25c2..00000000 Binary files a/assets/fonts/SFPRODISPLAYBOLD.otf and /dev/null differ diff --git a/assets/fonts/SFPRODISPLAYMEDIUM.OTF b/assets/fonts/SFPRODISPLAYMEDIUM.OTF deleted file mode 100644 index b2f7daca..00000000 Binary files a/assets/fonts/SFPRODISPLAYMEDIUM.OTF and /dev/null differ diff --git a/assets/fonts/SFPRODISPLAYMEDIUM.otf b/assets/fonts/SFPRODISPLAYMEDIUM.otf deleted file mode 100644 index b2f7daca..00000000 Binary files a/assets/fonts/SFPRODISPLAYMEDIUM.otf and /dev/null differ diff --git a/assets/fonts/SFPRODISPLAYREGULAR.OTF b/assets/fonts/SFPRODISPLAYREGULAR.OTF deleted file mode 100644 index 09aaca9f..00000000 Binary files a/assets/fonts/SFPRODISPLAYREGULAR.OTF and /dev/null differ diff --git a/assets/fonts/SFPRODISPLAYREGULAR.otf b/assets/fonts/SFPRODISPLAYREGULAR.otf deleted file mode 100644 index 09aaca9f..00000000 Binary files a/assets/fonts/SFPRODISPLAYREGULAR.otf and /dev/null differ diff --git a/lib/actions/actions.dart b/lib/actions/actions.dart index e5e99402..84e57a65 100644 --- a/lib/actions/actions.dart +++ b/lib/actions/actions.dart @@ -348,7 +348,6 @@ Future checkLocals({ Future showShare(payload) async { for (var i = 0; i < payload['convites'].length; i++) { - log('ADD'); await Share.share(''' Olá, \*${payload['convites'][i]['VTE_NOME']}\*! Você foi convidado para \*${AppState().local}\*. diff --git a/lib/app_state.dart b/lib/app_state.dart index 45bda94a..8d469e35 100644 --- a/lib/app_state.dart +++ b/lib/app_state.dart @@ -160,6 +160,9 @@ class AppState extends ChangeNotifier { await _safeInitAsync(() async { _whatsapp = await secureStorage.getBool('whatsapp') ?? _whatsapp; }); + await _safeInitAsync(() async { + _pets = await secureStorage.getBool('pets') ?? _pets; + }); await _safeInitAsync(() async { _haveLocal = await secureStorage.getBool('ff_have_local') ?? _haveLocal; }); @@ -188,6 +191,17 @@ class AppState extends ChangeNotifier { secureStorage.delete(key: 'ff_request_os_notification'); } + bool _pets = false; + bool get pets => _pets; + set pets(bool value) { + _pets = value; + secureStorage.setBool('pets', value); + } + + void deletePets() { + secureStorage.delete(key: 'pets'); + } + bool _whatsapp = false; bool get whatsapp => _whatsapp; diff --git a/lib/backend/api_requests/api_calls.dart b/lib/backend/api_requests/api_calls.dart index 5cef38f7..b79db3e0 100644 --- a/lib/backend/api_requests/api_calls.dart +++ b/lib/backend/api_requests/api_calls.dart @@ -49,8 +49,223 @@ class PhpGroup { static DeleteAccount deleteAccount = DeleteAccount(); static CancelaVisita cancelaVisita = CancelaVisita(); static BuscaEnconcomendas buscaEnconcomendas = BuscaEnconcomendas(); + static RegisterPet registerPet = RegisterPet(); + static DeletePet deletePet = DeletePet(); + static UpdatePet updatePet = UpdatePet(); + static GetPets getPets = GetPets(); + static GetPetPhoto getPetPhoto = GetPetPhoto(); } +class DeletePet { + Future call({ + String? devUUID = '', + String? userUUID = '', + String? cliID = '', + String? atividade = 'excluirPet', + int? petID = 0, + }) async { + final baseUrl = PhpGroup.getBaseUrl(); + + return ApiManager.instance.makeApiCall( + callName: 'deletePet', + apiUrl: '$baseUrl/processRequest.php', + callType: ApiCallType.POST, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + params: { + 'devUUID': devUUID, + 'userUUID': userUUID, + 'cliID': cliID, + 'atividade': atividade, + 'petId': petID, + }, + bodyType: BodyType.X_WWW_FORM_URL_ENCODED, + returnBody: true, + encodeBodyUtf8: false, + decodeUtf8: false, + cache: false, + isStreamingApi: false, + alwaysAllowBody: false, + ); + } +} + +class UpdatePet { + Future call({ + String? devUUID = '', + String? userUUID = '', + String? cliID = '', + String? atividade = 'atualizarPet', + int? petID = 0, + String? image = '', + String? name = '', + String? species = '', + String? breed = '', + String? color = '', + String? birthdayDate = '', + String? gender = '', + String? size = '', + String? notes = '', + }) async { + final baseUrl = PhpGroup.getBaseUrl(); + + return ApiManager.instance.makeApiCall( + callName: 'updatePet', + apiUrl: '$baseUrl/processRequest.php', + callType: ApiCallType.POST, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + params: { + 'devUUID': devUUID, + 'userUUID': userUUID, + 'cliID': cliID, + 'atividade': atividade, + 'id': petID, + 'image': image, + 'name': name, + 'species': species, + 'breed': breed, + 'color': color, + 'birthdayDate': ValidatorUtil.toISO8601USA('dd/MM/yyyy', birthdayDate!), + 'gender': gender, + 'size': size, + 'notes': notes, + }, + bodyType: BodyType.X_WWW_FORM_URL_ENCODED, + returnBody: true, + encodeBodyUtf8: false, + decodeUtf8: false, + cache: false, + isStreamingApi: false, + alwaysAllowBody: false, + ); + } +} + +class GetPets { + Future call({ + String? devUUID = '', + String? userUUID = '', + String? cliID = '', + String? atividade = 'consultaPets', + int? page = 0, + int? pageSize = 0, + }) async { + final baseUrl = PhpGroup.getBaseUrl(); + + return ApiManager.instance.makeApiCall( + callName: 'getPets', + apiUrl: '$baseUrl/processRequest.php', + callType: ApiCallType.POST, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + params: { + 'devUUID': devUUID, + 'userUUID': userUUID, + 'cliID': cliID, + 'atividade': atividade, + 'page': page, + 'pageSize': pageSize, + }, + bodyType: BodyType.X_WWW_FORM_URL_ENCODED, + returnBody: true, + encodeBodyUtf8: false, + decodeUtf8: false, + cache: false, + alwaysAllowBody: false, + ); + } +} + +class GetPetPhoto { + Future call({ + String? devUUID = '', + String? userUUID = '', + String? cliID = '', + String? atividade = 'consultaFotoPet', + int? petId = 0, + }) async { + final baseUrl = PhpGroup.getBaseUrl(); + + return ApiManager.instance.makeApiCall( + callName: 'getPetPhoto', + apiUrl: '$baseUrl/getImage.php', + callType: ApiCallType.POST, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + params: { + 'devUUID': devUUID, + 'userUUID': userUUID, + 'atividade': atividade, + 'cliID': cliID, + 'petId': petId, + }, + bodyType: BodyType.BLOB, + returnBody: true, + encodeBodyUtf8: false, + decodeUtf8: false, + cache: false, + isStreamingApi: false, + ); + } +} + +class RegisterPet { + Future call({ + String? devUUID = '', + String? userUUID = '', + String? cliID = '', + String? atividade = 'cadastrarPet', + String? image = '', + String? name = '', + String? species = '', + String? breed = '', + String? color = '', + String? birthdayDate = '', + String? gender = '', + String? size = '', + String? notes = '', + }) async { + final baseUrl = PhpGroup.getBaseUrl(); + + return ApiManager.instance.makeApiCall( + callName: 'registerPet', + apiUrl: '$baseUrl/processRequest.php', + callType: ApiCallType.POST, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + params: { + 'devUUID': devUUID, + 'userUUID': userUUID, + 'cliID': cliID, + 'atividade': atividade, + 'image': image, + 'name': name, + 'species': species, + 'breed': breed, + if (color != '') 'color': color, + if (birthdayDate != '') + 'birthdayDate': + ValidatorUtil.toISO8601USA('dd/MM/yyyy', birthdayDate!), + 'gender': gender, + 'size': size, + if (notes != '') 'notes': notes, + }, + bodyType: BodyType.X_WWW_FORM_URL_ENCODED, + returnBody: true, + encodeBodyUtf8: false, + decodeUtf8: false, + cache: false, + isStreamingApi: false, + alwaysAllowBody: false, + ); + } +} class BuscaEnconcomendas { Future call({ @@ -58,7 +273,6 @@ class BuscaEnconcomendas { String? userUUID = '', String? cliID = '', String? atividade = '', - String? page = '', String? pageSize = '', String? adresseeType = '', @@ -78,7 +292,6 @@ class BuscaEnconcomendas { 'userUUID': userUUID, 'atividade': atividade, 'cliID': cliID, - 'page': page, 'pageSize': pageSize, 'adresseeType': adresseeType, @@ -94,9 +307,9 @@ class BuscaEnconcomendas { } bool? error(dynamic response) => castToType(getJsonField( - response, - r'''$.error''', - )); + response, + r'''$.error''', + )); } class CancelaVisita { diff --git a/lib/backend/api_requests/api_manager.dart b/lib/backend/api_requests/api_manager.dart index 7c474720..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; @@ -513,8 +528,8 @@ class ApiManager { log('API Call: $callName'); log('URL: $apiUrl'); log('Headers: $headers'); - log('Params$params'); - log('Response${result.jsonBody}'); + log('Params: $params'); + log('Response: ${result.jsonBody}'); return result; } } diff --git a/lib/components/atomic_components/shared_components_atoms/appbar.dart b/lib/components/atomic_components/shared_components_atoms/appbar.dart new file mode 100644 index 00000000..d8be68d9 --- /dev/null +++ b/lib/components/atomic_components/shared_components_atoms/appbar.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:hub/flutter_flow/flutter_flow_icon_button.dart'; +import 'package:hub/flutter_flow/flutter_flow_theme.dart'; + +class AppBarUtil extends StatelessWidget implements PreferredSizeWidget { + final String title; + final VoidCallback? onBackButtonPressed; + final Widget? actionButton; + + const AppBarUtil({ + Key? key, + required this.title, + this.onBackButtonPressed, + this.actionButton, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return AppBar( + backgroundColor: FlutterFlowTheme.of(context).primaryBackground, + automaticallyImplyLeading: false, + forceMaterialTransparency: true, + elevation: 0.0, + leading: FlutterFlowIconButton( + borderColor: Colors.transparent, + borderRadius: 30.0, + borderWidth: 1.0, + buttonSize: 60.0, + icon: Icon( + Icons.keyboard_arrow_left, + color: FlutterFlowTheme.of(context).primaryText, + size: 30.0, + ), + onPressed: onBackButtonPressed ?? () => Navigator.of(context).pop(), + ), + title: Text( + title, + style: FlutterFlowTheme.of(context).headlineMedium.override( + fontFamily: 'Nunito', + color: FlutterFlowTheme.of(context).primaryText, + fontSize: 15.0, + letterSpacing: 0.0, + fontWeight: FontWeight.bold, + useGoogleFonts: GoogleFonts.asMap().containsKey('Nunito'), + ), + ), + actions: [ + if (actionButton != null) actionButton!, + ], + centerTitle: true, + ); + } + + @override + Size get preferredSize => const Size.fromHeight(kToolbarHeight); +} diff --git a/lib/components/atomic_components/shared_components_atoms/custom_datepicker.dart b/lib/components/atomic_components/shared_components_atoms/custom_datepicker.dart new file mode 100644 index 00000000..78732aa3 --- /dev/null +++ b/lib/components/atomic_components/shared_components_atoms/custom_datepicker.dart @@ -0,0 +1,273 @@ +import 'dart:developer'; + +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_util.dart'; + +class CustomDatePickerUtil extends StatefulWidget { + TextEditingController? controller; + final FocusNode? focusNode; + final String hintText; + final String dateFormat; + final String locale; + final DateTime? initialDate; + final DateTime firstDate; + final DateTime? lastDate; + final bool timePicker; + final FormFieldValidator? validator; + + CustomDatePickerUtil({ + Key? key, + this.controller, + this.focusNode, + required this.hintText, + required this.dateFormat, + required this.locale, + required this.timePicker, + this.initialDate, + required this.firstDate, + this.lastDate, + this.validator, + }) : super(key: key); + + @override + _CustomDatePickerState createState() => _CustomDatePickerState(); +} + +class _CustomDatePickerState extends State { + DateTime? _selectedDate; + TimeOfDay? _selectedTime; + + @override + void initState() { + super.initState(); + _selectedDate = widget.initialDate; + } + + @override + void dispose() { + super.dispose(); + } + + _selectDate(BuildContext context) async { + final pickedDate = await showDatePicker( + context: context, + initialDate: getCurrentTimestamp, + firstDate: widget.firstDate, + lastDate: widget.lastDate ?? DateTime(2100), + 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) { + if (widget.timePicker == true) { + final TimeOfDay? pickedTime = await showTimePicker( + context: context, + initialTime: TimeOfDay.fromDateTime(_selectedDate ?? DateTime.now()), + builder: (context, child) { + return wrapInMaterialTimePickerTheme( + 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).info, + selectedDateTimeBackgroundColor: + FlutterFlowTheme.of(context).primary, + selectedDateTimeForegroundColor: + FlutterFlowTheme.of(context).info, + pickerDialForegroundColor: + FlutterFlowTheme.of(context).primaryText, + actionButtonForegroundColor: + FlutterFlowTheme.of(context).primaryText, + iconSize: 24.0, + ); + }, + ); + + if (pickedTime != null) { + setState(() { + _selectedDate = DateTime( + pickedDate.year, + pickedDate.month, + pickedDate.day, + pickedTime.hour, + pickedTime.minute, + ); + widget.controller?.text = dateTimeFormat( + widget.dateFormat, + _selectedDate, + locale: widget.locale, + ); + widget.controller?.selection = TextSelection.collapsed( + offset: widget.controller!.text.length, + ); + }); + } + } else { + setState(() { + _selectedDate = DateTime( + pickedDate.year, + pickedDate.month, + pickedDate.day, + ); + widget.controller = TextEditingController( + text: dateTimeFormat( + widget.dateFormat, + _selectedDate, + locale: widget.locale, + )); + widget.controller?.selection = TextSelection.collapsed( + offset: widget.controller!.text.length, + ); + }); + } + } + } + + @override + Widget build(BuildContext context) { + return 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: widget.controller, + focusNode: widget.focusNode, + 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: widget.hintText, + 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: widget.validator, + ), + ), + 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: () => _selectDate(context), + child: Container( + width: double.infinity, + height: 80.0, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.0), + ), + ), + ), + ), + ], + ), + ), + ], + ); + } +} diff --git a/lib/components/atomic_components/shared_components_atoms/custom_input.dart b/lib/components/atomic_components/shared_components_atoms/custom_input.dart new file mode 100644 index 00000000..d7b724d6 --- /dev/null +++ b/lib/components/atomic_components/shared_components_atoms/custom_input.dart @@ -0,0 +1,142 @@ +import 'package:easy_debounce/easy_debounce.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:hub/components/atomic_components/shared_components_atoms/tabview.dart'; +import 'package:hub/flutter_flow/flutter_flow_theme.dart'; +import 'package:hub/flutter_flow/flutter_flow_util.dart'; + +class CustomInputUtil extends StatefulWidget { + final TextEditingController? controller; + final String? labelText; + final String? hintText; + final bool? obscureText; + final IconData? suffixIcon; + final bool autoFocus; + FocusNode? focusNode; + final TextInputAction textInputAction; + final TextInputType keyboardType; + final int maxLength; + final String? Function(String?)? validator; + final bool haveMaxLength; + final void Function(String)? onChanged; + + CustomInputUtil( + {Key? key, + this.controller, + required this.labelText, + required this.hintText, + required this.suffixIcon, + this.autoFocus = false, + required this.focusNode, + this.onChanged, + this.textInputAction = TextInputAction.next, + this.keyboardType = TextInputType.text, + this.maxLength = 80, + this.validator, + this.obscureText, + required this.haveMaxLength}) + : super(key: key); + + @override + State createState() => _CustomInputUtilState(); +} + +class _CustomInputUtilState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsetsDirectional.fromSTEB(24.0, 0.0, 24.0, 10.0), + child: Column( + children: [ + TextFormField( + controller: widget.controller, + autovalidateMode: AutovalidateMode.onUserInteraction, + validator: widget.validator, + autofocus: widget.autoFocus, + focusNode: widget.focusNode, + onChanged: (_) => EasyDebounce.debounce( + '${widget.controller}', + const Duration(milliseconds: 500), + () => setState(() {}), + ), + textInputAction: widget.textInputAction, + obscureText: false, + decoration: InputDecoration( + isDense: true, + labelText: widget.labelText, + 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: widget.hintText, + hintStyle: FlutterFlowTheme.of(context).labelMedium.override( + fontFamily: FlutterFlowTheme.of(context).labelMediumFamily, + color: FlutterFlowTheme.of(context).primaryText, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context).labelMediumFamily, + ), + ), + enabledBorder: OutlineInputBorder( + 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( + widget.suffixIcon, + color: FlutterFlowTheme.of(context).accent1, + ), + ), + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily, + color: FlutterFlowTheme.of(context).primaryText, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context).bodyMediumFamily, + ), + ), + maxLines: null, + maxLength: widget.haveMaxLength ? widget.maxLength : null, + keyboardType: widget.keyboardType, + inputFormatters: [ + LengthLimitingTextInputFormatter(widget.maxLength), + ], + ), + ], + ), + ); + } +} diff --git a/lib/components/atomic_components/shared_components_atoms/custom_select.dart b/lib/components/atomic_components/shared_components_atoms/custom_select.dart new file mode 100644 index 00000000..89396d14 --- /dev/null +++ b/lib/components/atomic_components/shared_components_atoms/custom_select.dart @@ -0,0 +1,140 @@ +import 'dart:developer'; +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:hub/components/organism_components/message_well_component/message_well_component_widget.dart'; +import 'package:hub/flutter_flow/flutter_flow_drop_down.dart'; +import 'package:hub/flutter_flow/flutter_flow_theme.dart'; +import 'package:hub/flutter_flow/flutter_flow_util.dart'; +import 'package:hub/flutter_flow/flutter_flow_widgets.dart'; +import 'package:hub/flutter_flow/form_field_controller.dart'; + +class CustomSelect extends StatefulWidget { + final List options; + final List optionsLabel; + final String hintText; + final bool isMultiSelect; + FormFieldController controller; + dynamic Function(String?)? changed; + String? dropDownValue; + bool isRequired; + + CustomSelect({ + Key? key, + required this.options, + required this.optionsLabel, + required this.hintText, + required this.controller, + required this.changed, + this.isRequired = false, + this.dropDownValue, + this.isMultiSelect = false, + }) : super(key: key); + + @override + _CustomSelectState createState() => _CustomSelectState(); +} + +class _CustomSelectState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsetsDirectional.fromSTEB(0, 0.0, 0, 10.0), + child: Column( + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Expanded( + child: Padding( + padding: const EdgeInsetsDirectional.fromSTEB( + 24.0, 0.0, 24.0, 0.0), + child: Container( + width: 100.0, + height: 48.0, + decoration: const BoxDecoration(), + child: FlutterFlowDropDown( + fillColor: FlutterFlowTheme.of(context).primaryBackground, + controller: widget.controller ??= + FormFieldController( + widget.dropDownValue ??= ''), + options: widget.options, + optionLabels: widget.optionsLabel, + onChanged: widget.changed, + isMultiSelect: false, + width: double.infinity, + height: double.infinity, + textStyle: FlutterFlowTheme.of(context) + .bodyMedium + .override( + fontFamily: + FlutterFlowTheme.of(context).bodyMediumFamily, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context).bodyMediumFamily), + ), + hintText: widget.hintText, + icon: Icon( + Icons.keyboard_arrow_down_rounded, + color: FlutterFlowTheme.of(context).accent1, + size: 24.0, + ), + elevation: 2.0, + borderColor: FlutterFlowTheme.of(context).customColor6, + borderWidth: 0.5, + borderRadius: 10.0, + margin: const EdgeInsetsDirectional.fromSTEB( + 12.0, 0.0, 16.0, 0.0), + hidesUnderline: true, + isOverButton: true, + isSearchable: false, + ), + ), + ), + ), + ], + ), + if (widget.isRequired) + if (widget.dropDownValue == null || widget.dropDownValue == '') + Padding( + padding: + const EdgeInsetsDirectional.fromSTEB(24.0, 0.0, 24.0, 0.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: + const EdgeInsetsDirectional.only(top: 5, start: 15), + child: Text( + FFLocalizations.of(context).getVariableText( + enText: 'This field is required', + ptText: 'Este campo é obrigatório', + ), + style: FlutterFlowTheme.of(context) + .bodySmall + .override( + fontFamily: FlutterFlowTheme.of(context) + .bodySmallFamily, + color: + FlutterFlowTheme.of(context).customColor6, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context) + .bodySmallFamily), + )), + ), + ], + ), + ), + ], + ), + ); + } +} 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 new file mode 100644 index 00000000..719d4efb --- /dev/null +++ b/lib/components/atomic_components/shared_components_atoms/media_upload_button.dart @@ -0,0 +1,178 @@ +import 'dart:developer'; +import 'dart:typed_data'; + +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/upload_data.dart'; +import 'package:hub/flutter_flow/uploaded_file.dart'; + +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, + this.uploadedFiles}) + : super(key: key); + + @override + State createState() => _MediaUploadButtonUtilState(); +} + +class _MediaUploadButtonUtilState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsetsDirectional.fromSTEB(24.0, 0.0, 24.0, 0.0), + child: Builder( + builder: (context) { + if (widget.uploadedFiles != null && + widget.uploadedFiles!.bytes!.isNotEmpty) { + { + return InkWell( + splashColor: Colors.transparent, + focusColor: Colors.transparent, + hoverColor: Colors.transparent, + highlightColor: Colors.transparent, + onTap: () async { + setState(() { + widget.isUploading = false; + widget.uploadedFiles = + FFUploadedFile(bytes: Uint8List.fromList([])); + widget.onUploadComplete(widget.uploadedFiles!); + }); + }, + child: ClipRRect( + borderRadius: BorderRadius.circular(8.0), + child: Image.memory( + widget.uploadedFiles!.bytes ?? Uint8List.fromList([]), + width: 300.0, + height: 200.0, + fit: BoxFit.cover, + ), + ), + ); + } + } else { + return Stack( + children: [ + Align( + alignment: const AlignmentDirectional(0.0, 0.0), + child: FFButtonWidget( + onPressed: () async { + final selectedMedia = + await selectMediaWithSourceBottomSheet( + context: context, + imageQuality: 100, + allowPhoto: true, + includeDimensions: true, + ); + if (selectedMedia != null) { + setState(() => widget.isUploading = true); + var selectedUploadedFiles = []; + + try { + showUploadMessage( + context, + 'Uploading file...', + showLoading: true, + ); + selectedUploadedFiles = selectedMedia + .map((m) => FFUploadedFile( + name: m.storagePath.split('/').last, + bytes: m.bytes, + height: m.dimensions?.height, + width: m.dimensions?.width, + // blurHash: m.blurHash, + )) + .toList(); + } finally { + ScaffoldMessenger.of(context).hideCurrentSnackBar(); + widget.isUploading = false; + } + if (selectedUploadedFiles.length == + selectedMedia.length) { + setState(() { + widget.uploadedFiles = selectedUploadedFiles.first; + }); + widget.onUploadComplete(widget.uploadedFiles!); + + showUploadMessage(context, 'Success!'); + } else { + setState(() {}); + showUploadMessage(context, 'Failed to upload data'); + return; + } + } + }, + text: '', + icon: Icon( + Icons.photo_camera, + color: FlutterFlowTheme.of(context).accent1, + size: 30.0, + ), + options: FFButtonOptions( + width: double.infinity, + height: 120.0, + padding: const EdgeInsetsDirectional.fromSTEB( + 0.0, 0.0, 0.0, 20.0), + iconPadding: const EdgeInsetsDirectional.fromSTEB( + 14.0, 0.0, 0.0, 20.0), + color: FlutterFlowTheme.of(context).primaryBackground, + textStyle: FlutterFlowTheme.of(context) + .titleSmall + .override( + fontFamily: + FlutterFlowTheme.of(context).titleSmallFamily, + color: FlutterFlowTheme.of(context).primaryText, + fontSize: 16.0, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context).titleSmallFamily), + ), + borderSide: BorderSide( + color: FlutterFlowTheme.of(context).accent1, + width: 0.2, + ), + borderRadius: BorderRadius.circular(8.0), + ), + ), + ), + Align( + alignment: const AlignmentDirectional(0.0, 0.0), + child: Padding( + padding: const EdgeInsetsDirectional.fromSTEB( + 10.0, 65.0, 10.0, 0.0), + child: Text( + widget.labelText, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: + FlutterFlowTheme.of(context).bodyMediumFamily, + color: FlutterFlowTheme.of(context).primaryText, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context).bodyMediumFamily), + ), + ), + ), + ), + ], + ); + } + }, + ), + ); + } +} diff --git a/lib/components/atomic_components/shared_components_atoms/submit_button.dart b/lib/components/atomic_components/shared_components_atoms/submit_button.dart new file mode 100644 index 00000000..c970dca3 --- /dev/null +++ b/lib/components/atomic_components/shared_components_atoms/submit_button.dart @@ -0,0 +1,49 @@ +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:json_path/fun_sdk.dart'; + +class SubmitButtonUtil extends StatelessWidget { + final String labelText; + Future Function()? onPressed; + + SubmitButtonUtil({ + required this.labelText, + required this.onPressed, + }); + + @override + Widget build(BuildContext context) { + return FFButtonWidget( + onPressed: onPressed, + text: labelText, + options: FFButtonOptions( + width: 250.0, + height: 36.0, + disabledColor: FlutterFlowTheme.of(context).customColor5, + padding: const EdgeInsetsDirectional.fromSTEB(80.0, 0.0, 80.0, 0.0), + iconPadding: const EdgeInsetsDirectional.fromSTEB(0.0, 0.0, 0.0, 0.0), + color: FlutterFlowTheme.of(context).primary, + textStyle: FlutterFlowTheme.of(context).titleSmall.override( + fontFamily: FlutterFlowTheme.of(context).titleSmallFamily, + color: FlutterFlowTheme.of(context).info, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap() + .containsKey(FlutterFlowTheme.of(context).titleSmallFamily), + ), + borderSide: const BorderSide( + color: Colors.transparent, + width: 30.0, + ), + borderRadius: const BorderRadius.only( + bottomLeft: Radius.circular(15.0), + bottomRight: Radius.circular(15.0), + topLeft: Radius.circular(15.0), + topRight: Radius.circular(15.0), + ), + ), + ); + } +} diff --git a/lib/components/atomic_components/shared_components_atoms/tabview.dart b/lib/components/atomic_components/shared_components_atoms/tabview.dart new file mode 100644 index 00000000..422c3d60 --- /dev/null +++ b/lib/components/atomic_components/shared_components_atoms/tabview.dart @@ -0,0 +1,79 @@ +import 'dart:developer'; + +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:hub/flutter_flow/flutter_flow_theme.dart'; + +class TabViewUtil extends StatelessWidget { + final BuildContext context; + final dynamic model; + String labelTab1; + String labelTab2; + final TabController controller; + final Function(bool) onEditingChanged; + Widget widget1; + Widget widget2; + + TabViewUtil({ + super.key, + required this.onEditingChanged, + required this.context, + required this.model, + required this.labelTab1, + required this.labelTab2, + required this.controller, + required this.widget1, + required this.widget2, + }); + + @override + Widget build(BuildContext context) { + return SafeArea( + top: true, + child: Column( + children: [ + Align( + alignment: const Alignment(0.0, 0), + child: TabBar( + labelColor: FlutterFlowTheme.of(context).primaryText, + unselectedLabelColor: FlutterFlowTheme.of(context).primaryText, + labelStyle: FlutterFlowTheme.of(context).titleMedium.override( + fontFamily: FlutterFlowTheme.of(context).titleMediumFamily, + fontSize: 13.0, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context).titleMediumFamily), + ), + unselectedLabelStyle: const TextStyle(), + indicatorColor: FlutterFlowTheme.of(context).primary, + padding: const EdgeInsets.all(4.0), + tabs: [ + Tab( + text: labelTab1, + ), + Tab( + text: labelTab2, + ), + ], + controller: controller, + onTap: (i) async { + if (i == 1) onEditingChanged(false); + [() async {}, () async {}][i](); + }, + ), + ), + Expanded( + child: TabBarView( + controller: controller, + children: [ + widget1, + widget2, + ], + ), + ), + // Adicione o conteúdo do TabBarView aqui + ], + ), + ); + } +} diff --git a/lib/components/organism_components/menu_component/menu_component_model.dart b/lib/components/organism_components/menu_component/menu_component_model.dart index 4d6abf72..b92b303c 100644 --- a/lib/components/organism_components/menu_component/menu_component_model.dart +++ b/lib/components/organism_components/menu_component/menu_component_model.dart @@ -356,4 +356,22 @@ class MenuComponentModel extends FlutterFlowModel { }, ); } + + Future petsAction(BuildContext context) async { + bool isPet = AppState().pets; + if (isPet) { + context.push( + '/petsPage', + extra: { + kTransitionInfoKey: const TransitionInfo( + hasTransition: true, + transitionType: PageTransitionType.scale, + alignment: Alignment.bottomCenter, + ), + }, + ); + } else { + DialogUnavailable.unavailableFeature(context); + } + } } diff --git a/lib/components/organism_components/menu_component/menu_component_widget.dart b/lib/components/organism_components/menu_component/menu_component_widget.dart index 35a6e9d7..fd4ea8e5 100644 --- a/lib/components/organism_components/menu_component/menu_component_widget.dart +++ b/lib/components/organism_components/menu_component/menu_component_widget.dart @@ -89,6 +89,17 @@ class _MenuComponentWidgetState extends State { ptText: 'QRCode\nde Acesso', ), ), + MenuButtonWidget( + icon: Icons.pets, + action: () async { + await _model.petsAction(context); + setState(() {}); + }, + title: FFLocalizations.of(context).getVariableText( + enText: 'Pets\nRegister', + ptText: 'Cadastro\nde Pet', + ), + ), MenuButtonWidget( icon: Icons.people, action: () async { @@ -213,6 +224,17 @@ class _MenuComponentWidgetState extends State { ptText: 'QRCode\nde Acesso', ), ), + MenuButtonWidget( + icon: Icons.pets, + action: () async { + await _model.petsAction(context); + setState(() {}); + }, + title: FFLocalizations.of(context).getVariableText( + enText: 'Pets\nRegister', + ptText: 'Cadastrar\nPet', + ), + ), MenuButtonWidget( icon: Icons.transfer_within_a_station_outlined, action: () async { @@ -338,6 +360,17 @@ class _MenuComponentWidgetState extends State { ptText: 'Encomendas', ), ), + MenuButtonWidget( + icon: Icons.pets, + action: () async { + await _model.petsAction(context); + setState(() {}); + }, + title: FFLocalizations.of(context).getVariableText( + enText: 'Pets\nRegister', + ptText: 'Cadastrar\nPet', + ), + ), MenuCardItem( icon: Icons.event_available, action: () async { @@ -452,6 +485,17 @@ class _MenuComponentWidgetState extends State { ptText: 'QRCode de Acesso', ), ), + MenuCardItem( + icon: Icons.pets, + action: () async { + await _model.petsAction(context); + setState(() {}); + }, + title: FFLocalizations.of(context).getVariableText( + enText: 'Pets Register', + ptText: 'Cadastro de Pet', + ), + ), MenuCardItem( icon: Icons.transfer_within_a_station_outlined, action: () async { 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 be15f8a0..ae40af8f 100644 --- a/lib/flutter_flow/nav/nav.dart +++ b/lib/flutter_flow/nav/nav.dart @@ -6,6 +6,7 @@ import 'package:hub/pages/delivery_schedule_page/delivery_schedule_widget.dart'; import 'package:hub/pages/fast_pass_page/fast_pass_page_widget.dart'; import 'package:hub/pages/message_history_page/message_history_page_widget.dart'; import 'package:hub/pages/package_order_page/package_order_page.dart'; +import 'package:hub/pages/pets_page/pets_page_widget.dart'; import 'package:hub/pages/provisional_schedule_page/provisional_schedule_widget.dart'; import 'package:hub/pages/reception_page/reception_page_widget.dart'; import 'package:hub/pages/reservation_page/reservation_page_widget.dart'; @@ -201,6 +202,20 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) => GoRouter( path: '/reservation', builder: (context, params) => ReservationPageWidget(), ), + FFRoute( + name: 'petsPage', + path: '/petsPage', + builder: (context, params) { + final pet = params.getParam( + 'pet', + ParamType.JSON, + ); + + return PetsPageWidget( + pet: pet, + ); + }, + ), // FFRoute( // name: 'settingsPage', // path: '/settingsPage', diff --git a/lib/pages/home_page/home_page_widget.dart b/lib/pages/home_page/home_page_widget.dart index fbc81b98..5e2f88c5 100644 --- a/lib/pages/home_page/home_page_widget.dart +++ b/lib/pages/home_page/home_page_widget.dart @@ -1,10 +1,6 @@ import 'dart:async'; import 'dart:developer'; -import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:hub/backend/notifications/notification_service.dart'; -import 'package:hub/shared/utils/log_util.dart'; - import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -38,6 +34,79 @@ class _HomePageWidgetState extends State { LocalProfileComponentWidget(showBottomSheet: showModalSelectLocal); } + Future processData() async { + try { + var response = await PhpGroup.getDadosCall.call( + devUUID: AppState().devUUID, + userUUID: AppState().userUUID, + cliUUID: AppState().cliUUID, + atividade: 'getDados', + ); + + final error = response.jsonBody['error']; + final errorMsg = response.jsonBody['error_msg']; + + if (error == false) { + log(response.jsonBody.toString()); + AppState().whatsapp = response.jsonBody['whatsapp'] ?? false; + AppState().provisional = response.jsonBody['provisional'] ?? false; + AppState().pets = response.jsonBody['pet'] ?? false; + AppState().name = response.jsonBody['visitado']['VDO_NOME']; + safeSetState(() {}); + return; + } + + DialogUtil.warningDefault(context).whenComplete(() => processLocals()); + safeSetState(() {}); + return; + } catch (e, s) { + DialogUtil.warningDefault(context).whenComplete(() => processLocals()); + } + } + + Future processLocals() async { + try { + var response = await PhpGroup.getLocalsCall.call( + devUUID: AppState().devUUID, + userUUID: AppState().userUUID, + ); + + List locals = response.jsonBody['locais'] ?? []; + + final activeLocals = + locals.where((local) => local['CLU_STATUS'] == 'A').toList(); + + if (activeLocals.isEmpty || AppState().cliUUID.isEmpty) { + await showModalSelectLocal(); + } else { + await processData(); + } + } catch (e) { + await showModalSelectLocal(); + } + } + + Future showModalSelectLocal() async { + await showModalBottomSheet( + isScrollControlled: true, + backgroundColor: Colors.transparent, + enableDrag: false, + isDismissible: false, + context: context, + builder: (context) => Padding( + padding: MediaQuery.viewInsetsOf(context), + child: const BottomArrowLinkedLocalsComponentWidget(), + ), + ).then((_) async { + _model.updatePage(() => safeSetState(() { + _localProfileComponentWidget = LocalProfileComponentWidget( + showBottomSheet: showModalSelectLocal); + })); + + await processData(); + }); + } + @override void dispose() { super.dispose(); diff --git a/lib/pages/liberation_history/liberation_history_widget.dart b/lib/pages/liberation_history/liberation_history_widget.dart index 57852e34..25975d1c 100644 --- a/lib/pages/liberation_history/liberation_history_widget.dart +++ b/lib/pages/liberation_history/liberation_history_widget.dart @@ -240,7 +240,6 @@ class _LiberationHistoryWidgetState extends State { id: liberationHistoryItem['VTE_ID'].toString(), ) .then((value) { - log('test: $value'); if (value) { showSnackbar( context, 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 new file mode 100644 index 00000000..9e7a18a5 --- /dev/null +++ b/lib/pages/pets_page/pets_page_model.dart @@ -0,0 +1,246 @@ +import 'dart:developer'; +import 'package:flutter/material.dart'; +import 'package:hub/backend/api_requests/api_calls.dart'; +import 'package:hub/backend/api_requests/api_manager.dart'; +import 'package:hub/flutter_flow/flutter_flow_util.dart'; +import 'package:hub/flutter_flow/form_field_controller.dart'; +import 'package:hub/pages/pets_page/pets_page_widget.dart'; +import 'package:hub/shared/utils/dialog_util.dart'; +import 'package:hub/shared/utils/log_util.dart'; +import '/custom_code/actions/index.dart' as actions; + +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; + String? imgBase64; + + // Controller para o DropDown + String? dropDownValue1; + FormFieldController? dropDownValueController1; + + String? dropDownValue2; + FormFieldController? dropDownValueController2; + + // Controller para o TextField + FocusNode? textFieldFocusName; + TextEditingController? textControllerName; + String? textControllerNameValidator(BuildContext context, String? val) { + if (val == null || val.isEmpty) { + return FFLocalizations.of(context).getVariableText( + enText: 'This field is required', + ptText: 'Este campo é obrigatório', + ); + } + return null; + } + + FocusNode? textFieldFocusSpecies; + TextEditingController? textControllerSpecies; + String? textControllerSpeciesValidator(BuildContext context, String? val) { + if (val == null || val.isEmpty) { + return FFLocalizations.of(context).getVariableText( + enText: 'This field is required', + ptText: 'Este campo é obrigatório', + ); + } + return null; + } + + FocusNode? textFieldFocusRace; + TextEditingController? textControllerRace; + String? textControllerRaceValidator(BuildContext context, String? val) { + if (val == null || val.isEmpty) { + return FFLocalizations.of(context).getVariableText( + enText: 'This field is required', + ptText: 'Este campo é obrigatório', + ); + } + return null; + } + + FocusNode? textFieldFocusColor; + TextEditingController? textControllerColor; + String? Function(BuildContext, String?)? textControllerColorValidator; + + DateTime? selectedDate; + FocusNode? textFieldFocusData; + TextEditingController? textControllerData; + String? textControllerDataValidator(BuildContext context, String? val) { + return null; + } + + FocusNode? textFieldFocusObservation; + TextEditingController? textControllerObservation; + String? Function(BuildContext, String?)? textControllerObservationValidator; + + @override + void initState(BuildContext context) { + // Se liga! Esse é o controller do TabBar + tabBarController = TabController( + vsync: Navigator.of(context), + length: 2, + ); + + textFieldFocusName = FocusNode(); + textControllerName = TextEditingController(); + // textControllerNameValidator = + // (context, value) => _textControllerNameValidator(context, value); + + textFieldFocusSpecies = FocusNode(); + textControllerSpecies = TextEditingController(); + + textFieldFocusRace = FocusNode(); + textControllerRace = TextEditingController(); + + textFieldFocusColor = FocusNode(); + textControllerColor = TextEditingController(); + + textFieldFocusData = FocusNode(); + textControllerData = TextEditingController( + text: dateTimeFormat( + 'dd/MM/yyyy', + DateTime.now(), + )); + + textFieldFocusObservation = FocusNode(); + textControllerObservation = TextEditingController(); + + dropDownValueController1 = + FormFieldController(dropDownValue1 ??= 'Selecione uma opção'); + + dropDownValueController2 = + FormFieldController(dropDownValue2 ??= 'Selecione uma opção'); + } + + @override + void dispose() { + tabBarController.dispose(); + + textFieldFocusName?.dispose(); + textControllerName?.dispose(); + + textFieldFocusSpecies?.dispose(); + textControllerSpecies?.dispose(); + + textFieldFocusRace?.dispose(); + textControllerRace?.dispose(); + + textFieldFocusColor?.dispose(); + textControllerColor?.dispose(); + + textFieldFocusData?.dispose(); + textControllerData?.dispose(); + + textFieldFocusObservation?.dispose(); + textControllerObservation?.dispose(); + + dropDownValueController1?.dispose(); + dropDownValueController2?.dispose(); + } + + // Validador do formulário + bool isFormValid(BuildContext context) { + if (uploadedLocalFile == null) { + return false; + } + if (textControllerName.text.isEmpty || + textControllerName.text.length > 80 || + textControllerName.text == '') { + return false; + } + if (textControllerSpecies.text.isEmpty || + textControllerSpecies.text == '') { + return false; + } + if (textControllerRace.text.isEmpty || textControllerRace.text == '') { + return false; + } + + if (dropDownValue1 == null || + dropDownValue1!.isEmpty || + dropDownValue1 == '') { + return false; + } + if (dropDownValue2 == null) { + return false; + } + 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) { + 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 updated', + ptText: 'Pet atualizado com sucesso', + )); + }).catchError((error) { + log(error.toString()); + DialogUtil.errorDefault(buildContext!); + }); + } + + Future registerPet() async { + 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 new file mode 100644 index 00000000..c9542b06 --- /dev/null +++ b/lib/pages/pets_page/pets_page_widget.dart @@ -0,0 +1,1092 @@ +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/components/atomic_components/shared_components_atoms/appbar.dart'; +import 'package:hub/components/atomic_components/shared_components_atoms/custom_datepicker.dart'; +import 'package:hub/components/atomic_components/shared_components_atoms/custom_input.dart'; +import 'package:hub/components/atomic_components/shared_components_atoms/custom_select.dart'; +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'; +import 'package:hub/flutter_flow/flutter_flow_widgets.dart'; +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'; +import 'package:hub/shared/utils/validator_util.dart'; +import 'package:material_symbols_icons/symbols.dart'; +import 'package:sqflite/sqflite.dart'; +import '/custom_code/actions/index.dart' as actions; + +class PetsPageWidget extends StatefulWidget { + dynamic pet; + + PetsPageWidget({ + super.key, + this.pet, + }); + + @override + State createState() => _PetsPageWidgetState(); +} + +class _PetsPageWidgetState extends State + with SingleTickerProviderStateMixin { + late PetsPageModel _model; + final _formKey = GlobalKey(); + bool isEditing = false; + + @override + void initState() { + super.initState(); + _model = PetsPageModel(); + _model.tabBarController = TabController(length: 2, vsync: this); + + 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( + text: widget.pet != null ? widget.pet['species'] : ''); + _model.textFieldFocusSpecies ??= FocusNode(); + + _model.textControllerRace ??= TextEditingController( + text: widget.pet != null ? widget.pet['breed'] : ''); + _model.textFieldFocusRace ??= FocusNode(); + + _model.textControllerColor ??= TextEditingController( + text: widget.pet != null ? widget.pet['color'] : ''); + _model.textFieldFocusColor ??= FocusNode(); + + _model.textControllerData ??= TextEditingController( + text: widget.pet != null + ? ValidatorUtil.formatDateTimePicker(widget.pet['birthdayDate']) + : ''); + _model.textFieldFocusData ??= FocusNode(); + + _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 ??= ''); + + _model.dropDownValueController2 ??= + FormFieldController(_model.dropDownValue2 ??= ''); + } + + @override + void dispose() { + super.dispose(); + _model.dispose(); + } + + void _handleUploadComplete(FFUploadedFile uploadedFile) { + setState(() { + _model.uploadedLocalFile = uploadedFile; + }); + } + + void _handleEditingChanged(bool editing) { + setState(() { + isEditing = editing; + _model.uploadedLocalFile = null; + _model.textControllerName.text = ''; + _model.textControllerSpecies.text = ''; + _model.textControllerRace.text = ''; + _model.textControllerColor.text = ''; + _model.textControllerData.text = ''; + _model.textControllerObservation.text = ''; + _model.dropDownValueController1 = FormFieldController(''); + _model.dropDownValueController2 = FormFieldController(''); + }); + } + + @override + Widget build(BuildContext context) { + _model.buildContext = context; + return Scaffold( + appBar: _buildAppBar(context), + backgroundColor: FlutterFlowTheme.of(context).primaryBackground, + body: _buildTabView(context)); + } + + PreferredSizeWidget _buildAppBar(BuildContext context) { + return AppBarUtil(title: 'Pets', onBackButtonPressed: () => context.pop()); + } + + Widget _buildTabView(BuildContext context) { + return TabViewUtil( + context: context, + model: _model, + labelTab1: isEditing + ? FFLocalizations.of(context) + .getVariableText(ptText: 'Editar', enText: 'Edit') + : FFLocalizations.of(context) + .getVariableText(ptText: 'Cadastrar', enText: 'Register'), + labelTab2: FFLocalizations.of(context) + .getVariableText(ptText: 'Consultar', enText: 'History'), + controller: _model.tabBarController, + widget1: + isEditing ? _buildEditForm(context) : _buildRegisterForm(context), + widget2: PetsHistoryScreen(), + onEditingChanged: _handleEditingChanged, + ); + } + + Widget _buildRegisterForm(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: Symbols.format_color_text, + 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: 'Ex: Cachorro, Gato, Papagaio', + enText: 'e.g. Dog, Cat, Parrot'), + suffixIcon: Symbols.sound_detection_dog_barking, + 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: 'Ex: Labrador, Poodle, Siamês, Persa', + enText: 'e.g. Labrador, Poodle, Siamese, Persian'), + suffixIcon: Icons.pets_outlined, + 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: 'Ex: Preto, Amarelo, Branco', + enText: 'e.g. Black, Yellow, White'), + suffixIcon: Symbols.palette, + 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(() { + _model.selectedDate = DateTime( + pickedDate.year, + pickedDate.month, + pickedDate.day, + ); + + _model.textControllerData = + TextEditingController( + text: dateTimeFormat( + 'dd/MM/yyyy', + _model.selectedDate, + locale: FFLocalizations.of(context) + .languageCode, + )); + _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: 250, + ), + 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: Symbols.format_color_text, + 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: 'Ex: Cachorro, Gato, Papagaio', + enText: 'e.g. Dog, Cat, Parrot'), + suffixIcon: Symbols.sound_detection_dog_barking, + 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: 'Ex: Labrador, Poodle, Siamês, Persa', + enText: 'e.g. Labrador, Poodle, Siamese, Persian'), + suffixIcon: Icons.pets_outlined, + 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: 'Ex: Preto, Amarelo, Branco', + enText: 'e.g. Black, Yellow, White'), + suffixIcon: Symbols.palette, + 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(() { + _model.selectedDate = DateTime( + pickedDate.year, + pickedDate.month, + pickedDate.day, + ); + + _model.textControllerData = + TextEditingController( + text: dateTimeFormat( + 'dd/MM/yyyy', + _model.selectedDate, + locale: FFLocalizations.of(context) + .languageCode, + )); + _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: 250, + ), + Padding( + padding: const EdgeInsets.fromLTRB(70, 20, 70, 30), + child: SubmitButtonUtil( + labelText: FFLocalizations.of(context) + .getVariableText( + ptText: 'Salvar', enText: 'Save'), + onPressed: _model.isFormValid(context) + ? _model.updatePet + : null), + ), + ])), + ], + ), + ); + } +} diff --git a/lib/pages/preferences_settings_page/preferences_settings_model.dart b/lib/pages/preferences_settings_page/preferences_settings_model.dart index e892e7ea..795d5417 100644 --- a/lib/pages/preferences_settings_page/preferences_settings_model.dart +++ b/lib/pages/preferences_settings_page/preferences_settings_model.dart @@ -225,7 +225,6 @@ class PreferencesPageModel with ChangeNotifier { ); }).whenComplete(() => notifyListeners()); } on Exception catch (e) { - log(e.toString()); context.pop(); } }, 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..aaa09bd5 100644 --- a/lib/shared/utils/validator_util.dart +++ b/lib/shared/utils/validator_util.dart @@ -20,10 +20,21 @@ 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'; + + return date; } static String toLocalDateTime(String format, String value) { @@ -32,4 +43,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 5817c85f..148e4c8b 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