Merge pull request #19 from FRE-Informatica/bugfix/fd-645

FIX: Hístorico de Visitas
This commit is contained in:
Lucas Martin Mota 2024-08-15 08:58:02 -03:00 committed by GitHub
commit 7e002981b0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 250 additions and 167 deletions

View File

@ -102,8 +102,8 @@ class _CardItemTemplateComponentWidgetState
borderRadius: BorderRadius.circular(8.0), borderRadius: BorderRadius.circular(8.0),
), ),
child: Container( child: Container(
width: 350.0, width: 350,
height: 115.0, height: 125,
decoration: BoxDecoration( decoration: BoxDecoration(
color: FlutterFlowTheme.of(context).primaryBackground, color: FlutterFlowTheme.of(context).primaryBackground,
), ),
@ -147,7 +147,6 @@ class _CardItemTemplateComponentWidgetState
fontFamily: fontFamily:
FlutterFlowTheme.of(context) FlutterFlowTheme.of(context)
.bodyMediumFamily, .bodyMediumFamily,
fontSize: screenWidth * 0.025,
letterSpacing: 0.0, letterSpacing: 0.0,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
useGoogleFonts: GoogleFonts useGoogleFonts: GoogleFonts
@ -158,12 +157,10 @@ class _CardItemTemplateComponentWidgetState
.bodyMediumFamily), .bodyMediumFamily),
color: color:
FlutterFlowTheme.of(context) FlutterFlowTheme.of(context)
.customColor6, .primaryText,
), ),
), ),
const SizedBox( const SizedBox(width: 3.0), // Espaçamento entre o label e o valor
width:
5.0), // Espaçamento entre o label e o valor
Flexible( Flexible(
child: Text( child: Text(
value, value,

View File

@ -11,18 +11,14 @@ import 'package:hub/flutter_flow/flutter_flow_widgets.dart';
import 'package:hub/flutter_flow/internationalization.dart'; import 'package:hub/flutter_flow/internationalization.dart';
import 'package:hub/flutter_flow/nav/nav.dart'; import 'package:hub/flutter_flow/nav/nav.dart';
import 'package:hub/pages/schedule_complete_visit_page/schedule_complete_visit_page_widget.dart'; import 'package:hub/pages/schedule_complete_visit_page/schedule_complete_visit_page_widget.dart';
import 'package:hub/shared/utils/validator_util.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
Widget buildDetails( Widget buildDetails(dynamic visitaWrapItem, BuildContext context, Future<dynamic> Function(BuildContext, int, int, String, String)? changeStatusAction) {
dynamic visitaWrapItem,
BuildContext context,
Future<dynamic> Function(BuildContext, int, int, String, String)?
changeStatusAction) {
return DetailsComponentWidget( return DetailsComponentWidget(
buttons: [ buttons: [
if (getStatus(visitaWrapItem['VAW_STATUS']) == if (getStatus(visitaWrapItem['VAW_STATUS']) == status.active) // REJECT ACTION
status.active) // REJECT ACTION
FFButtonWidget( FFButtonWidget(
text: FFLocalizations.of(context).getVariableText( text: FFLocalizations.of(context).getVariableText(
ptText: 'Cancelar', ptText: 'Cancelar',
@ -172,9 +168,9 @@ URL do Convite: https://visita.freaccess.com.br/${visitaWrapItem['VAW_ID']}/${vi
), ),
], ],
labelsHashMap: Map<String, String>.from({ labelsHashMap: Map<String, String>.from({
'Nome': visitaWrapItem['VTE_NOME'] ?? '', '${FFLocalizations.of(context).getVariableText(ptText: "Nome", enText: "Name")}:': visitaWrapItem['VTE_NOME'] ?? '',
'Inicio': visitaWrapItem['VAW_DTINICIO'] ?? '', '${FFLocalizations.of(context).getVariableText(ptText: "Inicio", enText: "Start")}:': visitaWrapItem['VAW_DTINICIO'] != '' && visitaWrapItem['VAW_DTINICIO'] != null ? ValidatorUtil.toLocalDateTime('yyyy-MM-dd HH:mm:ss', visitaWrapItem['VAW_DTINICIO']) : '',
'Fim': visitaWrapItem['VAW_DTFIM'] ?? '', '${FFLocalizations.of(context).getVariableText(ptText: "Fim", enText: "End")}:': visitaWrapItem['VAW_DTFIM'] != '' && visitaWrapItem['VAW_DTFIM'] != null ? ValidatorUtil.toLocalDateTime('yyyy-MM-dd HH:mm:ss', visitaWrapItem['VAW_DTFIM']) : '',
}), }),
imagePath: imagePath:
'https://freaccess.com.br/freaccess/getImage.php?cliID=${FFAppState().cliUUID}&atividade=getFoto&Documento=${visitaWrapItem['VTE_DOCUMENTO'] ?? ''}&tipo=E', 'https://freaccess.com.br/freaccess/getImage.php?cliID=${FFAppState().cliUUID}&atividade=getFoto&Documento=${visitaWrapItem['VTE_DOCUMENTO'] ?? ''}&tipo=E',

View File

@ -1,5 +1,6 @@
import 'dart:developer'; import 'dart:developer';
import 'package:easy_debounce/easy_debounce.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:hub/shared/utils/dialog_util.dart'; import 'package:hub/shared/utils/dialog_util.dart';
import 'package:hub/shared/utils/validator_util.dart'; import 'package:hub/shared/utils/validator_util.dart';
@ -49,13 +50,11 @@ class _RegisiterVistorTemplateComponentWidgetState
visitorAlreadyRegistered = BehaviorSubject<bool>.seeded(false); visitorAlreadyRegistered = BehaviorSubject<bool>.seeded(false);
_model = _model = createModel(context, () => RegisiterVistorTemplateComponentModel());
createModel(context, () => RegisiterVistorTemplateComponentModel());
_model.textController1 ??= TextEditingController(); _model.textController1 ??= TextEditingController();
_model.textFieldFocusNode1 ??= FocusNode(); _model.textFieldFocusNode1 ??= FocusNode();
log('doc: ${widget.doc}');
_model.textController2 ??= TextEditingController(); _model.textController2 ??= TextEditingController();
_model.textFieldFocusNode2 ??= FocusNode(); _model.textFieldFocusNode2 ??= FocusNode();
_model.textController2?.addListener(_onTextChanged); _model.textController2?.addListener(_onTextChanged);
@ -162,7 +161,7 @@ class _RegisiterVistorTemplateComponentWidgetState
controller: _model.textController2, controller: _model.textController2,
focusNode: _model.textFieldFocusNode2, focusNode: _model.textFieldFocusNode2,
autovalidateMode: AutovalidateMode.onUserInteraction, autovalidateMode: AutovalidateMode.onUserInteraction,
autofocus: false, autofocus: true,
textCapitalization: TextCapitalization.none, textCapitalization: TextCapitalization.none,
autofillHints: const [AutofillHints.password], autofillHints: const [AutofillHints.password],
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
@ -296,7 +295,14 @@ class _RegisiterVistorTemplateComponentWidgetState
controller: _model.textController1, controller: _model.textController1,
autovalidateMode: AutovalidateMode.onUserInteraction, autovalidateMode: AutovalidateMode.onUserInteraction,
focusNode: _model.textFieldFocusNode1, focusNode: _model.textFieldFocusNode1,
autofocus: false, onChanged: (_) =>
EasyDebounce.debounce(
'_model.textFieldFocusNode1',
const Duration(
milliseconds: 500),
() => setState(() {}),
),
autofocus: true,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
obscureText: false, obscureText: false,
decoration: InputDecoration( decoration: InputDecoration(
@ -722,7 +728,14 @@ class _RegisiterVistorTemplateComponentWidgetState
controller: _model.textController4, controller: _model.textController4,
focusNode: _model.textFieldFocusNode4, focusNode: _model.textFieldFocusNode4,
autovalidateMode: AutovalidateMode.onUserInteraction, autovalidateMode: AutovalidateMode.onUserInteraction,
autofocus: false, autofocus: true,
onChanged: (_) =>
EasyDebounce.debounce(
'_model.textFieldFocusNode4',
const Duration(
milliseconds: 500),
() => setState(() {}),
),
textInputAction: TextInputAction.done, textInputAction: TextInputAction.done,
obscureText: false, obscureText: false,
decoration: InputDecoration( decoration: InputDecoration(

View File

@ -64,7 +64,7 @@ class _VisitorSearchModalTemplateComponentWidgetState
context.watch<FFAppState>(); context.watch<FFAppState>();
return Padding( return Padding(
padding: const EdgeInsetsDirectional.fromSTEB(0.0, 50.0, 0.0, 0.0), padding: const EdgeInsetsDirectional.fromSTEB(0.0, 20.0, 0.0, 0.0),
child: Container( child: Container(
width: MediaQuery.of(context).size.width, width: MediaQuery.of(context).size.width,
decoration: BoxDecoration( decoration: BoxDecoration(
@ -78,8 +78,16 @@ class _VisitorSearchModalTemplateComponentWidgetState
), ),
child: Column( child: Column(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Container(
width: 40,
height: 5,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(10),
),
),
Padding( Padding(
padding: padding:
const EdgeInsetsDirectional.fromSTEB(16.0, 10.0, 16.0, 0.0), const EdgeInsetsDirectional.fromSTEB(16.0, 10.0, 16.0, 0.0),

View File

@ -11,188 +11,248 @@ 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_util.dart';
import 'package:hub/flutter_flow/internationalization.dart'; import 'package:hub/flutter_flow/internationalization.dart';
import 'package:hub/pages/schedule_complete_visit_page/schedule_complete_visit_page_model.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 VisitHistoryWidget extends StatefulWidget { class VisitHistoryWidget extends StatefulWidget {
VisitHistoryWidget({ VisitHistoryWidget({Key? key}) : super(key: key);
Key? key,
}) : super(key: key);
@override @override
_VisitHistoryWidgetState createState() => _VisitHistoryWidgetState(); _VisitHistoryWidgetState createState() => _VisitHistoryWidgetState();
} }
class _VisitHistoryWidgetState extends State<VisitHistoryWidget> { class _VisitHistoryWidgetState extends State<VisitHistoryWidget> with TickerProviderStateMixin {
List<dynamic> visitaWrap = []; late ScrollController _scrollController;
var pageNumber = 1; int _pageNumber = 1;
late Future<void> visitFuture; final int _pageSize = 10;
bool _hasData = false;
bool _loading = false;
late Future<void> _visitFuture;
List<dynamic> _visitWrap = [];
@override @override
void initState() { void initState() {
super.initState(); super.initState();
visitFuture = _fetchVisits(); _visitFuture = _fetchVisits();
_scrollController = ScrollController()..addListener(() {
if (_scrollController.position.atEdge && _scrollController.position.pixels != 0) {
_loadMore();
}
});
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
} }
Future<ApiCallResponse?> _fetchVisits() async { Future<ApiCallResponse?> _fetchVisits() async {
try { try {
setState(() => _loading = true);
var response = await ScheduleCompleteVisitPageModel().visitHistory( var response = await ScheduleCompleteVisitPageModel().visitHistory(
requestFn: () => PhpGroup.getVisitsCall.call( requestFn: () => PhpGroup.getVisitsCall.call(
devUUID: FFAppState().devUUID, devUUID: FFAppState().devUUID,
userUUID: FFAppState().userUUID, userUUID: FFAppState().userUUID,
cliID: FFAppState().cliUUID, cliID: FFAppState().cliUUID,
atividade: 'getVisitas', atividade: 'getVisitas',
pageSize: 10, pageSize: _pageSize,
pageNumber: pageNumber, pageNumber: _pageNumber,
), ),
); );
var newVisits = response.jsonBody['visitas'] final List<dynamic> visits = response.jsonBody['visitas'] ?? [];
as List<dynamic>?; // Ajuste conforme a estrutura da resposta
if (newVisits != null && newVisits.isNotEmpty) { if (visits != null && visits.isNotEmpty) {
safeSetState(() { setState(() {
visitaWrap.addAll(newVisits); _visitWrap.addAll(visits);
_hasData = true;
_loading = false;
}); });
return response; return response;
} else {
log('No new visits found');
return null;
} }
} catch (err) {
log('Erro ao carregar mais visitas: $err'); _showNoMoreDataSnackBar(context);
setState(() {
_hasData = false;
_loading = false;
});
return null;
} catch (e, s) {
DialogUtil.errorDefault(context);
LogUtil.requestAPIFailed("proccessRequest.php", "", "Consulta de Visitas", e, s);
setState(() {
_hasData = false;
_loading = false;
});
} }
} }
void _loadMoreVisits() { void _loadMore() {
pageNumber++; if (_hasData == true) {
visitFuture = _fetchVisits(); _pageNumber++;
_visitFuture = _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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FutureBuilder<void>(
future: visitFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting &&
visitaWrap.isEmpty) {
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
FlutterFlowTheme.of(context).primary,
),
),
);
} else if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
} else if (visitaWrap.isEmpty) {
return Center(
child: Text(
FFLocalizations.of(context).getVariableText(
ptText: 'Nenhum visitante foi encontrado',
enText: 'No visitors found',
),
),
);
}
return ListView.builder( return Column(
itemCount: visitaWrap.length + 1, mainAxisSize: MainAxisSize.max,
shrinkWrap: true, mainAxisAlignment: MainAxisAlignment.start,
physics: const BouncingScrollPhysics(), children: [
itemBuilder: (context, index) { if (_hasData == false && _pageNumber <= 1 && _loading == false)
if (index == visitaWrap.length) { Expanded(
_loadMoreVisits(); child: Column(
return Center( mainAxisAlignment: MainAxisAlignment.center,
child: CircularProgressIndicator( mainAxisSize: MainAxisSize.max,
valueColor: AlwaysStoppedAnimation<Color>( children: [
FlutterFlowTheme.of(context).primary, Center(
), child: Text(FFLocalizations.of(context).getVariableText(
), ptText: "Nenhuma visita encontrada!",
); enText: "No visit found")),
} )
final visitaWrapItem = visitaWrap[index];
return CardItemTemplateComponentWidget(
imagePath:
'https://freaccess.com.br/freaccess/getImage.php?devUUID=${FFAppState().devUUID}&userUUID=${FFAppState().userUUID}&cliID=${FFAppState().cliUUID}&atividade=getFoto&Documento=${visitaWrapItem['VTE_DOCUMENTO'] ?? ''}&tipo=E',
labelsHashMap: {
'Nome:': visitaWrapItem['VTE_NOME'] ?? '',
'Inicio:': visitaWrapItem['VAW_DTINICIO'] ?? '',
'Fim:': visitaWrapItem['VAW_DTFIM'] ?? '',
},
statusHashMap: [
if (getStatus(visitaWrapItem['VAW_STATUS']) == status.active)
{
FFLocalizations.of(context).getVariableText(
ptText: 'Ativo',
enText: 'Active',
): FlutterFlowTheme.of(context).warning,
},
if (getStatus(visitaWrapItem['VAW_STATUS']) == status.finished)
{
FFLocalizations.of(context).getVariableText(
ptText: 'Finalizado',
enText: 'Finished',
): FlutterFlowTheme.of(context).success,
},
if (getStatus(visitaWrapItem['VAW_STATUS']) == status.unknown)
{
FFLocalizations.of(context).getVariableText(
ptText: 'Desconhecido',
enText: 'Unknown',
): FlutterFlowTheme.of(context).alternate,
},
if (getStatus(visitaWrapItem['VAW_STATUS']) == status.canceled)
{
FFLocalizations.of(context).getVariableText(
ptText: 'Cancelado',
enText: 'Canceled',
): FlutterFlowTheme.of(context).error,
},
if (getStatus(visitaWrapItem['VAW_STATUS']) == status.blocked)
{
FFLocalizations.of(context).getVariableText(
ptText: 'Bloqueado',
enText: 'Blocked',
): FlutterFlowTheme.of(context).error,
},
if (getStatus(visitaWrapItem['VAW_STATUS']) == status.inactive)
{
FFLocalizations.of(context).getVariableText(
ptText: 'Inativo',
enText: 'Inactive',
): FlutterFlowTheme.of(context).error,
},
], ],
onTapCardItemAction: () async { ),
await showDialog( )
useSafeArea: true, else if (_hasData == true || _pageNumber >= 1)
context: context, Expanded(
builder: (context) { child: FutureBuilder<void>(
return Dialog( future: _visitFuture,
alignment: Alignment.center, builder: (context, snapshot) {
child: buildDetails( return ListView.builder(
visitaWrapItem, shrinkWrap: true,
context, physics: const BouncingScrollPhysics(),
changeStatusAction, controller: _scrollController,
), itemCount: _visitWrap.length,
); itemBuilder: (context, index) {
}, final item = _visitWrap[index];
).whenComplete(() { return _item(context, item);
// updateVisitFuture();
_fetchVisits().then((response) {
safeSetState(() {
visitaWrap = PhpGroup.getVisitsCall
.visitasList(response!.jsonBody)
?.toList() ??
[];
visitFuture = Future.value(response);
}); });
});
}).catchError((err, stack) {});
}, },
)
),
if (_hasData == true && _loading == true)
Container(
padding: const EdgeInsets.only(top: 15, bottom: 15),
child: Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
FlutterFlowTheme.of(context).primary,
),
),
),
)
].addToStart(const SizedBox(height: 0)),
);
}
Widget _item(BuildContext context, dynamic visitaWrapItem) {
return CardItemTemplateComponentWidget(
imagePath:
'https://freaccess.com.br/freaccess/getImage.php?devUUID=${FFAppState().devUUID}&userUUID=${FFAppState().userUUID}&cliID=${FFAppState().cliUUID}&atividade=getFoto&Documento=${visitaWrapItem['VTE_DOCUMENTO'] ?? ''}&tipo=E',
labelsHashMap: {
'${FFLocalizations.of(context).getVariableText(ptText: "Nome", enText: "Name")}:': visitaWrapItem['VTE_NOME'] ?? '',
'${FFLocalizations.of(context).getVariableText(ptText: "Inicio", enText: "Start")}:': visitaWrapItem['VAW_DTINICIO'] != '' && visitaWrapItem['VAW_DTINICIO'] != null ? ValidatorUtil.toLocalDateTime('yyyy-MM-dd HH:mm:ss', visitaWrapItem['VAW_DTINICIO']) : '',
'${FFLocalizations.of(context).getVariableText(ptText: "Fim", enText: "End")}:': visitaWrapItem['VAW_DTFIM'] != '' && visitaWrapItem['VAW_DTFIM'] != null ? ValidatorUtil.toLocalDateTime('yyyy-MM-dd HH:mm:ss', visitaWrapItem['VAW_DTFIM']) : '',
},
statusHashMap: [
if (getStatus(visitaWrapItem['VAW_STATUS']) == status.active)
{
FFLocalizations.of(context).getVariableText(
ptText: 'Ativo',
enText: 'Active',
): FlutterFlowTheme.of(context).warning,
},
if (getStatus(visitaWrapItem['VAW_STATUS']) == status.finished)
{
FFLocalizations.of(context).getVariableText(
ptText: 'Finalizado',
enText: 'Finished',
): FlutterFlowTheme.of(context).success,
},
if (getStatus(visitaWrapItem['VAW_STATUS']) == status.unknown)
{
FFLocalizations.of(context).getVariableText(
ptText: 'Desconhecido',
enText: 'Unknown',
): FlutterFlowTheme.of(context).alternate,
},
if (getStatus(visitaWrapItem['VAW_STATUS']) == status.canceled)
{
FFLocalizations.of(context).getVariableText(
ptText: 'Cancelado',
enText: 'Canceled',
): FlutterFlowTheme.of(context).error,
},
if (getStatus(visitaWrapItem['VAW_STATUS']) == status.blocked)
{
FFLocalizations.of(context).getVariableText(
ptText: 'Bloqueado',
enText: 'Blocked',
): FlutterFlowTheme.of(context).error,
},
if (getStatus(visitaWrapItem['VAW_STATUS']) == status.inactive)
{
FFLocalizations.of(context).getVariableText(
ptText: 'Inativo',
enText: 'Inactive',
): FlutterFlowTheme.of(context).error,
},
],
onTapCardItemAction: () async {
await showDialog(
useSafeArea: true,
context: context,
builder: (context) {
return Dialog(
alignment: Alignment.center,
child: buildDetails(
visitaWrapItem,
context,
changeStatusAction,
),
); );
}, },
); ).whenComplete(() {
safeSetState(() {
_pageNumber = 1;
_visitWrap = [];
_visitFuture = _fetchVisits();
});
}).catchError((e, s) {
DialogUtil.errorDefault(context);
LogUtil.requestAPIFailed("proccessRequest.php", "", "Consulta de Visitas", e, s);
safeSetState(() {
_hasData = false;
_loading = false;
});
});
}, },
); );
} }

View File

@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
class ValidatorUtil { class ValidatorUtil {
@ -23,4 +25,11 @@ class ValidatorUtil {
return dateTime.toIso8601String(); return dateTime.toIso8601String();
} }
static String toLocalDateTime(String format, String value) {
DateFormat dateFormat = DateFormat(format);
DateTime dateTime = dateFormat.parse(value);
return DateFormat('dd/MM/yyyy HH:mm:ss').format(dateTime);
}
} }