estado da arte

This commit is contained in:
Jonatas Antunes Messias 2024-07-03 08:49:54 -03:00
parent 3ead866739
commit 72237ae168
9 changed files with 325 additions and 172 deletions

File diff suppressed because one or more lines are too long

3
devtools_options.yaml Normal file
View File

@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:

View File

@ -1,4 +1,6 @@
import 'package:f_r_e_hub/backend/push_notification/pushNotificationService.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/widgets.dart';
import '/backend/api_requests/api_calls.dart';
import '/components/molecular_components/throw_exception/throw_exception_widget.dart';
@ -322,3 +324,119 @@ Future toggleHomePage(BuildContext context) async {
},
);
}
Future<bool> visitRequestComponentAction(
BuildContext context, {
required String? actionValue,
required String? refUUID,
required String? responseValue,
required String? vteUUID,
}) async {
ApiCallResponse? respondeSolicitacaoCall;
respondeSolicitacaoCall = await PhpGroup.respondeSolicitacaoCall.call(
userUUID: FFAppState().userUUID,
devUUID: FFAppState().devUUID,
cliUUID: FFAppState().cliUUID,
atividade: 'respondeSolicitacao',
referencia: refUUID,
tarefa: actionValue,
resposta: responseValue,
idVisitante: vteUUID,
);
if (respondeSolicitacaoCall.statusCode == 200) {
return true;
} else {
debugPrint('headers: ${respondeSolicitacaoCall.headers}');
debugPrint('bodyText: ${respondeSolicitacaoCall.bodyText}');
debugPrint('jsonBody: ${respondeSolicitacaoCall.jsonBody}');
debugPrint('userUUID: ${FFAppState().userUUID}');
debugPrint('devUUID: ${FFAppState().devUUID}');
debugPrint('cliUUID: ${FFAppState().cliUUID}');
debugPrint('atividade: respondeSolicitacao');
debugPrint('referencia: $refUUID');
debugPrint('tarefa: $actionValue');
debugPrint('resposta: $responseValue');
debugPrint('idVisitante: $vteUUID');
return false;
}
}
Future<void> snackbar(BuildContext context, {required bool opt}) async {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
elevation: 10,
margin: const EdgeInsets.all(50),
content: Center(
child: Text(
opt
? FFLocalizations.of(context).getText('asjd2q3k2j4l21')
: FFLocalizations.of(context).getText('asda2e42fafa'),
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.normal,
color: FlutterFlowTheme.of(context).info,
),
),
),
backgroundColor: opt
? FlutterFlowTheme.of(context).success
: FlutterFlowTheme.of(context).error,
duration: const Duration(seconds: 3),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
),
);
}
Future changeStatusAction(
BuildContext context,
String status,
String vawREF,
String msg,
String vteUUID,
) async {
debugPrint('status: $status');
switch (status) {
case 'L':
Navigator.pop(context, true);
bool? approveVisitRequest;
approveVisitRequest = await visitRequestComponentAction(
context,
actionValue: status,
refUUID: vawREF,
responseValue: msg,
vteUUID: vteUUID,
);
if (!context.mounted) return;
if (approveVisitRequest == true) {
debugPrint('Aprovado');
} else {
debugPrint('Erro ao aprovar');
}
break;
case 'B':
Navigator.pop(context, true);
bool? blockVisitRequest;
blockVisitRequest = await visitRequestComponentAction(
context,
actionValue: status,
refUUID: vawREF,
responseValue: msg,
vteUUID: vteUUID,
);
if (!context.mounted) return;
if (blockVisitRequest == true) {
debugPrint('Bloqueado');
} else {
debugPrint('Erro ao bloquear');
}
break;
default:
break;
}
}

View File

