Merge branch 'feat/fd-1023' into feat/fd-905
This commit is contained in:
commit
a89edb0ae4
|
@ -59,6 +59,43 @@ class PhpGroup {
|
|||
static GetResidentsByProperty getResidentsByProperty = GetResidentsByProperty();
|
||||
static GetOpenedVisits getOpenedVisits = GetOpenedVisits();
|
||||
static GetLicense getLicense = GetLicense();
|
||||
static GetProvSchedules getProvSchedules = GetProvSchedules();
|
||||
}
|
||||
|
||||
class GetProvSchedules {
|
||||
Future<ApiCallResponse> call(final String page) async {
|
||||
// final String baseUrl = PhpGroup.getBaseUrl();
|
||||
// final String devUUID = (await StorageHelper().g(KeychainStorageKey.devUUID.value)) ?? '';
|
||||
// final String userUUID = (await StorageHelper().g(KeychainStorageKey.userUUID.value)) ?? '';
|
||||
// final String cliID = (await StorageHelper().g(KeychainStorageKey.clientUUID.value)) ?? '';
|
||||
// const String atividade = 'getProvSchedules';
|
||||
// const String pageSize = '10';
|
||||
final String baseUrl = 'http://localhost:3000';
|
||||
return await ApiManager.instance.makeApiCall(
|
||||
callName: 'getProvSchedules',
|
||||
apiUrl: '$baseUrl/getAgendamentoProv.php',
|
||||
callType: ApiCallType.POST,
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
params: {
|
||||
"proId": "8",
|
||||
"status": "AT",
|
||||
"page": page,
|
||||
"pageSize": "10"
|
||||
// 'devUUID': devUUID,
|
||||
// 'userUUID': userUUID,
|
||||
// 'cliID': cliID,
|
||||
// 'atividade': atividade,
|
||||
// 'page': page,
|
||||
// 'pageSize': pageSize,
|
||||
},
|
||||
bodyType: BodyType.X_WWW_FORM_URL_ENCODED,
|
||||
returnBody: true,
|
||||
encodeBodyUtf8: false,
|
||||
decodeUtf8: false,
|
||||
cache: false,
|
||||
alwaysAllowBody: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GetOpenedVisits {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
export 'data_sources/index.dart';
|
||||
export 'repositories/index.dart';
|
||||
export 'models/index.dart';
|
|
@ -0,0 +1,3 @@
|
|||
export 'entities/index.dart';
|
||||
export 'respositories/index.dart';
|
||||
export 'usecases/index.dart';
|
|
@ -0,0 +1,3 @@
|
|||
export 'data/index.dart';
|
||||
export 'domain/index.dart';
|
||||
export 'presentation/index.dart';
|
|
@ -0,0 +1,3 @@
|
|||
export 'blocs/index.dart';
|
||||
export 'pages/index.dart';
|
||||
export 'widgets/index.dart';
|
|
@ -0,0 +1,3 @@
|
|||
export 'data_sources/index.dart';
|
||||
export 'repositories/index.dart';
|
||||
export 'models/index.dart';
|
|
@ -0,0 +1,3 @@
|
|||
export 'entities/index.dart';
|
||||
export 'respositories/index.dart';
|
||||
export 'usecases/index.dart';
|
|
@ -0,0 +1,3 @@
|
|||
export 'data/index.dart';
|
||||
export 'domain/index.dart';
|
||||
export 'presentation/index.dart';
|
|
@ -0,0 +1,117 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hub/backend/api_requests/api_manager.dart';
|
||||
import 'package:hub/flutter_flow/nav/nav.dart';
|
||||
import 'package:hub/shared/utils/dialog_util.dart';
|
||||
import 'package:hub/shared/utils/log_util.dart';
|
||||
|
||||
class HistoryBloc extends Bloc<HistoryEvent, HistoryState> {
|
||||
final ScrollController scrollController = ScrollController();
|
||||
final Future<ApiCallResponse> Function(int pageSize, int pageNumber, Map<String, String> opt) fetchHistoryService;
|
||||
final bool Function(dynamic item, Map<String, String> opt)? filterLogic;
|
||||
|
||||
HistoryBloc(Map<String, String> opt, this.fetchHistoryService, {this.filterLogic}) : super(HistoryState(opt: opt)) {
|
||||
scrollController.addListener(_onScroll);
|
||||
on<UpdateHistoryEvent>(_onUpdateHistory);
|
||||
on<FetchHistoryEvent>(_onFetchHistory);
|
||||
add(FetchHistoryEvent());
|
||||
}
|
||||
|
||||
void _onScroll() {
|
||||
if (scrollController.position.atEdge && scrollController.position.pixels != 0) {
|
||||
add(FetchHistoryEvent());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onUpdateHistory(UpdateHistoryEvent event, Emitter<HistoryState> emit) async {
|
||||
emit(state.copyWith(opt: event.newOpt, historyWrap: [], pageNumber: 1));
|
||||
add(FetchHistoryEvent());
|
||||
}
|
||||
|
||||
Future<void> _onFetchHistory(FetchHistoryEvent event, Emitter<HistoryState> emit) async {
|
||||
if (state.loading) return;
|
||||
emit(state.copyWith(loading: true));
|
||||
try {
|
||||
var response = await fetchHistoryService(state.pageSize, state.pageNumber, state.opt);
|
||||
|
||||
final List<dynamic> history = response.jsonBody['acessos'] ?? [];
|
||||
List<dynamic> filteredHistory = history.where((item) {
|
||||
if (filterLogic != null) {
|
||||
return filterLogic!(item, state.opt);
|
||||
} else {
|
||||
return true; // Default to no filtering if no logic is provided
|
||||
}
|
||||
}).toList();
|
||||
|
||||
if (filteredHistory.isNotEmpty) {
|
||||
emit(state.copyWith(
|
||||
historyWrap: [...state.historyWrap, ...filteredHistory],
|
||||
hasData: true,
|
||||
loading: false,
|
||||
pageNumber: state.pageNumber + 1,
|
||||
));
|
||||
} else {
|
||||
emit(state.copyWith(hasData: false, loading: false));
|
||||
}
|
||||
} catch (e, s) {
|
||||
await DialogUtil.errorDefault(navigatorKey.currentContext!);
|
||||
LogUtil.requestAPIFailed('processRequest', "", "Fetch History", e, s);
|
||||
emit(state.copyWith(hasData: false, loading: false, error: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
scrollController.dispose();
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
|
||||
class HistoryState {
|
||||
final Map<String, String> opt;
|
||||
final List<dynamic> historyWrap;
|
||||
final bool hasData;
|
||||
final bool loading;
|
||||
final int pageNumber;
|
||||
final int pageSize;
|
||||
final String? error;
|
||||
|
||||
HistoryState({
|
||||
required this.opt,
|
||||
this.historyWrap = const [],
|
||||
this.hasData = false,
|
||||
this.loading = false,
|
||||
this.pageNumber = 1,
|
||||
this.pageSize = 10,
|
||||
this.error,
|
||||
});
|
||||
|
||||
HistoryState copyWith({
|
||||
Map<String, String>? opt,
|
||||
List<dynamic>? historyWrap,
|
||||
bool? hasData,
|
||||
bool? loading,
|
||||
int? pageNumber,
|
||||
int? pageSize,
|
||||
String? error,
|
||||
}) {
|
||||
return HistoryState(
|
||||
opt: opt ?? this.opt,
|
||||
historyWrap: historyWrap ?? this.historyWrap,
|
||||
hasData: hasData ?? this.hasData,
|
||||
loading: loading ?? this.loading,
|
||||
pageNumber: pageNumber ?? this.pageNumber,
|
||||
pageSize: pageSize ?? this.pageSize,
|
||||
error: error ?? this.error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class HistoryEvent {}
|
||||
|
||||
class UpdateHistoryEvent extends HistoryEvent {
|
||||
final Map<String, String> newOpt;
|
||||
UpdateHistoryEvent(this.newOpt);
|
||||
}
|
||||
|
||||
class FetchHistoryEvent extends HistoryEvent {}
|
|
@ -0,0 +1,2 @@
|
|||
export 'history_bloc_template.dart';
|
||||
export 'provisional_history_bloc.dart';
|
|
@ -0,0 +1,3 @@
|
|||
export 'blocs/index.dart';
|
||||
export 'pages/index.dart';
|
||||
export 'widgets/index.dart';
|
|
@ -0,0 +1,106 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hub/backend/api_requests/api_calls.dart';
|
||||
import 'package:hub/components/molecular_components/message_opt_modal/opt_modal_widget.dart';
|
||||
import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart';
|
||||
import 'package:hub/features/history/index.dart';
|
||||
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
||||
import 'package:hub/flutter_flow/internationalization.dart';
|
||||
import 'package:hub/shared/helpers/storage/base_storage.dart';
|
||||
import 'package:hub/shared/helpers/storage/storage_helper.dart';
|
||||
|
||||
class AcessCubit extends Cubit<String?> {
|
||||
AcessCubit() : super('Provisional');
|
||||
|
||||
Future<void> fetchCliUUID() async {
|
||||
final cliUUID = await StorageHelper().get(KeychainStorageKey.clientUUID.value);
|
||||
emit(cliUUID);
|
||||
}
|
||||
|
||||
Future<ApiCallResponse> fetchHistoryService(int pageSize, int pageNumber, Map<String, String> opt) async {
|
||||
return await PhpGroup.getAccessCall.call(
|
||||
pageSize: pageSize.toString(),
|
||||
pageNumber: pageNumber.toString(),
|
||||
pesTipo: opt['personType'] != 'E' && opt['personType'] != 'O' ? 'T' : opt['personType'],
|
||||
);
|
||||
}
|
||||
|
||||
bool filterLogic(dynamic item, Map<String, String> opt) {
|
||||
final personTypeMatches = opt['personType'] == '.*' || item["PES_TIPO"].toString() == opt['personType'];
|
||||
return personTypeMatches;
|
||||
}
|
||||
}
|
||||
|
||||
class AccessHistoryPage extends StatelessWidget {
|
||||
final Map<String, String> opt = {
|
||||
'personType': '.*',
|
||||
'accessType': '.*',
|
||||
'search': '.*',
|
||||
};
|
||||
|
||||
final String appBarTitle = 'Histórico Provisório';
|
||||
final String noHistoryMessage = 'Nenhum histórico encontrado!';
|
||||
final String errorMessage = 'Falha ao realizar operação!';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => AcessCubit()..fetchCliUUID(),
|
||||
child: HistoryPageTemplate(
|
||||
opt: opt,
|
||||
fetchHistoryService: (pageSize, pageNumber, opt) => context.read<AcessCubit>().fetchHistoryService(pageSize, pageNumber, opt),
|
||||
cardBuilder: cardBuilder,
|
||||
appBarTitle: appBarTitle,
|
||||
noHistoryMessage: noHistoryMessage,
|
||||
errorMessage: errorMessage,
|
||||
isLeading: true,
|
||||
isFilter: true,
|
||||
filterLogic: (item, opt) => context.read<AcessCubit>().filterLogic(item, opt),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget cardBuilder(BuildContext context, dynamic historyItem) {
|
||||
final cliUUID = context.watch<AcessCubit>().state;
|
||||
final String imagePath = 'https://freaccess.com.br/freaccess/getImage.php?cliID=${cliUUID}&atividade=getFoto&Documento=${historyItem['PES_ID'] ?? ''}&tipo=${historyItem['PES_TIPO'] ?? ''}';
|
||||
final Map<String, String> labelsHashMap = {
|
||||
FFLocalizations.of(context).getVariableText(ptText: 'Nome:', enText: 'Name:'): historyItem['PES_NOME'] ?? '',
|
||||
FFLocalizations.of(context).getVariableText(ptText: 'Acesso:', enText: 'Access:'): historyItem['ACE_DATAHORA'] ?? '',
|
||||
FFLocalizations.of(context).getVariableText(ptText: 'Setor', enText: 'Sector'): historyItem['SET_DESCRICAO'] ?? '',
|
||||
};
|
||||
|
||||
final statusHashMap = [
|
||||
_getPersonTypeStatus(context, historyItem['PES_TIPO']),
|
||||
_getAccessTypeStatus(context, historyItem['ACE_TIPO']),
|
||||
];
|
||||
|
||||
return CardItemTemplateComponentWidget(
|
||||
imagePath: imagePath,
|
||||
labelsHashMap: labelsHashMap,
|
||||
statusHashMap: statusHashMap,
|
||||
onTapCardItemAction: () async {},
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, Color> _getPersonTypeStatus(BuildContext context, String? personType) {
|
||||
switch (personType) {
|
||||
case 'O':
|
||||
return {FFLocalizations.of(context).getVariableText(ptText: 'Morador', enText: 'Resident'): FlutterFlowTheme.of(context).alternate2};
|
||||
case 'E':
|
||||
return {FFLocalizations.of(context).getVariableText(ptText: 'Visitante', enText: 'Visitor'): FlutterFlowTheme.of(context).alternate2};
|
||||
default:
|
||||
return {FFLocalizations.of(context).getVariableText(ptText: 'Desconhecido', enText: 'Unknown'): FlutterFlowTheme.of(context).alternate2};
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Color> _getAccessTypeStatus(BuildContext context, String? accessType) {
|
||||
switch (accessType) {
|
||||
case '0':
|
||||
return {FFLocalizations.of(context).getVariableText(ptText: 'Entrada', enText: 'Entrance'): FlutterFlowTheme.of(context).success};
|
||||
case '1':
|
||||
return {FFLocalizations.of(context).getVariableText(ptText: 'Saída', enText: 'Exit'): FlutterFlowTheme.of(context).error};
|
||||
default:
|
||||
return {FFLocalizations.of(context).getVariableText(ptText: 'Desconhecido', enText: 'Unknown'): FlutterFlowTheme.of(context).warning};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,211 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:hub/features/history/index.dart';
|
||||
import 'package:hub/flutter_flow/internationalization.dart';
|
||||
import 'package:hub/flutter_flow/nav/nav.dart';
|
||||
import 'package:hub/backend/api_requests/api_manager.dart';
|
||||
import 'package:hub/features/history/presentation/widgets/filter_modal.dart';
|
||||
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
||||
import 'package:hub/shared/utils/dialog_util.dart';
|
||||
import 'package:hub/shared/utils/log_util.dart';
|
||||
import 'package:hub/flutter_flow/flutter_flow_icon_button.dart';
|
||||
|
||||
class HistoryPageTemplate extends StatelessWidget {
|
||||
final Map<String, String> opt;
|
||||
final Future<ApiCallResponse> Function(int pageSize, int pageNumber, Map<String, String> opt) fetchHistoryService;
|
||||
final Widget Function(BuildContext context, dynamic historyItem) cardBuilder;
|
||||
final String appBarTitle;
|
||||
final String noHistoryMessage;
|
||||
final String errorMessage;
|
||||
final bool isLeading;
|
||||
final bool isFilter;
|
||||
final bool Function(dynamic item, Map<String, String> opt)? filterLogic;
|
||||
|
||||
const HistoryPageTemplate({
|
||||
Key? key,
|
||||
required this.opt,
|
||||
required this.fetchHistoryService,
|
||||
required this.cardBuilder,
|
||||
this.appBarTitle = 'History',
|
||||
this.noHistoryMessage = 'No history found!',
|
||||
this.errorMessage = 'Failed to perform operation!',
|
||||
this.isLeading = true,
|
||||
this.isFilter = false,
|
||||
this.filterLogic,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => HistoryBloc(opt, fetchHistoryService, filterLogic: filterLogic),
|
||||
child: HistoryPageView(
|
||||
cardBuilder: cardBuilder,
|
||||
appBarTitle: appBarTitle,
|
||||
noHistoryMessage: noHistoryMessage,
|
||||
errorMessage: errorMessage,
|
||||
isLeading: isLeading,
|
||||
isFilter: isFilter,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HistoryPageView extends StatelessWidget {
|
||||
final Widget Function(BuildContext context, dynamic historyItem) cardBuilder;
|
||||
final String appBarTitle;
|
||||
final String noHistoryMessage;
|
||||
final String errorMessage;
|
||||
final bool isLeading;
|
||||
final bool isFilter;
|
||||
|
||||
const HistoryPageView({
|
||||
Key? key,
|
||||
required this.cardBuilder,
|
||||
required this.appBarTitle,
|
||||
required this.noHistoryMessage,
|
||||
required this.errorMessage,
|
||||
this.isLeading = false,
|
||||
this.isFilter = false,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = FlutterFlowTheme.of(context);
|
||||
return Scaffold(
|
||||
key: GlobalKey<ScaffoldState>(),
|
||||
backgroundColor: theme.primaryBackground,
|
||||
appBar: _buildAppBar(context, theme),
|
||||
body: _buildBody(context),
|
||||
);
|
||||
}
|
||||
|
||||
PreferredSizeWidget _buildAppBar(BuildContext context, FlutterFlowTheme theme) {
|
||||
return AppBar(
|
||||
backgroundColor: theme.primaryBackground,
|
||||
automaticallyImplyLeading: false,
|
||||
leading: isLeading ? _buildBackButton(context, theme) : null,
|
||||
title: _buildTitle(context, theme),
|
||||
centerTitle: true,
|
||||
elevation: 0.0,
|
||||
actions: [
|
||||
if (isFilter) _buildFilterButton(context),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBackButton(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 _buildTitle(BuildContext context, FlutterFlowTheme theme) {
|
||||
return Text(
|
||||
appBarTitle,
|
||||
style: theme.headlineMedium.override(
|
||||
fontFamily: theme.headlineMediumFamily,
|
||||
color: theme.primaryText,
|
||||
fontSize: 16.0,
|
||||
letterSpacing: 0.0,
|
||||
useGoogleFonts: GoogleFonts.asMap().containsKey(theme.headlineMediumFamily),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFilterButton(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: (BuildContext bottomSheetContext) {
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.of(bottomSheetContext).pop(),
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: GestureDetector(
|
||||
onTap: () {},
|
||||
child: FilterModalWidget(
|
||||
defaultSelections: {
|
||||
'personType': context.read<HistoryBloc>().state.opt['personType'] == '.*' ? ['E', 'O'] : [context.read<HistoryBloc>().state.opt['personType']],
|
||||
'search': '.*',
|
||||
},
|
||||
filterOptions: {
|
||||
'personType': [
|
||||
{'title': FFLocalizations.of(context).getVariableText(ptText: 'Visitante', enText: 'Visitor'), 'value': 'E',},
|
||||
{'title': FFLocalizations.of(context).getVariableText(ptText: 'Morador', enText: 'Resident'), 'value': 'O',},
|
||||
|
||||
],
|
||||
},
|
||||
filterTitles: {
|
||||
'personType': 'Person Type',
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (selectedFilter != null) {
|
||||
context.read<HistoryBloc>().add(UpdateHistoryEvent(selectedFilter));
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(BuildContext context) {
|
||||
return BlocBuilder<HistoryBloc, HistoryState>(
|
||||
builder: (context, state) {
|
||||
if (state.loading && state.historyWrap.isEmpty) {
|
||||
return Center(
|
||||
child: CircularProgressIndicator(
|
||||
valueColor: AlwaysStoppedAnimation<Color>(
|
||||
FlutterFlowTheme.of(context).primary,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else if (state.error != null) {
|
||||
return Center(
|
||||
child: Text(errorMessage),
|
||||
);
|
||||
} else if (state.historyWrap.isEmpty) {
|
||||
return Center(
|
||||
child: Text(noHistoryMessage),
|
||||
);
|
||||
} else {
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
controller: context.read<HistoryBloc>().scrollController,
|
||||
itemCount: state.historyWrap.length,
|
||||
itemBuilder: (context, index) {
|
||||
final historyItem = state.historyWrap[index];
|
||||
return cardBuilder(context, historyItem);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
export 'history_page_template.dart';
|
||||
export 'provisional_history_page.dart';
|
|
@ -0,0 +1,105 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hub/backend/api_requests/api_calls.dart';
|
||||
import 'package:hub/components/molecular_components/message_opt_modal/opt_modal_widget.dart';
|
||||
import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart';
|
||||
import 'package:hub/features/history/index.dart';
|
||||
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
||||
import 'package:hub/flutter_flow/internationalization.dart';
|
||||
import 'package:hub/shared/helpers/storage/base_storage.dart';
|
||||
import 'package:hub/shared/helpers/storage/storage_helper.dart';
|
||||
|
||||
class ProvisionalCubit extends Cubit<String?> {
|
||||
ProvisionalCubit() : super('Provisional');
|
||||
|
||||
Future<void> fetchCliUUID() async {
|
||||
final cliUUID = await StorageHelper().get(KeychainStorageKey.clientUUID.value);
|
||||
emit(cliUUID);
|
||||
}
|
||||
|
||||
Future<ApiCallResponse> fetchHistoryService(int pageSize, int pageNumber, Map<String, String> status) async {
|
||||
return await PhpGroup.getProvSchedules(pageNumber.toString());
|
||||
}
|
||||
|
||||
bool filterLogic(dynamic item, Map<String, String> opt) {
|
||||
final personTypeMatches = opt['personType'] == '.*' || item["PES_TIPO"].toString() == opt['personType'];
|
||||
return personTypeMatches;
|
||||
}
|
||||
}
|
||||
|
||||
class ProvisionalHistoryPage extends StatelessWidget {
|
||||
final Map<String, String> opt = {
|
||||
'personType': '.*',
|
||||
'accessType': '.*',
|
||||
'search': '.*',
|
||||
};
|
||||
|
||||
final String appBarTitle = 'Histórico Provisório';
|
||||
final String noHistoryMessage = 'Nenhum histórico encontrado!';
|
||||
final String errorMessage = 'Falha ao realizar operação!';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => ProvisionalCubit()..fetchCliUUID(),
|
||||
child: HistoryPageTemplate(
|
||||
opt: opt,
|
||||
fetchHistoryService: (pageSize, pageNumber, opt) => context.read<ProvisionalCubit>().fetchHistoryService(pageSize, pageNumber, opt),
|
||||
cardBuilder: cardBuilder,
|
||||
appBarTitle: appBarTitle,
|
||||
noHistoryMessage: noHistoryMessage,
|
||||
errorMessage: errorMessage,
|
||||
isLeading: true,
|
||||
isFilter: false,
|
||||
filterLogic: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget cardBuilder(BuildContext context, dynamic historyItem) {
|
||||
log('historyItem: $historyItem');
|
||||
final cliUUID = context.watch<ProvisionalCubit>().state;
|
||||
final String imagePath = 'https://freaccess.com.br/freaccess/getImage.php?cliID=${cliUUID}&atividade=getFoto&Documento=${historyItem['PES_ID'] ?? ''}&tipo=${historyItem['PES_TIPO'] ?? ''}';
|
||||
final Map<String, String> labelsHashMap = {
|
||||
FFLocalizations.of(context).getVariableText(ptText: 'Nome:', enText: 'Name:'): historyItem['PES_NOME'] ?? '',
|
||||
FFLocalizations.of(context).getVariableText(ptText: 'Acesso:', enText: 'Access:'): historyItem['ACE_DATAHORA'] ?? '',
|
||||
FFLocalizations.of(context).getVariableText(ptText: 'Setor', enText: 'Sector'): historyItem['SET_DESCRICAO'] ?? '',
|
||||
};
|
||||
|
||||
final statusHashMap = [
|
||||
_getPersonTypeStatus(context, historyItem['PES_TIPO']),
|
||||
_getAccessTypeStatus(context, historyItem['ACE_TIPO']),
|
||||
];
|
||||
|
||||
return CardItemTemplateComponentWidget(
|
||||
imagePath: imagePath,
|
||||
labelsHashMap: labelsHashMap,
|
||||
statusHashMap: statusHashMap,
|
||||
onTapCardItemAction: () async {},
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, Color> _getPersonTypeStatus(BuildContext context, String? personType) {
|
||||
switch (personType) {
|
||||
case 'O':
|
||||
return {FFLocalizations.of(context).getVariableText(ptText: 'Morador', enText: 'Resident'): FlutterFlowTheme.of(context).alternate2};
|
||||
case 'E':
|
||||
return {FFLocalizations.of(context).getVariableText(ptText: 'Visitante', enText: 'Visitor'): FlutterFlowTheme.of(context).alternate2};
|
||||
default:
|
||||
return {FFLocalizations.of(context).getVariableText(ptText: 'Desconhecido', enText: 'Unknown'): FlutterFlowTheme.of(context).alternate2};
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Color> _getAccessTypeStatus(BuildContext context, String? accessType) {
|
||||
switch (accessType) {
|
||||
case '0':
|
||||
return {FFLocalizations.of(context).getVariableText(ptText: 'Entrada', enText: 'Entrance'): FlutterFlowTheme.of(context).success};
|
||||
case '1':
|
||||
return {FFLocalizations.of(context).getVariableText(ptText: 'Saída', enText: 'Exit'): FlutterFlowTheme.of(context).error};
|
||||
default:
|
||||
return {FFLocalizations.of(context).getVariableText(ptText: 'Desconhecido', enText: 'Unknown'): FlutterFlowTheme.of(context).warning};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import '/flutter_flow/flutter_flow_util.dart';
|
||||
import '/flutter_flow/form_field_controller.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
||||
import 'package:hub/flutter_flow/nav/nav.dart';
|
||||
import 'package:hub/shared/utils/limited_text_size.dart';
|
||||
|
||||
|
||||
|
||||
class FilterModal extends FlutterFlowModel<FilterModalWidget> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
class FilterModalWidget extends StatefulWidget {
|
||||
final Map<String, dynamic> defaultSelections;
|
||||
final Map<String, List<Map<String, String>>> filterOptions;
|
||||
final Map<String, String> filterTitles;
|
||||
|
||||
const FilterModalWidget({
|
||||
super.key,
|
||||
required this.defaultSelections,
|
||||
required this.filterOptions,
|
||||
required this.filterTitles,
|
||||
});
|
||||
|
||||
@override
|
||||
_FilterModalWidgetState createState() => _FilterModalWidgetState();
|
||||
}
|
||||
|
||||
class _FilterModalWidgetState extends State<FilterModalWidget> {
|
||||
late FilterModal _model;
|
||||
late Map<String, dynamic> selected;
|
||||
|
||||
@override
|
||||
void setState(VoidCallback callback) {
|
||||
super.setState(callback);
|
||||
_model.onUpdate();
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_model = createModel(context, () => FilterModal());
|
||||
|
||||
_model.textController ??= TextEditingController();
|
||||
_model.textFieldFocusNode ??= FocusNode();
|
||||
|
||||
selected = 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]!.first;
|
||||
});
|
||||
|
||||
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]!.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,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
double screenWidth = MediaQuery.of(context).size.width;
|
||||
|
||||
return Center(
|
||||
child: Container(
|
||||
width: screenWidth - (screenWidth * 0.35),
|
||||
height: 250,
|
||||
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'),
|
||||
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).getText('88kshkph'),
|
||||
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),
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export 'filter_modal.dart';
|
|
@ -4,6 +4,7 @@ import 'dart:io';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hub/backend/schema/util/schema_util.dart';
|
||||
import 'package:hub/features/history/presentation/pages/provisional_history_page.dart';
|
||||
import 'package:hub/features/home/index.dart';
|
||||
import 'package:hub/features/property/index.dart';
|
||||
import 'package:hub/flutter_flow/flutter_flow_util.dart';
|
||||
|
@ -130,7 +131,7 @@ GoRouter createRouter(AppStateNotifier appStateNotifier) {
|
|||
FFRoute(name: 'petsOnThePropertyPage', path: '/petsOnThePropertyPage', builder: (context, params) => Scaffold(body: const PetsHistoryScreen(isApp: true))),
|
||||
FFRoute(name: 'vehiclesOnThePropertyPage', path: '/vehiclesOnThePropertyPage', builder: (context, params) => const VehicleOnTheProperty()),
|
||||
FFRoute(name: 'receptionPage', path: '/receptionPage', builder: (context, params) => const ReceptionPageWidget()),
|
||||
FFRoute(name: 'messageHistoryPage', path: '/messageHistoryPage', builder: (context, params) => const MessageHistoryPageWidget()),
|
||||
FFRoute(name: 'messageHistoryPage', path: '/messageHistoryPage', builder: (context, params) => ProvisionalHistoryPage()),
|
||||
FFRoute(name: 'registerVisitorPage', path: '/registerVisitorPage', builder: (context, params) => const RegisterVisitorPageWidget()),
|
||||
FFRoute(name: 'scheduleCompleteVisitPage', path: '/scheduleCompleteVisitPage', builder: (context, params) => const ScheduleCompleteVisitPageWidget()),
|
||||
FFRoute(name: 'deliverySchedule', path: '/deliverySchedule', builder: (context, params) => const DeliverySchedule()),
|
||||
|
|
|
@ -169,8 +169,7 @@ class _AppState extends State<App> {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
|
||||
|
||||
FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
|
||||
_appStateNotifier = AppStateNotifier.instance;
|
||||
_router = createRouter(_appStateNotifier);
|
||||
|
|
|
@ -77,7 +77,8 @@ class LocalsRepositoryImpl implements LocalsRepository {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<void> unlinkLocal(BuildContext context) {
|
||||
return remoteDataSource.detachLocal(context);
|
||||
Future<void> unlinkLocal(BuildContext context) async {
|
||||
await remoteDataSource.detachLocal(context);
|
||||
await select(context);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,6 +90,7 @@ class LocalUtil {
|
|||
await StorageHelper().set(KeychainStorageKey.petAmount.value,
|
||||
jsonBody['petAmountRegister']?.toString().isEmpty ?? true ? '0' : jsonBody['petAmountRegister'].toString());
|
||||
await StorageHelper().set(KeychainStorageKey.userName.value, jsonBody['visitado']['VDO_NOME'] ?? '');
|
||||
await StorageHelper().set(KeychainStorageKey.userEmail.value, jsonBody['visitado']['VDO_EMAIL'] ?? '');
|
||||
|
||||
final bool isNewVersion = jsonBody['newVersion'] ?? false;
|
||||
await StorageHelper().set(KeychainStorageKey.isNewVersion.value, isNewVersion);
|
||||
|
|
|
@ -82,7 +82,6 @@ class LicenseLocalDataSourceImpl implements LicenseLocalDataSource {
|
|||
@override
|
||||
Future<void> clear() async {
|
||||
await DatabaseStorage.database.delete(tableLicense);
|
||||
await DatabaseStorage.database.rawDelete(
|
||||
'DELETE FROM sqlite_sequence WHERE name = "$tableLicense"');
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,8 +33,7 @@ class DatabaseStorage {
|
|||
onUpgrade: _onUpgrade,
|
||||
onDowngrade: _onDowngrade,
|
||||
onOpen: _onOpen,
|
||||
onConfigure: _onConfigure,
|
||||
singleInstance: true,
|
||||
onConfigure: _onConfigure,
|
||||
);
|
||||
await LicenseRepositoryImpl().updateLicense();
|
||||
isInitialized = true;
|
||||
|
@ -62,7 +61,7 @@ class DatabaseStorage {
|
|||
}
|
||||
|
||||
Future<void> _onDowngrade(Database database, int oldVersion, int newVersion) async {
|
||||
print('Downgrading database from version $oldVersion to $newVersion...');
|
||||
print('Downgrading database from version $oldVersion to $newVersion...');
|
||||
if (oldVersion >= 2 && newVersion < 2) {
|
||||
await _dropTables(database);
|
||||
}
|
||||
|
@ -70,9 +69,9 @@ class DatabaseStorage {
|
|||
|
||||
Future<void> _executePragmas(Database database) async {
|
||||
// await database.execute('PRAGMA journal_mode = WAL;');
|
||||
await database.execute('PRAGMA synchronous = NORMAL;');
|
||||
await database.execute('PRAGMA temp_store = MEMORY;');
|
||||
await database.execute('PRAGMA foreign_keys = ON;');
|
||||
// await database.execute('PRAGMA synchronous = NORMAL;');
|
||||
// await database.execute('PRAGMA temp_store = MEMORY;');
|
||||
// await database.execute('PRAGMA foreign_keys = ON;');
|
||||
}
|
||||
|
||||
Future<void> _createTables(Database database, int version) async {
|
||||
|
|
|
@ -79,6 +79,7 @@ enum KeychainStorageKey {
|
|||
userDevUUID,
|
||||
status,
|
||||
userName,
|
||||
userEmail,
|
||||
clientUUID,
|
||||
ownerName,
|
||||
ownerUUID,
|
||||
|
@ -100,6 +101,8 @@ enum KeychainStorageKey {
|
|||
extension KeychainStorageKeyExtension on KeychainStorageKey {
|
||||
String get value {
|
||||
switch (this) {
|
||||
case KeychainStorageKey.userEmail:
|
||||
return 'fre_userEmail';
|
||||
case KeychainStorageKey.devUUID:
|
||||
return 'fre_devUUID';
|
||||
case KeychainStorageKey.userUUID:
|
||||
|
|
|
@ -59,8 +59,7 @@ class KeychainHelper implements BaseStorage {
|
|||
Future<void> clearAll() async {
|
||||
try {
|
||||
await DatabaseStorage.database.delete(tableKeychain);
|
||||
await DatabaseStorage.database.rawDelete(
|
||||
'DELETE FROM sqlite_sequence WHERE name = "$tableKeychain"');
|
||||
|
||||
|
||||
} catch (e, s) {
|
||||
log('() => clearAll keychain: $e', stackTrace: s);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:hub/shared/helpers/storage/base_storage.dart';
|
||||
|
||||
|
@ -23,7 +25,12 @@ class SecureStorage implements BaseStorage {
|
|||
|
||||
@override
|
||||
Future<void> set<T>(String key, T value) async {
|
||||
await _secureStorage.write(key: key, value: value.toString());
|
||||
log('Setting value: $value');
|
||||
try {
|
||||
await _secureStorage.write(key: key, value: value.toString());
|
||||
} catch (e, s) {
|
||||
log('Error setting value: $e', stackTrace: s);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -33,6 +40,15 @@ class SecureStorage implements BaseStorage {
|
|||
|
||||
@override
|
||||
Future<void> clearAll() async {
|
||||
await _secureStorage.deleteAll();
|
||||
log('Clearing SecureStorage');
|
||||
try{
|
||||
await _secureStorage.deleteAll(
|
||||
aOptions: AndroidOptions(),
|
||||
iOptions: IOSOptions(),
|
||||
);
|
||||
} catch (e, s) {
|
||||
log('Error clearing all: $e', stackTrace: s);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hub/features/home/presentation/pages/home_page.dart';
|
||||
import 'package:hub/flutter_flow/nav/nav.dart';
|
||||
import 'package:hub/shared/components/molecules/modules/data/repositories/license_repository_impl.dart';
|
||||
|
@ -14,6 +15,8 @@ import '../../../backend/api_requests/api_calls.dart';
|
|||
import '../../../flutter_flow/flutter_flow_util.dart';
|
||||
import '../../../flutter_flow/random_data_util.dart';
|
||||
import '../../components/molecules/locals/data/index.dart';
|
||||
import '../../components/molecules/locals/index.dart';
|
||||
import '../../components/molecules/menu/index.dart';
|
||||
import '../../utils/device_util.dart';
|
||||
import '../../utils/dialog_util.dart';
|
||||
import '../../utils/log_util.dart';
|
||||
|
@ -152,6 +155,7 @@ class AuthenticationService {
|
|||
await LicenseRepositoryImpl().cleanLicense();
|
||||
DatabaseStorage.isInitialized = false;
|
||||
await StorageHelper().init();
|
||||
|
||||
context.go('/welcomePage', extra: extra);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue