This commit is contained in:
J. A. Messias 2025-01-30 19:33:04 -03:00
parent 1d4041f92b
commit 742312e888
1 changed files with 0 additions and 779 deletions

View File

@ -1,779 +0,0 @@
import 'dart:async';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart';
import 'package:hub/features/backend/index.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/snackbar_util.dart';
import 'package:rxdart/rxdart.dart';
import '/flutter_flow/form_field_controller.dart';
import 'package:hub/flutter_flow/nav/nav.dart';
import 'package:hub/shared/utils/limited_text_size.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hub/features/storage/index.dart';
/// [ProvisionalHistoryEvent]
class ProvisionalHistoryEvent {}
/// [LoadProvisionalHistory]
class LoadProvisionalHistory extends ProvisionalHistoryEvent {}
/// [ProvisionalHistoryStateBloc]
class ProvisionalHistoryStateBloc {
final String devUUID;
final String userUUID;
final String cliUUID;
final bool isLoading;
ProvisionalHistoryStateBloc({
required this.devUUID,
required this.userUUID,
required this.cliUUID,
this.isLoading = false,
});
ProvisionalHistoryStateBloc copyWith({
String? devUUID,
String? userUUID,
String? cliUUID,
bool? isLoading,
}) {
return ProvisionalHistoryStateBloc(
devUUID: devUUID ?? this.devUUID,
userUUID: userUUID ?? this.userUUID,
cliUUID: cliUUID ?? this.cliUUID,
isLoading: isLoading ?? this.isLoading,
);
}
}
/// [ProvisionalHistoryBloc]
class ProvisionalHistoryBloc
extends Bloc<ProvisionalHistoryEvent, ProvisionalHistoryStateBloc> {
ProvisionalHistoryBloc()
: super(ProvisionalHistoryStateBloc(
devUUID: '', userUUID: '', cliUUID: '')) {
on<LoadProvisionalHistory>(_onLoadProvisionalHistory);
}
Future<void> _onLoadProvisionalHistory(
LoadProvisionalHistory event,
Emitter<ProvisionalHistoryStateBloc> emit,
) async {
emit(state.copyWith(isLoading: true));
final devUUID =
(await StorageHelper().get(ProfileStorageKey.devUUID.key)) ?? '';
final userUUID =
(await StorageHelper().get(ProfileStorageKey.userUUID.key)) ?? '';
final cliUUID =
(await StorageHelper().get(ProfileStorageKey.clientUUID.key)) ?? '';
emit(state.copyWith(
devUUID: devUUID,
userUUID: userUUID,
cliUUID: cliUUID,
isLoading: false));
}
}
/// [ProvisionalHistoryPage]
@immutable
// ignore: must_be_immutable
class ProvisionalHistoryPage extends StatefulWidget {
Map<String, String> opt;
ProvisionalHistoryPage({super.key, Map<String, String>? opt})
: opt = opt ?? const {'AGP_STATUS': '.*'};
@override
State<ProvisionalHistoryPage> createState() => ProvisionalHistoryState(opt);
}
/// [ProvisionalHistoryState]
class ProvisionalHistoryState extends State<ProvisionalHistoryPage> {
final BehaviorSubject<Map<String, String>> selectedTypeSubject;
late ScrollController _scrollController;
final scaffoldKey = GlobalKey<ScaffoldState>();
bool _isSubjectClosed = false;
int _pageNumber = 1;
bool hasData = false;
bool _loading = false;
String status = '.*';
late Future<void> future;
List<dynamic> wrap = [];
ProvisionalHistoryState(Map<String, String> opt)
: selectedTypeSubject = BehaviorSubject.seeded(opt) {
selectedTypeSubject.listen((value) {});
}
@override
void initState() {
super.initState();
future = fetchHistoryService();
_scrollController = ScrollController()
..addListener(() {
if (_scrollController.position.atEdge &&
_scrollController.position.pixels != 0) {
_loadMore();
}
});
}
@override
void dispose() {
selectedTypeSubject.close();
_isSubjectClosed = true;
super.dispose();
}
@override
Widget build(BuildContext context) {
final theme = FlutterFlowTheme.of(context);
return Scaffold(
key: scaffoldKey,
backgroundColor: FlutterFlowTheme.of(context).primaryBackground,
appBar: _appBar(context, theme),
body: _body(context),
);
}
PreferredSizeWidget _appBar(BuildContext context, FlutterFlowTheme theme) {
return AppBar(
backgroundColor: theme.primaryBackground,
automaticallyImplyLeading: false,
leading: _backButton(context, theme),
title: _title(context, theme),
centerTitle: true,
elevation: 0.0,
actions: [_filterButton(context)],
);
}
Widget _backButton(BuildContext context, FlutterFlowTheme theme) {
return FlutterFlowIconButton(
key: ValueKey<String>('BackNavigationAppBar'),
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 _title(BuildContext context, FlutterFlowTheme theme) {
return Text(
FFLocalizations.of(context).getVariableText(
ptText: 'Consultar Agendas',
enText: 'Provisional History',
),
style: FlutterFlowTheme.of(context).headlineMedium.override(
fontFamily: FlutterFlowTheme.of(context).headlineMediumFamily,
color: FlutterFlowTheme.of(context).primaryText,
fontSize: 16.0,
fontWeight: FontWeight.bold,
letterSpacing: 0.0,
useGoogleFonts: GoogleFonts.asMap()
.containsKey(FlutterFlowTheme.of(context).headlineMediumFamily),
),
);
}
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<String, String>? selectedFilter =
await showModalBottomSheet<Map<String, String>>(
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: FilterWidget(
defaultSelections: selectedTypeSubject.value,
filterOptions: {
'AGP_STATUS': [
{
'title': FFLocalizations.of(context)
.getVariableText(
ptText: 'Ativo',
enText: 'Active',
),
'value': 'AT',
},
{
'title': FFLocalizations.of(context)
.getVariableText(
ptText: 'Concluído',
enText: 'Completed',
),
'value': 'CO',
},
{
'title': FFLocalizations.of(context)
.getVariableText(
ptText: 'Inativo',
enText: 'Inactive',
),
'value': 'IN',
},
{
'title': FFLocalizations.of(context)
.getVariableText(
ptText: 'Aguardando Aprovação',
enText: 'Awaiting Approval',
),
'value': 'AA',
},
],
},
filterTitles: {'AGP_STATUS': ''},
),
),
),
);
});
if (selectedFilter != null) {
_updateHistoryAction(selectedFilter);
}
},
),
),
],
);
}
void _updateHistoryAction(Map<String, String> newType) {
if (!_isSubjectClosed) {
final currentType = selectedTypeSubject.value;
final updatedType = Map<String, String>.from(currentType);
bool needsUpdate = false;
newType.forEach((key, newValue) {
if (currentType[key] != newValue) {
updatedType[key] = newValue;
needsUpdate = true;
}
});
if (needsUpdate) {
selectedTypeSubject.add(updatedType);
fetchCardListViewService(updatedType);
safeSetState(() {});
}
}
}
Future<ApiCallResponse?> fetchHistoryService() async {
try {
setState(() => _loading = true);
var response =
await PhpGroup.getProvSchedules(_pageNumber.toString(), status);
final List<dynamic> history =
response.jsonBody['agendamento']['value'] ?? [];
if (history.isNotEmpty) {
setState(() {
wrap.addAll(history);
hasData = true;
_loading = false;
});
return response;
}
SnackBarUtil.showNoMoreDataSnackbar(context);
setState(() {
hasData = false;
_loading = false;
});
return null;
} catch (e, s) {
await DialogUtil.errorDefault(context);
LogUtil.requestAPIFailed('processRequest', "", "Busca Acesso", e, s);
setState(() {
hasData = false;
_loading = false;
});
}
return null;
}
Widget _body(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
if (hasData == false && _pageNumber <= 1 && _loading == false)
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
Center(
child: Text(
FFLocalizations.of(context).getVariableText(
ptText: "Nenhum histórico encontrado!",
enText: "No history found!"),
)),
],
),
)
else if (hasData || _pageNumber >= 1)
Expanded(child: _cardListViewOrganismWidget()),
if (hasData == true && _loading)
Container(
padding: const EdgeInsets.only(top: 15, bottom: 15),
child: Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
FlutterFlowTheme.of(context).primary,
),
),
),
)
],
);
}
void _loadMore() {
if (hasData == true) {
_pageNumber++;
future = fetchHistoryService();
}
}
void fetchCardListViewService(Map<String, String> select) {
status = select['AGP_STATUS']!;
wrap = [];
_pageNumber = 1;
future = fetchHistoryService();
}
Widget _cardListViewOrganismWidget() {
return FutureBuilder<void>(
future: future,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting &&
wrap.isEmpty) {
return Center(
child: SizedBox(
width: 50.0,
height: 50.0,
child: SpinKitCircle(
color: FlutterFlowTheme.of(context).primary,
size: 50.0,
),
),
);
} else if (snapshot.hasError) {
return Center(
child: Text(FFLocalizations.of(context).getVariableText(
ptText: "Falha ao efetuar operação!",
enText: "Failed to perform operation!")),
);
}
return ListView.builder(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
controller: _scrollController,
itemCount: wrap.length,
itemBuilder: (context, index) {
final historyItem = wrap[index];
return _historyCardMoleculeWidget(context, historyItem);
},
);
},
);
}
Widget _historyCardMoleculeWidget(BuildContext context, dynamic historyItem) {
return CardItemTemplateComponentWidget(
imagePath: null,
labelsHashMap: _buildLabelsHashMap(context, historyItem),
statusHashMap: _buildStatusHashMap(context, historyItem),
onTapCardItemAction: () async {},
);
}
String _imageUrlAtomWidget(String document, String type) {
return valueOrDefault<String>(
"https://freaccess.com.br/freaccess/getImage.php?&cliID=&atividade=getFoto&Documento=$document&tipo=$type",
"https://storage.googleapis.com/flutterflow-io-6f20.appspot.com/projects/flutter-freaccess-hub-0xgz9q/assets/7ftdetkzc3s0/360_F_64676383_LdbmhiNM6Ypzb3FM4PPuFP9rHe7ri8Ju.jpg",
);
}
Map<String, String> _buildLabelsHashMap(
BuildContext context, dynamic historyItem) {
return {
FFLocalizations.of(context).getVariableText(
ptText: 'Nome:',
enText: 'Name:',
): historyItem['AGP_NOME'] ?? '',
FFLocalizations.of(context).getVariableText(
ptText: 'Vencimento',
enText: 'Expiration',
): formatDate(historyItem['AGP_DT_VISITA']),
FFLocalizations.of(context).getVariableText(
ptText: 'Observação:',
enText: 'Observation:',
): formatObs(historyItem['AGP_OBSERVACAO']),
};
}
String formatObs(String? obs) {
if (obs == null || obs.isEmpty) {
return FFLocalizations.of(context).getVariableText(
ptText: 'Não fornecida',
enText: 'No provided',
);
}
return obs;
}
String formatDate(String dateString) {
DateTime dateTime = DateTime.parse(dateString);
return "${dateTime.day.toString().padLeft(2, '0')}/${dateTime.month.toString().padLeft(2, '0')}/${dateTime.year} ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}";
}
List<Map<String, Color>> _buildStatusHashMap(
BuildContext context, dynamic historyItem) {
return [
{
FFLocalizations.of(context).getVariableText(
ptText: 'Visitante',
enText: 'Visitor',
): FlutterFlowTheme.of(context).alternate2,
},
_getStatusMap(context, historyItem)
];
}
Map<String, Color> _getStatusMap(BuildContext context, dynamic json) {
late Map<String, Color> statusColorMap;
log(DateTime.parse(json['AGP_DT_VISITA']).toString());
log(DateTime.now().toString());
final DateTime now = DateTime.now();
final DateTime date = DateTime.parse(json['AGP_DT_VISITA']);
final bool isExpired = now.isAfter(date);
final String statusMap = json['AGP_STATUS'];
switch (statusMap) {
case 'AT':
return isExpired
? {
FFLocalizations.of(context).getVariableText(
ptText: 'Vencido',
enText: 'Expired',
): FlutterFlowTheme.of(context).error,
}
: {
FFLocalizations.of(context).getVariableText(
ptText: 'Ativo',
enText: 'Active',
): FlutterFlowTheme.of(context).success,
};
case 'CO':
return {
FFLocalizations.of(context).getVariableText(
ptText: 'Concluido',
enText: 'Completed',
): Colors.blue,
};
case 'IN':
return {
FFLocalizations.of(context).getVariableText(
ptText: 'Inativo',
enText: 'Inactive',
): FlutterFlowTheme.of(context).error,
};
case 'AA':
return {
FFLocalizations.of(context).getVariableText(
ptText: 'Aguardando Aprovação',
enText: 'Awaiting Approval',
): FlutterFlowTheme.of(context).warning,
};
default:
return {
FFLocalizations.of(context).getVariableText(
ptText: 'Desconhecido',
enText: 'Unknown',
): FlutterFlowTheme.of(context).alternate2,
};
}
}
}
/// [FilterModel]
class FilterModel extends FlutterFlowModel<FilterWidget> {
FocusNode? textFieldFocusNode;
TextEditingController? textController;
String? Function(BuildContext, String?)? textControllerValidator;
bool? checkboxValue1;
bool? checkboxValue2;
FormFieldController<List<String>>? checkboxGroupValueController;
List<String>? get checkboxGroupValues => checkboxGroupValueController?.value;
set checkboxGroupValues(List<String>? v) =>
checkboxGroupValueController?.value = v;
@override
void initState(BuildContext context) {}
@override
void dispose() {
textFieldFocusNode?.dispose();
textController?.dispose();
}
}
/// [FilterWidget]
class FilterWidget extends StatefulWidget {
final Map<String, dynamic> defaultSelections;
final Map<String, List<Map<String, String>>> filterOptions;
final Map<String, String> filterTitles;
const FilterWidget({
super.key,
required this.defaultSelections,
required this.filterOptions,
required this.filterTitles,
});
@override
_FilterWidgetState createState() => _FilterWidgetState();
}
/// [_FilterWidgetState]
class _FilterWidgetState extends State<FilterWidget> {
late FilterModel _model;
late Map<String, dynamic> selected;
@override
void setState(VoidCallback callback) {
super.setState(callback);
_model.onUpdate();
}
@override
void initState() {
super.initState();
_model = createModel(context, () => FilterModel());
_model.textController ??= TextEditingController();
_model.textFieldFocusNode ??= FocusNode();
selected = Map<String, dynamic>.from(widget.defaultSelections);
}
void _applyFilter() {
Map<String, String> filterResult = {
'search': _model.textController?.text == ''
? '.*'
: _model.textController!.text.toLowerCase(),
};
widget.filterOptions.forEach((key, options) {
filterResult[key] = selected[key]!.isEmpty || selected[key]!.length < 1
? '.*'
: selected[key]!;
});
setState(() {
// Update the state with the new filter result
selected = filterResult;
});
context.pop(filterResult);
}
Widget _buildCheckboxListTile(
String key, List<Map<String, String>> options, double fontsize) {
double limitedInputFontSize = LimitedFontSizeUtil.getInputFontSize(context);
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(
widget.filterTitles[key]!,
textAlign: TextAlign.left,
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily: FlutterFlowTheme.of(context).bodyMediumFamily,
fontSize: limitedInputFontSize,
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: limitedInputFontSize,
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] = option['value'];
}
} else {
selected[key] = '';
}
});
},
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,
);
},
),
],
);
}
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
return Center(
child: Container(
width: screenWidth - (screenWidth * 0.35),
height: screenWidth,
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).getVariableText(
ptText: 'Filtros',
enText: 'Filters',
),
style: FlutterFlowTheme.of(context)
.headlineMedium
.override(
fontFamily: FlutterFlowTheme.of(context)
.headlineMediumFamily,
color: FlutterFlowTheme.of(context).primaryText,
fontSize:
LimitedFontSizeUtil.getHeaderFontSize(context),
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: widget.filterOptions.keys.map((key) {
return _buildCheckboxListTile(
key, widget.filterOptions[key]!, 14);
}).toList(),
),
),
),
),
ElevatedButton(
onPressed: _applyFilter,
style: ElevatedButton.styleFrom(
foregroundColor: FlutterFlowTheme.of(context).info,
backgroundColor: FlutterFlowTheme.of(context).primary,
),
child: Text(
FFLocalizations.of(context).getVariableText(
ptText: 'Aplicar',
enText: 'Apply',
),
style: FlutterFlowTheme.of(context).bodyMedium.override(
fontFamily:
FlutterFlowTheme.of(context).bodyMediumFamily,
color: FlutterFlowTheme.of(context).info,
fontSize:
LimitedFontSizeUtil.getInputFontSize(context),
letterSpacing: 0.0,
fontWeight: FontWeight.bold,
useGoogleFonts: GoogleFonts.asMap().containsKey(
FlutterFlowTheme.of(context).bodyMediumFamily),
)),
),
],
),
),
),
);
}
}