@ -1,11 +1,13 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:f_r_e_hub/app_state.dart';
import 'package:f_r_e_hub/backend/api_requests/api_calls.dart';
import 'package:f_r_e_hub/components/templates_components/access_notification_modal_template_component/access_notification_modal_template_component_widget.dart';
import 'package:flutter/material.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:rxdart/rxdart.dart';
import 'package:f_r_e_hub/components/templates_components/visit_request_template_component/visit_request_template_component_widget.dart';
@ -23,6 +25,10 @@ class PushNotificationService {
_createNotificationChannels();
}
Subject<RemoteMessage> getOnMessage() {
return _onMessage;
}
Future<void> initialize(BuildContext context) async {
_context.add(context);
await _requestPermissions();
@ -239,27 +245,14 @@ class PushNotificationService {
}
String _getChannelIdBasedOnClickAction(String clickAction) {
// Retorna o ID do canal com base no click_action
// Exemplo simples, pode ser expandido conforme necessário
switch (clickAction) {
case 'visit_request':
return 'visit_request';
case '':
return 'visit_response';
case 'access':
return 'access';
case 'mensagem':
return 'mensagem';
case 'enroll_cond':
return 'enroll_cond';
default:
return 'miscellaneous';
}
final baseId = clickAction.hashCode;
return 'channel_$baseId';
}
void _showNotification(RemoteMessage message) async {
String channelId =
_getChannelIdBasedOnClickAction(message.data['click_action']);
var androidDetails = AndroidNotificationDetails(
channelId,
'Channel Name for $channelId',
@ -271,8 +264,10 @@ class PushNotificationService {
var generalNotificationDetails =
NotificationDetails(android: androidDetails, iOS: iOSDetails);
debugPrint('Showing notification: ${message.messageId.hashCode}');
await _flutterLocalNotificationsPlugin.show(
message.hashCode,
// DateTime.now().millisecondsSinceEpoch % (1 << 31),
Random().nextInt(1 << 30),
message.notification?.title,
message.notification?.body,
generalNotificationDetails,
@ -329,6 +324,21 @@ class NotificationHandler {
}
}
String _getIdBasedOnUserType(Map<String, dynamic> message) {
// Verifica o tipo de usuário
if (message['USR_TIPO'].toString() == 'O') {
// Retorna USR_ID se não estiver vazio/nulo, caso contrário retorna '0'
return message['USR_ID'].toString().isEmpty
? '0'
: message['USR_ID'].toString();
} else {
// Retorna USR_DOCUMENTO se não estiver vazio/nulo, caso contrário retorna '0'
return message['USR_DOCUMENTO'].toString().isEmpty
? '0'
: message['USR_DOCUMENTO'].toString();
}
}
void _showAcessNotificationModal(
Map<String, dynamic> message, BuildContext context) {
debugPrint('Showing access notification dialog');
@ -338,6 +348,7 @@ class NotificationHandler {
showDialog(
context: context,
builder: (BuildContext context) {
String id = _getIdBasedOnUserType(message);
return Dialog(
backgroundColor: Colors.transparent,
child: AccessNotificationModalTemplateComponentWidget(
@ -363,6 +374,7 @@ class NotificationHandler {
showDialog(
context: context,
builder: (BuildContext context) {
String id = _getIdBasedOnUserType(message);
return Dialog(
backgroundColor: Colors.transparent,
child: VisitRequestTemplateComponentWidget(
@ -379,3 +391,21 @@ class NotificationHandler {
);
}
}
class PushNotificationManager {
final StreamController<RemoteMessage> _onMessageReceivedController =
StreamController<RemoteMessage>.broadcast();
Stream<RemoteMessage> get onMessageReceived =>
_onMessageReceivedController.stream;
PushNotificationManager() {
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
_onMessageReceivedController.add(message);
});
}
void dispose() {
_onMessageReceivedController.close();
}
}

View File

@ -28,10 +28,6 @@ class VisitRequestTemplateComponentModel
FocusNode? textFieldFocusNode5;
TextEditingController? textController5;
String? Function(BuildContext, String?)? textController5Validator;
// Stores action output result for [Action Block - visitRequestComponentAction] action in IconButton widget.
bool? blockVisitRequest;
// Stores action output result for [Action Block - visitRequestComponentAction] action in IconButton widget.
bool? approveVisitRequest;
@override
void initState(BuildContext context) {}
@ -53,35 +49,4 @@ class VisitRequestTemplateComponentModel
textFieldFocusNode5?.dispose();
textController5?.dispose();
}
/// Action blocks.
Future<bool> visitRequestComponentAction(
BuildContext context, {
required String? actionValue,
required String? refUUID,
required String? responseValue,
required String? vteUUID,
}) async {
ApiCallResponse? visitRequest;
visitRequest = await PhpGroup.respondeSolicitacaoCall.call(
userUUID: FFAppState().userUUID,
devUUID: FFAppState().devUUID,
cliUUID: FFAppState().cliUUID,
atividade: 'respondeSolicitacao',
referencia: refUUID,
tarefa: actionValue,
resposta: responseValue,
idVisitante: vteUUID,
);
if (PhpGroup.respondeSolicitacaoCall.error(
(visitRequest.jsonBody ?? ''),
) ==
false) {
return true;
} else {
return false;
}
}
}

View File

@ -1,3 +1,5 @@
import 'package:f_r_e_hub/backend/push_notification/pushNotificationService.dart';
import '/flutter_flow/flutter_flow_icon_button.dart';
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
@ -24,6 +26,7 @@ class VisitRequestTemplateComponentWidget extends StatefulWidget {
this.vawDestino,
required this.vawStatus,
this.vawDate,
this.changeStatusAction,
});
final String? vteName;
@ -40,6 +43,14 @@ class VisitRequestTemplateComponentWidget extends StatefulWidget {
final String? vawStatus;
final String? vawDate;
final Future Function(
BuildContext context,
String status,
String vawREF,
String msg,
String vteUUID,
)? changeStatusAction;
@override
State<VisitRequestTemplateComponentWidget> createState() =>
_VisitRequestTemplateComponentWidgetState();
@ -86,6 +97,7 @@ class _VisitRequestTemplateComponentWidgetState
@override
Widget build(BuildContext context) {
context.watch<FFAppState>();
bool isLoaded = false;
return Align(
alignment: const AlignmentDirectional(0.0, 0.0),
@ -681,6 +693,7 @@ class _VisitRequestTemplateComponentWidgetState
FlutterFlowIconButton(
borderRadius: 20.0,
borderWidth: 1.0,
showLoadingIndicator: isLoaded,
buttonSize: 40.0,
fillColor: FlutterFlowTheme.of(context).error,
icon: Icon(
@ -689,28 +702,18 @@ class _VisitRequestTemplateComponentWidgetState
size: 24.0,
),
onPressed: () async {
var shouldSetState = false;
_model.blockVisitRequest =
await _model.visitRequestComponentAction(
await widget.changeStatusAction?.call(
context,
actionValue: 'B',
refUUID: widget.vawRef,
responseValue: _model.textController5.text,
vteUUID: widget.vteUUID,
'B',
widget.vawRef ?? '',
_model.textController5.text,
widget.vteUUID ?? '',
);
shouldSetState = true;
if (_model.blockVisitRequest == true) {
Navigator.pop(context);
} else {
if (shouldSetState) setState(() {});
return;
}
if (shouldSetState) setState(() {});
},
),
FlutterFlowIconButton(
borderRadius: 20.0,
showLoadingIndicator: isLoaded,
borderWidth: 1.0,
buttonSize: 40.0,
fillColor: FlutterFlowTheme.of(context).success,
@ -720,24 +723,13 @@ class _VisitRequestTemplateComponentWidgetState
size: 24.0,
),
onPressed: () async {
var shouldSetState = false;
_model.approveVisitRequest =
await _model.visitRequestComponentAction(
await widget.changeStatusAction?.call(
context,
actionValue: 'L',
refUUID: widget.vawRef,
responseValue: _model.textController5.text,
vteUUID: widget.vteUUID,
'L',
widget.vawRef ?? '',
_model.textController5.text,
widget.vteUUID ?? '',
);
shouldSetState = true;
if (_model.approveVisitRequest == true) {
Navigator.pop(context);
} else {
if (shouldSetState) setState(() {});
return;
}
if (shouldSetState) setState(() {});
},
),
].divide(const SizedBox(width: 20.0)),

View File

@ -23,6 +23,6 @@ Future<String?> getDevUUID() async {
} else if (Platform.isAndroid) {
var androidDeviceInfo = await deviceInfo.androidInfo;
print(AndroidDeviceInfo);
return androidDeviceInfo.serialNumber; // unique ID on Android
return androidDeviceInfo.id; // unique ID on Android
}
}

View File

@ -1194,5 +1194,14 @@ final kTranslationsMap = <Map<String, Map<String, String>>>[
'pt': '',
'en': '',
},
// misc
'asjd2q3k2j4l21': {
'pt': 'Aprovação concluída',
'en': 'Approval completed',
},
'asda2e42fafa': {
'pt': 'Bloqueio concluído',
'en': 'Block completed',
},
},
].reduce((a, b) => a..addAll(b));

View File

@ -1,3 +1,7 @@
// import 'dart:js_interop';
import 'package:f_r_e_hub/actions/actions.dart';
import 'package:f_r_e_hub/backend/push_notification/pushNotificationService.dart';
import 'package:f_r_e_hub/components/templates_components/visit_request_template_component/visit_request_template_component_widget.dart';
import '/backend/api_requests/api_calls.dart';
@ -42,6 +46,11 @@ class _LiberationHistoryWidgetState extends State<LiberationHistoryWidget> {
super.dispose();
}
void onUpdate(BuildContext context) {
_model.clearGetLiberationsCache();
setState(() {});
}
@override
Widget build(BuildContext context) {
context.watch<FFAppState>();
@ -100,8 +109,13 @@ Widget bodyLiberationHistoryPage(
top: true,
child: Stack(
children: [
liberationDynamicListViw(context, _model),
searchBarContainer(context, _model),
liberationDynamicListView(context, _model),
Positioned(
top: 0,
left: 0,
right: 0,
bottom: 0,
child: searchBarContainer(context, _model)),
].addToStart(const SizedBox(height: 0)),
),
);
@ -198,83 +212,82 @@ Widget searchBarContainer(BuildContext context, LiberationHistoryModel _model) {
);
}
Widget liberationDynamicListViw(
BuildContext context, LiberationHistoryModel _model) {
return Expanded(
child: Container(
width: double.infinity,
height: double.infinity,
decoration: const BoxDecoration(),
child: StreamBuilder<ApiCallResponse>(
stream: _model.getLiberations(
requestFn: () => PhpGroup.getLiberationsCall.call(
devUUID: FFAppState().devUUID,
userUUID: FFAppState().userUUID,
cliID: FFAppState().cliUUID,
atividade: 'getSolicitacoes',
),
Widget liberationDynamicListView(
BuildContext context,
LiberationHistoryModel _model,
) {
return Container(
width: double.infinity,
height: double.infinity,
decoration: const BoxDecoration(),
child: StreamBuilder<ApiCallResponse>(
stream: _model.getLiberations(
requestFn: () => PhpGroup.getLiberationsCall.call(
devUUID: FFAppState().devUUID,
userUUID: FFAppState().userUUID,
cliID: FFAppState().cliUUID,
atividade: 'getSolicitacoes',
),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: SizedBox(
width: 50.0,
height: 50.0,
child: SpinKitCircle(
color: FlutterFlowTheme.of(context).primary,
size: 50.0,
),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: SizedBox(
width: 50.0,
height: 50.0,
child: SpinKitCircle(
color: FlutterFlowTheme.of(context).primary,
size: 50.0,
),
),
);
}
final columnGetLiberationsResponse = snapshot.data!;
final liberationHistory = PhpGroup.getLiberationsCall
.rqList(
columnGetLiberationsResponse.jsonBody,
)
?.toList() ??
[];
final filteredLiberationHistory = _model.textController.text.isNotEmpty
? liberationHistory
.where((item) => functions
.jsonToStr(getJsonField(
item,
r'''$.VTE_NOME''',
))
.toLowerCase()
.contains(
_model.textController.text.toLowerCase(),
))
.toList()
: liberationHistory;
return ListView.builder(
itemCount: filteredLiberationHistory.length,
addAutomaticKeepAlives: false,
addRepaintBoundaries: true,
cacheExtent: 1000.0,
itemBuilder: (BuildContext context, int index) {
final liberationHistoryItem = filteredLiberationHistory[index];
return Padding(
padding: EdgeInsets.only(
top: index == 0 ? 60.0 : 8.0,
left: 8.0,
right: 8.0,
bottom: 8.0,
),
child: Align(
alignment: AlignmentDirectional(0.0, 0.0),
child: liberationHistoryItemCard(
context,
liberationHistoryItem,
),
),
);
}
final columnGetLiberationsResponse = snapshot.data!;
final liberationHistory = PhpGroup.getLiberationsCall
.rqList(
columnGetLiberationsResponse.jsonBody,
)
?.toList() ??
[];
final filteredLiberationHistory =
_model.textController.text.isNotEmpty
? liberationHistory
.where((item) => functions
.jsonToStr(getJsonField(
item,
r'''$.VTE_NOME''',
))
.toLowerCase()
.contains(
_model.textController.text.toLowerCase(),
))
.toList()
: liberationHistory;
return ListView.builder(
itemCount: filteredLiberationHistory.length,
addAutomaticKeepAlives: false,
addRepaintBoundaries: true,
cacheExtent: 1000.0,
itemBuilder: (BuildContext context, int index) {
final liberationHistoryItem = filteredLiberationHistory[index];
return Padding(
padding: EdgeInsets.only(
top: index == 0 ? 60.0 : 8.0,
left: 8.0,
right: 8.0,
bottom: 8.0,
),
child: Align(
alignment: AlignmentDirectional(0.0, 0.0),
child: liberationHistoryItemCard(
context,
liberationHistoryItem,
),
),
);
},
);
},
),
},
);
},
),
);
}
@ -285,6 +298,7 @@ Widget liberationHistoryItemCard(
onTap: () {
showModalBottomSheet(
isScrollControlled: true,
isDismissible: true,
backgroundColor: Colors.transparent,
useSafeArea: true,
context: context,
@ -298,16 +312,38 @@ Widget liberationHistoryItemCard(
vawStatus: liberationHistoryItem['NOT_STATUS'],
vteMsg: liberationHistoryItem['NOT_MSGENVIO'],
vteUUID: liberationHistoryItem['VTE_ID'],
cliUUID: '',
msgUUID: '',
vawDestino: '',
vawUUID: '',
vawName: '',
vawRef: '',
vteDocument: '',
cliUUID: FFAppState().cliUUID,
msgUUID: liberationHistoryItem['NOT_ID'],
vawDestino: liberationHistoryItem['NOT_DESTINO'],
vawUUID: liberationHistoryItem['NOT_ID'],
vawName: liberationHistoryItem['NOT_NOME'],
vawRef: liberationHistoryItem['NOT_ID'],
changeStatusAction: changeStatusAction,
// vteDocument: liberationHistoryItem['VTE_DOCUMENTO'],
);
},
);
).then((_) {
PushNotificationManager _pushNotificationService =
PushNotificationManager();
_pushNotificationService.onMessageReceived.listen((received) {
if (received.data['click_action'] == 'cancel_request') {
debugPrint('Aprovado');
_pushNotificationService.dispose();
snackbar(context, opt: true);
context.pushReplacementNamed(
'liberationHistory',
extra: <String, dynamic>{
kTransitionInfoKey: const TransitionInfo(
hasTransition: true,
transitionType: PageTransitionType.scale,
alignment: Alignment.bottomCenter,
),
},
);
}
});
});
},
child: Card(
clipBehavior: Clip.antiAliasWithSaveLayer,
@ -341,7 +377,7 @@ Widget liberationHistoryItemCard(
).toString()}&tipo=E",
"https://storage.googleapis.com/flutterflow-io-6f20.appspot.com/projects/flutter-freaccess-hub-0xgz9q/assets/7ftdetkzc3s0/360_F_64676383_LdbmhiNM6Ypzb3FM4PPuFP9rHe7ri8Ju.jpg",
),
width: 80.0,
width: 100.0,
fit: BoxFit.cover,
),
),
@ -356,7 +392,7 @@ Widget liberationHistoryItemCard(
children: [
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Align(
alignment: const AlignmentDirectional(-1.0, -1.0),
@ -370,7 +406,7 @@ Widget liberationHistoryItemCard(
.override(
fontFamily: FlutterFlowTheme.of(context)
.bodyMediumFamily,
fontSize: 12.5,
fontSize: 15.5,
letterSpacing: 0.0,
fontWeight: FontWeight.bold,
useGoogleFonts: GoogleFonts.asMap()