diff --git a/android/.tool-versions b/android/.tool-versions deleted file mode 100644 index d673a119..00000000 --- a/android/.tool-versions +++ /dev/null @@ -1,4 +0,0 @@ -gradle 7.6.3 -flutter 3.24.0-stable -java openjdk-19 -kotlin 2.0.10 diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index bc60e9ce..66407cbd 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -14,7 +14,8 @@ android:label="FREHub" tools:replace="android:label" android:icon="@mipmap/ic_launcher" - android:requestLegacyExternalStorage="true"> + android:requestLegacyExternalStorage="true" + android:usesCleartextTraffic="true"> + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/light/reception.svg b/assets/images/light/reception.svg new file mode 100644 index 00000000..bb6d64de --- /dev/null +++ b/assets/images/light/reception.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/actions/actions.dart b/lib/actions/actions.dart index eec12875..192332a7 100644 --- a/lib/actions/actions.dart +++ b/lib/actions/actions.dart @@ -1,14 +1,14 @@ +import 'dart:developer'; + import 'package:flutter/material.dart'; // import 'package:hub/components/organisms/bottom_arrow_linked_locals/bottom_arrow_linked_locals_component_widget.dart'; import 'package:hub/backend/api_requests/api_calls.dart'; import 'package:hub/components/molecular_components/option_selection_modal/option_selection_modal_widget.dart'; -import 'package:hub/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart'; import 'package:hub/custom_code/actions/get_dev_u_u_i_d.dart'; import 'package:hub/flutter_flow/flutter_flow_theme.dart'; import 'package:hub/flutter_flow/flutter_flow_util.dart'; import 'package:hub/flutter_flow/nav/nav.dart'; import 'package:hub/flutter_flow/random_data_util.dart'; -import 'package:hub/pages/home_page/home_page_model.dart'; import 'package:hub/shared/utils/dialog_util.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -96,7 +96,8 @@ Future manageStatusColorAction( } Future singInLoginAction( - BuildContext context, { + BuildContext context, + FlutterFlowModel model, { String? emailAdress, String? password, }) async { @@ -156,8 +157,8 @@ Future singInLoginAction( AppState().serialNumber = await getSerialNumber() ?? ''; AppState().isLogged = true; - - await toggleHomePage(context); + AppState().haveLocal = + await checkLocals(context: context, model: model); } else { if (PhpGroup.loginCall.msg((loginCall?.jsonBody ?? '')) == null) { DialogUtil.errorDefault(context); @@ -301,16 +302,27 @@ Future toggleSignUpPage(BuildContext context) async { ); } -Future toggleHomePage(BuildContext context) async { - context.goNamed( - 'homePage', - extra: { - kTransitionInfoKey: const TransitionInfo( - hasTransition: true, - transitionType: PageTransitionType.fade, - ), - }, - ); +Future toggleApp(BuildContext context, bool haveLocal) async { + if (haveLocal == true) + context.goNamed( + 'homePage', + extra: { + kTransitionInfoKey: const TransitionInfo( + hasTransition: true, + transitionType: PageTransitionType.fade, + ), + }, + ); + else if (haveLocal == false) + context.goNamed( + 'receptionPage', + extra: { + kTransitionInfoKey: const TransitionInfo( + hasTransition: true, + transitionType: PageTransitionType.fade, + ) + }, + ); } Future visitCancelAction(BuildContext context, @@ -369,57 +381,40 @@ Future snackbar(BuildContext context, {required bool opt}) async { Future checkLocals({ String? cliUUID, required BuildContext context, - required HomePageModel model, - required void Function(void Function()) safeSetState, + required FlutterFlowModel model, }) async { - bool itemFound = false; - var modalResult; + final response = await PhpGroup.getLocalsCall.call( + devUUID: AppState().devUUID, + userUUID: AppState().userUUID, + ); + log(response.jsonBody.toString()); - do { - // A chamada para a API permanece a mesma, assumindo que é necessária sempre. - final response = await PhpGroup.getLocalsCall.call( - devUUID: AppState().devUUID, - userUUID: AppState().userUUID, - ); - - // Verificação rápida de erro para evitar processamento desnecessário. - if (response.jsonBody['error']) { - return false; - } - - // Uso eficiente de coleções para verificar a condição desejada. - final String uuid = cliUUID ?? AppState().cliUUID; - itemFound = !response.jsonBody['locais'].any( - (local) => local == null || local.toString().isEmpty, - ); - - // Log e retorno condicional baseado no resultado da busca. - if (itemFound) { - return true; - } else { - // A chamada para showModalBottomSheet permanece, mas a atualização da UI é otimizada. - modalResult = await showModalBottomSheet( - isScrollControlled: true, - backgroundColor: Colors.transparent, - enableDrag: false, - isDismissible: false, - context: context, - builder: (context) => GestureDetector( - onTap: () => model.unfocusNode.canRequestFocus - ? FocusScope.of(context).requestFocus(model.unfocusNode) - : FocusScope.of(context).unfocus(), - child: Padding( - padding: MediaQuery.viewInsetsOf(context), - child: const BottomArrowLinkedLocalsComponentWidget(), - ), - ), - ); - safeSetState( - () {}); // Chamada otimizada fora do then para evitar encadeamentos desnecessários. - } - } while (modalResult != true); - - return false; + // Verificação rápida de erro para evitar processamento desnecessário. + if (response.jsonBody['error']) { + DialogUtil.errorDefault(context); + return false; + } + List locals = response.jsonBody['locais'] ?? []; + if (locals != null && locals.isEmpty) { + await toggleApp(context, false); + return false; + } else { + // else if (locals.where((local) => local['CLU_STATUS'] != 'A').isNotEmpty) { + // await showModalBottomSheet( + // isScrollControlled: true, + // backgroundColor: Colors.transparent, + // enableDrag: false, + // isDismissible: false, + // context: context, + // builder: (context) => Padding( + // padding: MediaQuery.viewInsetsOf(context), + // child: const BottomArrowLinkedLocalsComponentWidget(), + // ), + // ); + // } + await toggleApp(context, true); + return true; + } } Future answersRequest(BuildContext context, String? ref, String? task, diff --git a/lib/app_state.dart b/lib/app_state.dart index 06276083..72548ef0 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 { + _haveLocal = await secureStorage.getBool('ff_have_local') ?? _haveLocal; + }); } void update(VoidCallback callback) { @@ -409,6 +412,13 @@ class AppState extends ChangeNotifier { secureStorage.setBool('ff_isLogged', value); } + bool _haveLocal = false; + bool get haveLocal => _haveLocal; + set haveLocal(bool value) { + _haveLocal = value; + secureStorage.setBool('ff_have_local', value); + } + void deleteIsLogged() { secureStorage.delete(key: 'ff_isLogged'); } diff --git a/lib/backend/api_requests/api_calls.dart b/lib/backend/api_requests/api_calls.dart index 2ffe5045..5cef38f7 100644 --- a/lib/backend/api_requests/api_calls.dart +++ b/lib/backend/api_requests/api_calls.dart @@ -16,7 +16,6 @@ const _kPrivateApiFunctionName = 'ffPrivateApiCall'; class PhpGroup { static String getBaseUrl() => 'https://freaccess.com.br/freaccess'; - // static String getBaseUrl() => 'http://192.168.2.250:8080'; static Map headers = {}; static LoginCall loginCall = LoginCall(); static UpdToken updToken = UpdToken(); @@ -49,6 +48,55 @@ class PhpGroup { static ChangePanic changePanic = ChangePanic(); static DeleteAccount deleteAccount = DeleteAccount(); static CancelaVisita cancelaVisita = CancelaVisita(); + static BuscaEnconcomendas buscaEnconcomendas = BuscaEnconcomendas(); +} + + +class BuscaEnconcomendas { + Future call({ + String? devUUID = '', + String? userUUID = '', + String? cliID = '', + String? atividade = '', + + String? page = '', + String? pageSize = '', + String? adresseeType = '', + String? status = '', + }) async { + final baseUrl = PhpGroup.getBaseUrl(); + + return ApiManager.instance.makeApiCall( + callName: 'getEncomendas', + apiUrl: '$baseUrl/processRequest.php', + callType: ApiCallType.POST, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + params: { + 'devUUID': devUUID, + 'userUUID': userUUID, + 'atividade': atividade, + 'cliID': cliID, + + 'page': page, + 'pageSize': pageSize, + 'adresseeType': adresseeType, + 'status': status, + }, + bodyType: BodyType.X_WWW_FORM_URL_ENCODED, + returnBody: true, + encodeBodyUtf8: false, + decodeUtf8: false, + cache: false, + alwaysAllowBody: false, + ); + } + + bool? error(dynamic response) => castToType(getJsonField( + response, + r'''$.error''', + )); } class CancelaVisita { diff --git a/lib/components/molecular_components/message_opt_modal/opt_modal_widget.dart b/lib/components/molecular_components/message_opt_modal/opt_modal_widget.dart index 25eeef5c..53a01170 100644 --- a/lib/components/molecular_components/message_opt_modal/opt_modal_widget.dart +++ b/lib/components/molecular_components/message_opt_modal/opt_modal_widget.dart @@ -147,7 +147,8 @@ class _OptModalWidgetState extends State { width: 5, color: FlutterFlowTheme.of(context).secondaryText, ), - controlAffinity: ListTileControlAffinity.leading, + controlAffinity: + ListTileControlAffinity.leading, // Adiciona esta linha ); }, ), @@ -176,8 +177,8 @@ class _OptModalWidgetState extends State { return Center( child: Container( - width: screenWidth * 0.75, - height: screenHeight * 0.35, + width: screenWidth - (screenWidth * 0.35), + height: 250, decoration: BoxDecoration( color: FlutterFlowTheme.of(context).primaryBackground, borderRadius: BorderRadius.circular(24.0), diff --git a/lib/components/molecular_components/order_filter_modal/order_filter_modal_model.dart b/lib/components/molecular_components/order_filter_modal/order_filter_modal_model.dart new file mode 100644 index 00000000..e1f99286 --- /dev/null +++ b/lib/components/molecular_components/order_filter_modal/order_filter_modal_model.dart @@ -0,0 +1,32 @@ +import '/flutter_flow/flutter_flow_util.dart'; +import '/flutter_flow/form_field_controller.dart'; +import 'order_filter_modal_widget.dart' show OrderFilterModalWidget; +import 'package:flutter/material.dart'; + + + +class OrderFilterModalModel extends FlutterFlowModel { + + // State field(s) for TextField widget. + FocusNode? textFieldFocusNode; + TextEditingController? textController; + String? Function(BuildContext, String?)? textControllerValidator; + // State field(s) for Checkbox widget. + bool? checkboxValue1; + // State field(s) for Checkbox widget. + bool? checkboxValue2; + // State field(s) for CheckboxGroup widget. + FormFieldController>? checkboxGroupValueController; + List? get checkboxGroupValues => checkboxGroupValueController?.value; + set checkboxGroupValues(List? v) => + checkboxGroupValueController?.value = v; + + @override + void initState(BuildContext context) {} + + @override + void dispose() { + textFieldFocusNode?.dispose(); + textController?.dispose(); + } +} \ No newline at end of file diff --git a/lib/components/molecular_components/order_filter_modal/order_filter_modal_widget.dart b/lib/components/molecular_components/order_filter_modal/order_filter_modal_widget.dart new file mode 100644 index 00000000..5951ef8a --- /dev/null +++ b/lib/components/molecular_components/order_filter_modal/order_filter_modal_widget.dart @@ -0,0 +1,223 @@ + +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:hub/components/molecular_components/order_filter_modal/order_filter_modal_model.dart'; +import 'package:hub/flutter_flow/flutter_flow_model.dart'; +import 'package:hub/flutter_flow/flutter_flow_theme.dart'; +import 'package:hub/flutter_flow/internationalization.dart'; + +class OrderFilterModalWidget extends StatefulWidget { + final String defaultAdresseeType; + final String defaultStatus; + + const OrderFilterModalWidget({ + Key? key, + this.defaultAdresseeType = '.*', + this.defaultStatus = '.*', + }) : super(key: key); + + @override + _OrderFilterModalWidgetState createState() => _OrderFilterModalWidgetState(); +} + +class _OrderFilterModalWidgetState extends State { + late OrderFilterModalModel _model; + + late Map selected; + final List> adresseeTypeOptions = [ + {'title': 'Morador', 'value': 'MOR'}, + {'title': 'Propriedade', 'value': 'PRO'}, + ]; + final List> statusOptions = [ + {'title': 'Aguardando Retirada', 'value': 'notPickedUp'}, + {'title': 'Retirado', 'value': 'pickedUp'}, + ]; + + @override + void setState(VoidCallback callback) { + super.setState(callback); + _model.onUpdate(); + } + + @override + void initState() { + super.initState(); + + _model = createModel(context, () => OrderFilterModalModel()); + + selected = { + 'adresseeType': widget.defaultAdresseeType == '.*' ? ['MOR', 'PRO'] : [widget.defaultAdresseeType], + 'status': widget.defaultStatus == '.*' ? ['notPickedUp', 'pickedUp'] : [widget.defaultStatus], + }; + } + + void _applyFilter() { + Map filterResult = { + 'adresseeType': '', + 'status': '', + }; + + if (selected['adresseeType']!.isEmpty) { + filterResult['adresseeType'] = '.*'; + } else if (selected['adresseeType']!.length > 1) { + filterResult['adresseeType'] = '.*'; + } else { + filterResult['adresseeType'] = selected['adresseeType']!.first; + } + + if (selected['status']!.isEmpty) { + filterResult['status'] = '.*'; + } else if (selected['status']!.length > 1) { + filterResult['status'] = '.*'; + } else { + filterResult['status'] = selected['status']!.first; + } + + Navigator.pop(context, filterResult); + } + + Widget _buildCheckboxListTile(String key, List> options, double fontsize) { + return Column( + children: [ + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsetsDirectional.fromSTEB(0.0, 3.0, 0.0, 0.0), + child: Text( + key == 'status' ? 'Status' : 'Destinátario', + textAlign: TextAlign.left, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily, + fontSize: fontsize, + letterSpacing: 0.0, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context).bodyMediumFamily), + color: FlutterFlowTheme.of(context).primaryText, + ), + ), + ), + ], + ), + ListView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: options.length, + itemBuilder: (context, index) { + final option = options[index]; + return CheckboxListTile( + title: Text( + option['title']!, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily, + letterSpacing: 0.0, + fontSize: fontsize, + useGoogleFonts: GoogleFonts.asMap().containsKey(FlutterFlowTheme.of(context).bodyMediumFamily), + color: FlutterFlowTheme.of(context).primaryText, + ), + ), + dense: true, + value: selected[key]!.contains(option['value']), + onChanged: (bool? value) { + setState(() { + if (value == true) { + if (!selected[key]!.contains(option['value'])) { + selected[key]!.add(option['value']); + } + } else { + selected[key]!.remove(option['value']); + } + }); + }, + activeColor: FlutterFlowTheme.of(context).primary, + checkColor: FlutterFlowTheme.of(context).info, + checkboxShape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(100), + ), + + enableFeedback: true, + side: BorderSide( + width: 5, + color: FlutterFlowTheme.of(context).secondaryText, + ), + controlAffinity: ListTileControlAffinity.leading, // Adiciona esta linha + ); + }, + ), + ], + ); + } + + @override + Widget build(BuildContext context) { + double screenWidth = MediaQuery.of(context).size.width; + double screenHeight = MediaQuery.of(context).size.height; + + return Center( + child: Container( + width: screenWidth - (screenWidth * 0.35), + height: 420, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context).primaryBackground, + borderRadius: BorderRadius.circular(24.0), + ), + child: Padding( + padding: const EdgeInsets.all(4.0), + child: Column( + children: [ + Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsetsDirectional.fromSTEB( + 10.0, 10.0, 0.0, 10.0), + child: Text( + FFLocalizations.of(context) + .getText('yfj9pd6k'), // Filtros + style: + FlutterFlowTheme.of(context).headlineMedium.override( + fontFamily: FlutterFlowTheme.of(context) + .headlineMediumFamily, + color: FlutterFlowTheme.of(context).primaryText, + fontSize: 18.0, + letterSpacing: 0.0, + fontWeight: FontWeight.bold, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context) + .headlineMediumFamily), + ), + ), + ), + ], + ), + Expanded( + child: SingleChildScrollView( + child: Container( + padding: const EdgeInsets.all(10), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + _buildCheckboxListTile('adresseeType', adresseeTypeOptions, 14), + _buildCheckboxListTile('status', statusOptions, 14), + ], + ), + ), + ), + ), + ElevatedButton( + onPressed: _applyFilter, + style: ElevatedButton.styleFrom( + foregroundColor: FlutterFlowTheme.of(context).info, + backgroundColor: FlutterFlowTheme.of(context).primary, + ), + child: Text(FFLocalizations.of(context).getText('88kshkph')), + ), + ], + ), + ), + ), + ); + } +} \ No newline at end of file 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 378e00ed..2db66ce7 100644 --- a/lib/components/organism_components/menu_component/menu_component_model.dart +++ b/lib/components/organism_components/menu_component/menu_component_model.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:hub/components/organism_components/menu_staggered_view_component/menu_staggered_view_component_model.dart'; import 'package:hub/flutter_flow/nav/nav.dart'; +import 'package:hub/pages/reservation_page/reservation_page_widget.dart'; import 'package:hub/shared/extensions/dialog_extensions.dart'; import '/components/molecular_components/option_selection_modal/option_selection_modal_widget.dart'; @@ -192,6 +193,60 @@ class MenuComponentModel extends FlutterFlowModel { ).then((value) => true); } + Future packageOrder(BuildContext context) async { + context.pushNamed( + 'packageOrder', + extra: { + kTransitionInfoKey: const TransitionInfo( + hasTransition: true, + transitionType: PageTransitionType.scale, + alignment: Alignment.bottomCenter, + ), + }, + ).then((value) => true); + } + + Future reservation(BuildContext context) async { + Navigator.push(context, + MaterialPageRoute(builder: (context) => ReservationPageWidget())); + } + + Future packageOrder(BuildContext context) async { + context.pushNamed( + 'packageOrder', + extra: { + kTransitionInfoKey: const TransitionInfo( + hasTransition: true, + transitionType: PageTransitionType.scale, + alignment: Alignment.bottomCenter, + ), + }, + ).then((value) => true); + } + + Future reservation(BuildContext context) async { + Navigator.push(context, + MaterialPageRoute(builder: (context) => ReservationPageWidget())); + } + + Future packageOrder(BuildContext context) async { + context.pushNamed( + 'packageOrder', + extra: { + kTransitionInfoKey: const TransitionInfo( + hasTransition: true, + transitionType: PageTransitionType.scale, + alignment: Alignment.bottomCenter, + ), + }, + ).then((value) => true); + } + + Future reservation(BuildContext context) async { + Navigator.push(context, + MaterialPageRoute(builder: (context) => ReservationPageWidget())); + } + Future consultHistoriesAction(BuildContext context) async { await showAdaptiveDialog( // isScrollControlled: true, 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 25cd37b5..4c708c2a 100644 --- a/lib/components/organism_components/menu_component/menu_component_widget.dart +++ b/lib/components/organism_components/menu_component/menu_component_widget.dart @@ -328,6 +328,28 @@ class _MenuComponentWidgetState extends State { ptText: 'Consultar\nHistoricos', ), ), + MenuCardItem( + icon: Icons.inventory_2_rounded, + action: () async { + await _model.packageOrder(context); + setState(() {}); + }, + title: FFLocalizations.of(context).getVariableText( + enText: 'Orders', + ptText: 'Encomendas', + ), + ), + MenuCardItem( + icon: Icons.event_available, + action: () async { + await _model.reservation(context); + setState(() {}); + }, + title: FFLocalizations.of(context).getVariableText( + enText: 'Reservations', + ptText: 'Reservas', + ), + ), MenuCardItem( icon: Icons.settings, action: () async { diff --git a/lib/components/templates_components/card_item_template_component/card_item_template_component_widget.dart b/lib/components/templates_components/card_item_template_component/card_item_template_component_widget.dart index 5ce3e572..911d27db 100644 --- a/lib/components/templates_components/card_item_template_component/card_item_template_component_widget.dart +++ b/lib/components/templates_components/card_item_template_component/card_item_template_component_widget.dart @@ -37,7 +37,6 @@ class _CardItemTemplateComponentWidgetState late CardItemTemplateComponentModel _model; LinkedHashMap get labelsLinkedHashMap => LinkedHashMap.from(widget.labelsHashMap ?? {}); - List> get statusLinkedHashMap => widget.statusHashMap .map((map) => LinkedHashMap.from(map ?? {})) @@ -57,42 +56,166 @@ class _CardItemTemplateComponentWidgetState @override void dispose() { - _model.maybeDispose(); - super.dispose(); + _model.maybeDispose(); } - Color _getColorForStatus(String status) { - if (status.length % 3 == 0) { - return FlutterFlowTheme.of(context).success; - } else if (status.length % 3 == 1) { - return FlutterFlowTheme.of(context).error; - } else { - return FlutterFlowTheme.of(context).primary; + String truncate(int cutoff, String text) { + return (text.length <= cutoff) ? text : '${text.substring(0, cutoff)}...'; + } + + List _generateLabels() { + List labels = []; + double width = MediaQuery.of(context).size.width; + + for (var key in labelsLinkedHashMap.keys) { + String text = labelsLinkedHashMap[key].toString(); + + Widget widget = Row( + children: [ + Text( + key, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily, + letterSpacing: 0.0, + fontWeight: FontWeight.bold, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context).bodyMediumFamily), + color: FlutterFlowTheme.of(context).primaryText, + ), + ), + const SizedBox(width: 2.5), + SizedBox( + width: 225, + child: Text( + text, + overflow: TextOverflow.ellipsis, + style: FlutterFlowTheme.of(context).bodyMedium.override( + fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily, + fontSize: 12.5, + letterSpacing: 0.0, + fontWeight: FontWeight.bold, + useGoogleFonts: GoogleFonts.asMap().containsKey( + FlutterFlowTheme.of(context).bodyMediumFamily), + ), + ), + ) + ], + ); + + labels.add(widget); } + + return labels; } - String truncate(int cutoff, String mytext) { - return (mytext.length <= cutoff) - ? mytext - : '${mytext.substring(0, cutoff)}...'; + Widget _generateImage() { + return Container( + width: 100, + height: 100, + decoration: BoxDecoration( + color: FlutterFlowTheme.of(context).primaryBackground, + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(20), + child: CachedNetworkImage( + fadeInDuration: const Duration(milliseconds: 500), + fadeOutDuration: const Duration(milliseconds: 500), + imageUrl: widget.imagePath ?? '', + fit: BoxFit.cover, + ), + ), + ); + } + + List _generateStatus() { + List status = []; + + statusLinkedHashMap.expand((statusLinked) { + statusLinked.forEach((text, color) { + Widget widget = Container( + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + color: color, borderRadius: BorderRadius.circular(5)), + child: Text( + text, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: FlutterFlowTheme.of(context).info, + fontSize: 12, + fontWeight: FontWeight.bold), + ), + ); + + status.add(widget); + }); + + return []; + }).toList(); + + return status; + } + + Widget _smallScreen() { + return Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _generateImage(), + const SizedBox(height: 5), + Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ..._generateLabels(), + const SizedBox(height: 5), + Wrap( + spacing: 8, + runSpacing: 8, + children: _generateStatus(), + ) + ]), + ], + ); + } + + Widget _largeScreen() { + return Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ..._generateLabels(), + const SizedBox(height: 5), + Wrap( + spacing: 8, + runSpacing: 8, + children: _generateStatus(), + ) + ]), + _generateImage() + ], + ); } @override Widget build(BuildContext context) { double screenWidth = MediaQuery.of(context).size.width; double screenHeight = MediaQuery.of(context).size.height; + context.watch(); + return InkWell( splashColor: Colors.transparent, focusColor: Colors.transparent, hoverColor: Colors.transparent, highlightColor: Colors.transparent, - onTap: () async { - await widget.onTapCardItemAction?.call(); - }, + onTap: () async => await widget.onTapCardItemAction?.call(), child: Padding( - padding: const EdgeInsets.fromLTRB(10, 0, 10, 0), + padding: const EdgeInsets.all(8), child: Card( clipBehavior: Clip.antiAliasWithSaveLayer, color: FlutterFlowTheme.of(context).primaryBackground, @@ -101,155 +224,16 @@ class _CardItemTemplateComponentWidgetState borderRadius: BorderRadius.circular(8.0), ), child: Container( - width: 350, - height: 125, + width: double.infinity, decoration: BoxDecoration( color: FlutterFlowTheme.of(context).primaryBackground, ), - child: Row( + padding: const EdgeInsets.all(8), + child: Column( mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded( - child: Container( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height, - decoration: const BoxDecoration(), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - child: ListView.builder( - shrinkWrap: true, - itemCount: labelsLinkedHashMap.length, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, index) { - String key = - labelsLinkedHashMap.keys.elementAt(index); - String value = labelsLinkedHashMap[key]!; - return Padding( - padding: - const EdgeInsets.fromLTRB(20, 0, 0, 5), - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: - MainAxisAlignment.start, - children: [ - Text( - key, - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: - FlutterFlowTheme.of(context) - .bodyMediumFamily, - letterSpacing: 0.0, - fontWeight: FontWeight.bold, - useGoogleFonts: GoogleFonts - .asMap() - .containsKey( - FlutterFlowTheme.of( - context) - .bodyMediumFamily), - color: - FlutterFlowTheme.of(context) - .primaryText, - ), - ), - const SizedBox( - width: - 3.0), // Espaçamento entre o label e o valor - Flexible( - child: Text( - value, - overflow: TextOverflow.ellipsis, - style: FlutterFlowTheme.of(context) - .bodyMedium - .override( - fontFamily: - FlutterFlowTheme.of( - context) - .bodyMediumFamily, - fontSize: 12.5, - letterSpacing: 0.0, - fontWeight: FontWeight.bold, - useGoogleFonts: GoogleFonts - .asMap() - .containsKey( - FlutterFlowTheme.of( - context) - .bodyMediumFamily), - ), - ), - ), - ], - ), - ); - }, - ), - ), - OverflowBar( - overflowAlignment: OverflowBarAlignment.center, - overflowSpacing: 2, - spacing: 2, - children: - statusLinkedHashMap.expand((linkedHashMap) { - return linkedHashMap.entries - .map((MapEntry item) { - return Container( - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 1.0, vertical: 3.0), - child: Container( - width: 100.0, - height: 27.0, - decoration: BoxDecoration( - color: item - .value, // Usa a cor do item atual - borderRadius: - BorderRadius.circular(5.0), - ), - child: Align( - alignment: Alignment.center, - child: Text( - item.key, // Usa a chave do item atual como texto - style: TextStyle( - color: FlutterFlowTheme.of( - context) - .info, // Ajuste conforme necessário - ), - ), - ), - ), - ), - ); - }).toList(); - }).toList(), - ), - ]), - ), - ), - ), - Padding( - padding: const EdgeInsetsDirectional.fromSTEB( - 10.0, 10.0, 10.0, 10.0), - child: ClipRRect( - borderRadius: BorderRadius.circular(22.0), - child: AspectRatio( - aspectRatio: - 1.0, // Define a proporção desejada (1:1 neste caso) - child: CachedNetworkImage( - fadeInDuration: const Duration(milliseconds: 500), - fadeOutDuration: const Duration(milliseconds: 500), - imageUrl: widget.imagePath ?? '', - fit: BoxFit.cover, - ), - ), - ), - ) + screenWidth > 360 ? _largeScreen() : _smallScreen(), ], ), ), diff --git a/lib/components/templates_components/sign_in_template_component/sign_in_template_component_model.dart b/lib/components/templates_components/sign_in_template_component/sign_in_template_component_model.dart index e1010ec7..165de263 100644 --- a/lib/components/templates_components/sign_in_template_component/sign_in_template_component_model.dart +++ b/lib/components/templates_components/sign_in_template_component/sign_in_template_component_model.dart @@ -1,3 +1,5 @@ +import 'package:hub/pages/home_page/home_page_model.dart'; + import '/flutter_flow/flutter_flow_util.dart'; import 'sign_in_template_component_widget.dart' show SignInTemplateComponentWidget; @@ -13,6 +15,8 @@ class SignInTemplateComponentModel FocusNode? emailAddressFocusNode; TextEditingController? emailAddressTextController; String? Function(BuildContext, String?)? emailAddressTextControllerValidator; + + SignInTemplateComponentModel(); String? _emailAddressTextControllerValidator( BuildContext context, String? val) { if (val == null || val.isEmpty) { diff --git a/lib/components/templates_components/sign_in_template_component/sign_in_template_component_widget.dart b/lib/components/templates_components/sign_in_template_component/sign_in_template_component_widget.dart index 37658f1b..419b80f9 100644 --- a/lib/components/templates_components/sign_in_template_component/sign_in_template_component_widget.dart +++ b/lib/components/templates_components/sign_in_template_component/sign_in_template_component_widget.dart @@ -548,6 +548,7 @@ class _SignInTemplateComponentWidgetState await action_blocks .singInLoginAction( context, + _model, emailAdress: _model .emailAddressTextController .text, @@ -707,6 +708,7 @@ class _SignInTemplateComponentWidgetState await action_blocks .singInLoginAction( context, + _model, emailAdress: _model .emailAddressTextController .text, diff --git a/lib/flutter_flow/nav/nav.dart b/lib/flutter_flow/nav/nav.dart index 53a62a9a..4b5ad183 100644 --- a/lib/flutter_flow/nav/nav.dart +++ b/lib/flutter_flow/nav/nav.dart @@ -5,6 +5,10 @@ import 'package:hub/flutter_flow/nav/nav.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/no_connection_page/no_connection_page.dart'; +import 'package:hub/pages/package_order_page/package_order_page.dart'; +import 'package:hub/pages/reservation_page/reservation_page_widget.dart'; +import 'package:hub/pages/preferences_settings_page/preferences_settings_widget.dart'; +import 'package:hub/pages/reception_page/reception_page_widget.dart'; import 'package:provider/provider.dart'; import '/backend/schema/structs/index.dart'; @@ -68,7 +72,9 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) => GoRouter( name: '_initialize', path: '/', builder: (context, _) => AppState().isLogged - ? const HomePageWidget() + ? AppState().haveLocal + ? const HomePageWidget() + : const ReceptionPageWidget() : const WelcomePageWidget(), ), FFRoute( @@ -78,15 +84,9 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) => GoRouter( params.isEmpty ? const HomePageWidget() : const HomePageWidget(), ), FFRoute( - name: 'no-connection', - path: '/no-connection', - builder: (context, params) => const NoConnectionScreen(), - ), - // FFRoute( - // name: 'visitHistoryPage', - // path: '/visitHistoryPage', - // builder: (context, params) => const VisitHistoryPageWidget(), - // ), + name: 'receptionPage', + path: '/receptionPage', + builder: (context, params) => const ReceptionPageWidget()), FFRoute( name: 'messageHistoryPage', path: '/messageHistoryPage', @@ -136,7 +136,7 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) => GoRouter( FFRoute( name: 'acessHistoryPage', path: '/acessHistoryPage', - builder: (context, params) => AcessHistoryPageWidget(opt: { + builder: (context, params) => AcessHistoryPageWidget(opt: const { 'personType': '.*', 'accessType': '.*', 'search': '.*', @@ -171,6 +171,16 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) => GoRouter( path: '/preferencesPage', builder: (context, params) => const PreferencesPageWidget(), ), + FFRoute( + name: 'packageOrder', + path: '/packageOrder', + builder: (context, params) => const PackageOrderPage(), + ), + FFRoute( + name: 'reservation', + path: '/reservation', + builder: (context, params) => ReservationPageWidget(), + ), // FFRoute( // name: 'settingsPage', // path: '/settingsPage', diff --git a/lib/pages/acess_history_page/acess_history_page_widget.dart b/lib/pages/acess_history_page/acess_history_page_widget.dart index dcc6b0a3..d4a7f003 100644 --- a/lib/pages/acess_history_page/acess_history_page_widget.dart +++ b/lib/pages/acess_history_page/acess_history_page_widget.dart @@ -23,23 +23,7 @@ class AcessHistoryPageWidget extends StatefulWidget { }; AcessHistoryPageWidget({super.key, required this.opt}); @override - State createState() => - _AcessHistoryPageWidgetState(opt); -} - -class AccessHistoryItemWidget extends StatelessWidget { - final dynamic accessHistoryItem; - - const AccessHistoryItemWidget({Key? key, required this.accessHistoryItem}) - : super(key: key); - - @override - Widget build(BuildContext context) { - return const Padding( - padding: EdgeInsets.all(8.0), - child: Column(), - ); - } + State createState() => _AcessHistoryPageWidgetState(opt); } class _AcessHistoryPageWidgetState extends State { @@ -59,8 +43,7 @@ class _AcessHistoryPageWidgetState extends State { late Future _accessFuture; List _accessWrap = []; - _AcessHistoryPageWidgetState(Map opt) - : selectedTypeSubject = BehaviorSubject.seeded(opt) { + _AcessHistoryPageWidgetState(Map opt) : selectedTypeSubject = BehaviorSubject.seeded(opt) { selectedTypeSubject.listen((value) {}); } diff --git a/lib/pages/fast_pass_page/fast_pass_page_widget.dart b/lib/pages/fast_pass_page/fast_pass_page_widget.dart index 01992bad..b5f962c5 100644 --- a/lib/pages/fast_pass_page/fast_pass_page_widget.dart +++ b/lib/pages/fast_pass_page/fast_pass_page_widget.dart @@ -1,8 +1,7 @@ -import 'dart:io' show Platform; - import 'package:flutter/material.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:hub/app_state.dart'; +import 'package:hub/app_state.dart'; import 'package:hub/flutter_flow/flutter_flow_theme.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:webview_flutter/webview_flutter.dart'; @@ -63,6 +62,9 @@ class _FastPassPageWidgetState extends State { await controller.evaluateJavascript( source: "window.localStorage.setItem('fre-user-data', '${widget.freUserData}')"); + await controller.evaluateJavascript( + source: + "window.localStorage.setItem('enableBackButton', 'true')"); }, ) : WebViewWidget( @@ -79,9 +81,12 @@ class _FastPassPageWidgetState extends State { "localStorage.setItem('fre-token', '\"${widget.freToken}\"');"; final String data = "localStorage.setItem('fre-user-data', '${widget.freUserData}');"; + const String backNavigation = + "localStorage.setItem('enableBackButton', 'true');"; _controllerAll.runJavaScript(token); _controllerAll.runJavaScript(data); + _controllerAll.runJavaScript(backNavigation); }, onNavigationRequest: (NavigationRequest request) { if (request.url.startsWith('http') || diff --git a/lib/pages/home_page/home_page_widget.dart b/lib/pages/home_page/home_page_widget.dart index 19dc8f0d..a204977f 100644 --- a/lib/pages/home_page/home_page_widget.dart +++ b/lib/pages/home_page/home_page_widget.dart @@ -77,7 +77,6 @@ class _HomePageWidgetState extends State { localStatus = await checkLocals( context: context, model: _model, - safeSetState: safeSetState, ); if (AppState().cliUUID.isEmpty) { diff --git a/lib/pages/package_order_page/package_order_page.dart b/lib/pages/package_order_page/package_order_page.dart new file mode 100644 index 00000000..9d8def5c --- /dev/null +++ b/lib/pages/package_order_page/package_order_page.dart @@ -0,0 +1,410 @@ +import 'dart:async'; +import 'dart:developer'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:hub/backend/api_requests/api_calls.dart'; +import 'package:hub/backend/api_requests/api_manager.dart'; +import 'package:hub/components/molecular_components/order_filter_modal/order_filter_modal_widget.dart'; +import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart'; +import 'package:hub/components/templates_components/details_component/details_component_widget.dart'; +import 'package:hub/flutter_flow/flutter_flow_icon_button.dart'; +import 'package:hub/flutter_flow/flutter_flow_theme.dart'; +import 'package:hub/flutter_flow/flutter_flow_util.dart'; +import 'package:hub/shared/utils/dialog_util.dart'; +import 'package:hub/shared/utils/log_util.dart'; +import 'package:hub/shared/utils/validator_util.dart'; +import 'package:rxdart/rxdart.dart'; + +class PackageOrderPage extends StatefulWidget { + const PackageOrderPage({super.key}); + + @override + State createState() => _PackageOrderPage(); +} + +class _PackageOrderPage extends State { + + late ScrollController _scrollController; + int _pageNumber = 1; + final int _pageSize = 10; + bool _hasData = false; + bool _loading = false; + + late Future _orderFuture; + List _orderList = []; + + final Map filter = { + 'adresseeType': '.*', + 'status': 'notPickedUp', + }; + + String _adresseeType = '.*'; + String _status = 'notPickedUp'; + + late BehaviorSubject> _selectedTypeSubject; + bool _isSubjectClosed = false; + + @override + void initState() { + super.initState(); + _selectedTypeSubject = BehaviorSubject.seeded(filter); + _selectedTypeSubject.listen((value) {}); + + _orderFuture = _fetchOrders(); + + _scrollController = ScrollController()..addListener(() { + if (_scrollController.position.atEdge && _scrollController.position.pixels != 0) { + _loadMoreOrders(); + } + }); + } + + @override + void dispose() { + super.dispose(); + _selectedTypeSubject.close(); + _isSubjectClosed = true; + _scrollController.dispose(); + } + + 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, + ), + ); + } + + Future _fetchOrders() async { + try { + setState(() => _loading = true); + + var response = await PhpGroup.buscaEnconcomendas.call( + devUUID: AppState().devUUID, + userUUID: AppState().userUUID, + cliID: AppState().cliUUID, + atividade: 'getEncomendas', + pageSize: _pageSize.toString(), + page: _pageNumber.toString(), + adresseeType: _adresseeType == '.*' ? 'TOD' : _adresseeType, + status: _status == '.*' ? 'all' : _status + ); + + final List orders = response.jsonBody['value']['list'] ?? []; + + if (orders != null && orders.isNotEmpty) { + setState(() { + _orderList.addAll(orders); + _hasData = true; + _loading = false; + }); + + return response; + } + + _showNoMoreDataSnackbar(context); + + setState(() { + _hasData = false; + _loading = false; + }); + + return null; + } catch (e, s) { + DialogUtil.errorDefault(context); + LogUtil.requestAPIFailed('processRequest', "", "Busca Encomendas", e, s); + setState(() { + _hasData = false; + _loading = false; + }); + } + } + + void _loadMoreOrders() { + if (_hasData == true) { + _pageNumber++; + _orderFuture = _fetchOrders(); + } + } + + Widget _backButton(BuildContext context, FlutterFlowTheme theme) { + return FlutterFlowIconButton( + borderColor: Colors.transparent, + borderRadius: 30.0, + borderWidth: 1.0, + buttonSize: 60.0, + icon: Icon( + Icons.keyboard_arrow_left, + color: theme.primaryText, + size: 30.0, + ), + onPressed: () => Navigator.of(context).pop(), + ); + } + + Widget _filterButton(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 10, 0), + child: IconButton( + icon: const Icon(Icons.filter_list), + onPressed: () async { + final Map? selectedFilter = + await showModalBottomSheet>( + isScrollControlled: true, + backgroundColor: Colors.transparent, + context: context, + builder: (context) { + return GestureDetector( + onTap: () => Navigator.of(context).pop(), + child: Container( + color: Colors.transparent, + child: GestureDetector( + onTap: () {}, + child: OrderFilterModalWidget( + defaultAdresseeType: _selectedTypeSubject.value['adresseeType'] ?? '.*', + defaultStatus: _selectedTypeSubject.value['status'] ?? '.*', + ), + ), + ), + ); + }); + + if (selectedFilter != null) { + _updateFilterAction(selectedFilter); + } + }, + ), + ), + ], + ); + } + + void _updateFilterAction(Map newType) { + if (!_isSubjectClosed) { + final currentType = _selectedTypeSubject.value; + final updatedType = Map.from(currentType); + bool needsUpdate = false; + newType.forEach((key, newValue) { + if (currentType[key] != newValue) { + updatedType[key] = newValue; + needsUpdate = true; + } + }); + if (needsUpdate) { + _selectedTypeSubject.add(updatedType); + _fetchFilterOrder(updatedType); + safeSetState(() {}); + } + } + } + + void _fetchFilterOrder(Map select) { + _adresseeType = select['adresseeType']!; + _status = select['status']!; + _orderList = []; + _pageNumber = 1; + _orderFuture = _fetchOrders(); + } + + PreferredSizeWidget _appBar(BuildContext context) { + double screenWidth = MediaQuery.of(context).size.width; + double screenHeight = MediaQuery.of(context).size.height; + + return AppBar( + backgroundColor: FlutterFlowTheme.of(context).primaryBackground, + automaticallyImplyLeading: false, + title: Text( + FFLocalizations.of(context).getVariableText(enText: 'Orders', ptText: 'Encomendas'), + 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'), + ) + ), + leading: _backButton(context, FlutterFlowTheme.of(context)), + centerTitle: true, + elevation: 0.0, + actions: [_filterButton(context)], + ); + } + + Widget _appBody(BuildContext context) { + return SafeArea( + top: true, + child: 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: "Nenhuma encomenda encontrada!", + enText: "No orders found!"), + )), + ], + ), + ) + else if (_hasData || _pageNumber >= 1) + Expanded(child: _itemList()), + if (_hasData == true && _loading == true) + Container( + padding: const EdgeInsets.only(top: 15, bottom: 15), + child: Center( + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + FlutterFlowTheme.of(context).primary, + ), + ), + ), + ) + ], + ), + ); + } + + Widget _itemList() { + return FutureBuilder( + future: _orderFuture, + builder: (context, snapshot) { + return ListView.builder( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + controller: _scrollController, + itemCount: _orderList.length, + itemBuilder: (context, index) { + final order = _orderList[index]; + return _item(context, order); + }, + ); + } + ); + } + + String _imagePath(dynamic order) { + return '${PhpGroup.getBaseUrl()}/getImage.php?cliID=${AppState().cliUUID}&atividade=getFotoEncomenda&orderId=${order['id'] ?? ''}'; + } + + Map _labelsHashMap(dynamic order) { + return Map.from({ + if (order['isPending'] == true) + '${FFLocalizations.of(context).getVariableText(ptText: 'Cód. Retirada', enText: 'Pickup Code')}:': order['id'].toString(), + '${FFLocalizations.of(context).getVariableText(ptText: 'Categoria', enText: 'Category')}:': order['customCategory'], + '${FFLocalizations.of(context).getVariableText(ptText: 'Recebido em', enText: 'Received on')}:': ValidatorUtil.toLocalDateTime('yyyy-MM-dd HH:mm:ss', order['dtReceive']), + if (order['isPending'] == false) + '${FFLocalizations.of(context).getVariableText(ptText: 'Retirado em', enText: 'Picked up on')}:': ValidatorUtil.toLocalDateTime('yyyy-MM-dd HH:mm:ss', order['dtPickUp']), + }); + } + + Map _labelsHashMapDialog(dynamic order) { + return Map.from({ + if (order['isPending'] == true) + '${FFLocalizations.of(context).getVariableText(ptText: 'Cód. Retirada', enText: 'Pickup Code')}:': order['id'].toString(), + + '${FFLocalizations.of(context).getVariableText(ptText: 'Categoria', enText: 'Category')}:': order['customCategory'], + + if (order['code'] != null) + '${FFLocalizations.of(context).getVariableText(ptText: 'Cód. Objeto', enText: 'Object Code')}:': order['code'], + + '${FFLocalizations.of(context).getVariableText(ptText: 'Entregue por', enText: 'Delivered by')}:': order['sendedBy'], + + '${FFLocalizations.of(context).getVariableText(ptText: 'Recebido por', enText: 'Received by')}:': order['receivedBy'], + '${FFLocalizations.of(context).getVariableText(ptText: 'Recebido em', enText: 'Received on')}:': ValidatorUtil.toLocalDateTime('yyyy-MM-dd HH:mm:ss', order['dtReceive']), + + if (order['isPending'] == false) + '${FFLocalizations.of(context).getVariableText(ptText: 'Retirado por', enText: 'Picked up by')}:': order['pickedUpBy'], + if (order['isPending'] == false) + '${FFLocalizations.of(context).getVariableText(ptText: 'Retirado em', enText: 'Picked up on')}:': ValidatorUtil.toLocalDateTime('yyyy-MM-dd HH:mm:ss', order['dtPickUp']), + + if (order['message'] != null) + '${FFLocalizations.of(context).getVariableText(ptText: 'Mensagem', enText: 'Message')}:': order['message'] + }); + } + + List> _statusHashMap(dynamic order) { + return [ + if (order['isPending'] == true) + Map.from({ + FFLocalizations.of(context).getVariableText(ptText: 'Aguardando Retirada', enText: 'Waiting Pick up'): FlutterFlowTheme.of(context).warning, + }) + else + Map.from({ + FFLocalizations.of(context).getVariableText(ptText: 'Retirado', enText: 'Picked up'): FlutterFlowTheme.of(context).primary, + }), + + if (order['adresseeType'] == 'PRO') + Map.from({ + FFLocalizations.of(context).getVariableText( + ptText: 'Propriedade', + enText: 'Property', + ): FlutterFlowTheme.of(context).alternate2, + }) + else if (order['adresseeType'] == 'MOR') + Map.from({ + FFLocalizations.of(context).getVariableText( + ptText: 'Morador', + enText: 'Resident', + ): FlutterFlowTheme.of(context).alternate2, + }) + ]; + } + + Widget _item(BuildContext context, dynamic order) { + return CardItemTemplateComponentWidget( + imagePath: _imagePath(order), + labelsHashMap: _labelsHashMap(order), + statusHashMap: _statusHashMap(order), + onTapCardItemAction: () async { + showDialog( + useSafeArea: true, + context: context, + builder: (context) { + return Dialog( + child: DetailsComponentWidget( + imagePath: _imagePath(order), + labelsHashMap: _labelsHashMapDialog(order), + statusHashMap: [ + if (order['isPending'] == true) + Map.from({ + FFLocalizations.of(context).getVariableText(ptText: 'Aguardando Retirada', enText: 'Waiting Pick up'): FlutterFlowTheme.of(context).warning, + }) + else + Map.from({ + FFLocalizations.of(context).getVariableText(ptText: 'Retirado', enText: 'Picked up'): FlutterFlowTheme.of(context).primary, + }), + ], + buttons: [], + ), + ); + } + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: _appBar(context), + body: _appBody(context), + ); + } +} \ No newline at end of file diff --git a/lib/pages/preferences_settings_page/preferences_settings_widget.dart b/lib/pages/preferences_settings_page/preferences_settings_widget.dart index 8b755d58..180c1cc2 100644 --- a/lib/pages/preferences_settings_page/preferences_settings_widget.dart +++ b/lib/pages/preferences_settings_page/preferences_settings_widget.dart @@ -65,14 +65,7 @@ class PreferencesPageWidget extends StatelessWidget { Expanded( flex: 2, child: ListView.builder( - // gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - // crossAxisCount: 3, - // crossAxisSpacing: 12.0, - // mainAxisSpacing: 12.0, - // childAspectRatio: 1.0, - // mainAxisExtent: 100.0, - // ), - itemCount: 8, // Assuming 4 items for simplicity + itemCount: 7, // Assuming 4 items for simplicity padding: const EdgeInsets.symmetric(horizontal: 20.0), physics: const AlwaysScrollableScrollPhysics(), itemBuilder: (BuildContext context, int index) { diff --git a/lib/pages/reception_page/reception_page_model.dart b/lib/pages/reception_page/reception_page_model.dart new file mode 100644 index 00000000..47dc15a9 --- /dev/null +++ b/lib/pages/reception_page/reception_page_model.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:hub/app_state.dart'; +import 'package:hub/flutter_flow/internationalization.dart'; +import 'package:share_plus/share_plus.dart'; + +class ReceptionPageModel with ChangeNotifier { + void getIdenfifier(BuildContext context) { + notifyListeners(); + Share.share( + FFLocalizations.of(context).getVariableText( + ptText: + 'Este é o meu identificador de acesso: ${AppState().userDevUUID}', + enText: 'This is my access identifier: ${AppState().userDevUUID}', + ), + ); + } +} diff --git a/lib/pages/reception_page/reception_page_widget.dart b/lib/pages/reception_page/reception_page_widget.dart new file mode 100644 index 00000000..82fe4d3f --- /dev/null +++ b/lib/pages/reception_page/reception_page_widget.dart @@ -0,0 +1,206 @@ +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:hub/app_state.dart'; +import 'package:hub/components/atomic_components/shared_components_atoms/atom_image_svg_theme.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/internationalization.dart'; +import 'package:hub/flutter_flow/nav/nav.dart'; +import 'package:hub/pages/reception_page/reception_page_model.dart'; +import 'package:provider/provider.dart'; + +class ReceptionPageWidget extends StatefulWidget { + const ReceptionPageWidget({super.key}); + + @override + State createState() => _ReceptionPageWidgetState(); +} + +class _ReceptionPageWidgetState extends State { + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider( + create: (context) => ReceptionPageModel(), + child: Scaffold( + body: Consumer( + builder: (context, ReceptionPageModel model, child) { + return SafeArea( + child: Center( + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(45, 0, 45, 15.0), + child: Text( + FFLocalizations.of(context).getVariableText( + ptText: + 'Falta pouco para você utilizar o FRE Access Hub...', + enText: + 'You are close to using the FRE Access Hub...'), + textAlign: TextAlign.center, + style: FlutterFlowTheme.of(context) + .displayLarge + .override( + fontFamily: 'Plus Jakarta Sans', + color: FlutterFlowTheme.of(context).accent1, + fontSize: 20.0, + letterSpacing: 0.0, + fontWeight: FontWeight.w700, + useGoogleFonts: GoogleFonts.asMap() + .containsKey('Plus Jakarta Sans'), + ), + ), + ), + const AtomImageSvgTheme( + filename: 'reception', width: 180, height: 180), + Padding( + padding: const EdgeInsets.fromLTRB(70, 30, 70.0, 60), + child: Text( + FFLocalizations.of(context).getVariableText( + ptText: + 'Envie seu identificador para seu condomínio para vincularem sua conta aos nossos sistemas.', + enText: + 'Send your identifier to your condominium to link your account to our systems.'), + textAlign: TextAlign.center, + style: FlutterFlowTheme.of(context) + .displaySmall + .override( + fontFamily: 'Nunito Sans', + color: FlutterFlowTheme.of(context).primaryText, + fontSize: 14.0, + letterSpacing: 0.0, + fontWeight: FontWeight.w600, + useGoogleFonts: GoogleFonts.asMap() + .containsKey('Plus Jakarta Sans'), + ), + ), + ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(60, 0, 60, 10), + child: Tooltip( + message: FFLocalizations.of(context).getVariableText( + ptText: + 'Seu identificador é utilizado para efetuar\no vinculo do seu APP com o condominio.', + enText: 'My Identifier'), + textStyle: FlutterFlowTheme.of(context) + .labelSmall + .override( + fontFamily: 'Nunito Sans', + color: FlutterFlowTheme.of(context) + .secondaryText, + fontSize: 10.0, + letterSpacing: 0.0, + fontWeight: FontWeight.w500, + useGoogleFonts: GoogleFonts.asMap() + .containsKey('Plus Jakarta Sans'), + ), + child: FFButtonWidget( + onPressed: () => model.getIdenfifier(context), + text: FFLocalizations.of(context) + .getVariableText( + ptText: 'Meu Identificador', + enText: 'My Identifier'), + options: FFButtonOptions( + width: double.infinity, + height: 44.0, + padding: const EdgeInsetsDirectional.fromSTEB( + 0.0, 0.0, 0.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: 'Nunito Sans', + color: FlutterFlowTheme.of(context) + .primaryBackground, + fontSize: 14.0, + letterSpacing: 0.0, + fontWeight: FontWeight.w500, + useGoogleFonts: GoogleFonts.asMap() + .containsKey('Plus Jakarta Sans'), + ), + elevation: 3.0, + borderSide: const BorderSide( + color: Colors.transparent, + width: 1.0, + ), + borderRadius: BorderRadius.circular(12.0), + ), + showLoadingIndicator: false, + ), + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(60, 0, 60, 0), + child: FFButtonWidget( + onPressed: () async { + AppState().deleteAll(); + setState(() {}); + + context.goNamed( + 'welcomePage', + extra: { + kTransitionInfoKey: const TransitionInfo( + hasTransition: true, + transitionType: PageTransitionType.scale, + alignment: Alignment.bottomCenter, + ), + }, + ); + }, + text: FFLocalizations.of(context).getVariableText( + ptText: 'Sair da Conta', enText: 'Logout'), + options: FFButtonOptions( + width: double.infinity, + height: 44.0, + padding: const EdgeInsetsDirectional.fromSTEB( + 0.0, 0.0, 0.0, 0.0), + iconPadding: + const EdgeInsetsDirectional.fromSTEB( + 0.0, 0.0, 0.0, 0.0), + color: + FlutterFlowTheme.of(context).customColor1, + textStyle: FlutterFlowTheme.of(context) + .titleSmall + .override( + fontFamily: 'Nunito Sans', + color: FlutterFlowTheme.of(context) + .primaryBackground, + fontSize: 14.0, + letterSpacing: 0.0, + fontWeight: FontWeight.w500, + useGoogleFonts: GoogleFonts.asMap() + .containsKey('Plus Jakarta Sans'), + ), + elevation: 3.0, + borderSide: const BorderSide( + color: Colors.transparent, + width: 1.0, + ), + borderRadius: BorderRadius.circular(12.0), + ), + showLoadingIndicator: false, + ), + ), + ], + ), + ], + ), + ), + ), + ); + }, + ), + ), + ); + } +} diff --git a/lib/pages/reservation_page/reservation_page_widget.dart b/lib/pages/reservation_page/reservation_page_widget.dart new file mode 100644 index 00000000..ec8dd7f5 --- /dev/null +++ b/lib/pages/reservation_page/reservation_page_widget.dart @@ -0,0 +1,109 @@ +import 'dart:developer'; + +import 'package:hub/app_state.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_inappwebview/flutter_inappwebview.dart'; +import 'package:url_launcher/url_launcher_string.dart'; +import 'package:webview_flutter/webview_flutter.dart'; +import 'dart:io' show Platform; + +class ReservationPageWidget extends StatefulWidget { + final String freToken = AppState().userUUID; + final String freUserData = "{\"name\": \"${AppState().name}\", " + + "\"email\": \"${AppState().email}\"," + + "\"dev_id\": \"${AppState().devUUID}\"," + + "\"created_at\": \"${AppState().createdAt}\"," + + "\"updated_at\": \"0000-00-00 00:00:00\"," + + "\"status\": \"A\" }"; + + final String clientId = AppState().cliUUID; + + @override + _ReservationPageWidgetState createState() => _ReservationPageWidgetState(); +} + +class _ReservationPageWidgetState extends State { + late InAppWebViewController _controllerIOS; + late WebViewController _controllerAll; + late String url; + late String name; + late String email; + late String userUUID; + late String created_at; + + @override + void initState() { + super.initState(); + name = AppState().name; + email = AppState().email; + userUUID = AppState().userUUID; + created_at = AppState().createdAt; + url = 'https://hub.freaccess.com.br/hub/reservation/${widget.clientId}'; + } + + @override + Widget build(BuildContext context) { + return SafeArea( + child: Scaffold( + body: Platform.isIOS + ? InAppWebView( + initialUrlRequest: URLRequest(url: WebUri(url)), + initialSettings: InAppWebViewSettings( + allowsBackForwardNavigationGestures: true, + javaScriptEnabled: true, + ), + onWebViewCreated: (controller) async { + _controllerIOS = controller; + }, + onLoadStop: (controller, url) async { + await controller.evaluateJavascript(source: "window.localStorage.setItem('fre-token', '\"${widget.freToken}\"')"); + await controller.evaluateJavascript(source: "window.localStorage.setItem('fre-user-data', '${widget.freUserData}')"); + await controller.evaluateJavascript(source: "window.localStorage.setItem('enableBackButton', 'true')"); + }, + onUpdateVisitedHistory: (controller, uri, isVisited) { + if (uri.toString().contains('/hub/home')) { + Navigator.pop(context); + } + }, + ) + : WebViewWidget( + controller: _controllerAll = WebViewController() + ..clearCache() + ..clearLocalStorage() + ..setJavaScriptMode(JavaScriptMode.unrestricted) + ..setBackgroundColor(const Color(0x00000000)) + ..setNavigationDelegate( + NavigationDelegate( + onProgress: (int progress) {}, + onPageStarted: (String url) { + final String token = "localStorage.setItem('fre-token', '\"${widget.freToken}\"');"; + final String data = "localStorage.setItem('fre-user-data', '${widget.freUserData}');"; + const String backNavigation = "localStorage.setItem('enableBackButton', 'true');"; + + _controllerAll.runJavaScript(token); + _controllerAll.runJavaScript(data); + _controllerAll.runJavaScript(backNavigation); + }, + onNavigationRequest: (NavigationRequest request) { + if (request.url.startsWith('http') || + request.url + .startsWith('https://api.whatsapp.com/send') || + request.url.startsWith('https://wa.me')) { + launchUrlString(request.url); + return NavigationDecision.prevent; + } + return NavigationDecision.prevent; + }, + onUrlChange: (url) { + if (url.url.toString().contains('/hub/home')) { + Navigator.pop(context); + } + } + ), + ) + ..loadRequest(Uri.parse(url)), + ), + ), + ); + } +}