refactor: visit synchronisation
parent
f2f3bf22fa
commit
a42c0847b8
|
|
@ -1,3 +0,0 @@
|
||||||
export 'example_bloc.dart';
|
|
||||||
export 'example_event.dart';
|
|
||||||
export 'example_state.dart';
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
import 'package:bloc/bloc.dart';
|
|
||||||
import 'package:mobdr/network/api_provider.dart';
|
|
||||||
import './bloc.dart';
|
|
||||||
|
|
||||||
class ExampleBloc extends Bloc<ExampleEvent, ExampleState> {
|
|
||||||
ExampleBloc() : super(InitialExampleState()) {
|
|
||||||
on<GetExample>(_getExample);
|
|
||||||
on<PostExample>(_postExample);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _getExample(GetExample event, Emitter<ExampleState> emit) async {
|
|
||||||
ApiProvider _apiProvider = ApiProvider();
|
|
||||||
|
|
||||||
emit(ExampleWaiting());
|
|
||||||
try {
|
|
||||||
String data = await _apiProvider.getExample(event.apiToken);
|
|
||||||
emit(GetExampleSuccess(exampleData: data));
|
|
||||||
} catch (ex) {
|
|
||||||
if (ex != 'cancel') {
|
|
||||||
emit(ExampleError(errorMessage: ex.toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _postExample(PostExample event, Emitter<ExampleState> emit) async {
|
|
||||||
ApiProvider _apiProvider = ApiProvider();
|
|
||||||
|
|
||||||
emit(ExampleWaiting());
|
|
||||||
try {
|
|
||||||
String data = await _apiProvider.postExample(event.id, event.apiToken);
|
|
||||||
emit(PostExampleSuccess(exampleData: data));
|
|
||||||
} catch (ex) {
|
|
||||||
if (ex != 'cancel') {
|
|
||||||
emit(ExampleError(errorMessage: ex.toString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
import 'package:meta/meta.dart';
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
abstract class ExampleEvent {}
|
|
||||||
|
|
||||||
class GetExample extends ExampleEvent {
|
|
||||||
final apiToken;
|
|
||||||
GetExample({@required this.apiToken});
|
|
||||||
}
|
|
||||||
|
|
||||||
class PostExample extends ExampleEvent {
|
|
||||||
final String id;
|
|
||||||
final apiToken;
|
|
||||||
PostExample({required this.id, required this.apiToken});
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
import 'package:meta/meta.dart';
|
|
||||||
|
|
||||||
@immutable
|
|
||||||
abstract class ExampleState {}
|
|
||||||
|
|
||||||
class InitialExampleState extends ExampleState {}
|
|
||||||
|
|
||||||
class ExampleError extends ExampleState {
|
|
||||||
final String errorMessage;
|
|
||||||
|
|
||||||
ExampleError({
|
|
||||||
required this.errorMessage,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class ExampleWaiting extends ExampleState {}
|
|
||||||
|
|
||||||
class GetExampleSuccess extends ExampleState {
|
|
||||||
final String exampleData;
|
|
||||||
GetExampleSuccess({required this.exampleData});
|
|
||||||
}
|
|
||||||
|
|
||||||
class PostExampleSuccess extends ExampleState {
|
|
||||||
final String exampleData;
|
|
||||||
PostExampleSuccess({required this.exampleData});
|
|
||||||
}
|
|
||||||
|
|
@ -71,7 +71,9 @@ Future<void> main() async {
|
||||||
Wakelock.enable();
|
Wakelock.enable();
|
||||||
|
|
||||||
eventBus.on().listen((event) {
|
eventBus.on().listen((event) {
|
||||||
LoggerUtil.logVerbose('${DateTime.now()} Event: $event');
|
if (!(event is EmptyEvent)) {
|
||||||
|
LoggerUtil.logNStackInfo('${DateTime.now()} Event: $event');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
runApp(MyApp());
|
runApp(MyApp());
|
||||||
|
|
|
||||||
|
|
@ -54,8 +54,8 @@ class VisitModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<List<VisitModel>> getPreviousVisits() async {
|
static Future<List<VisitModel>> getPreviousVisits() async {
|
||||||
// Retrieve all previsous visits from the database
|
// Retrieve all previous visits from the database
|
||||||
final visits = await objectbox.getPreviousVisit();
|
final visits = await objectbox.getActivePreviousVisit();
|
||||||
|
|
||||||
// Map each retrieved visit to VisiteModel
|
// Map each retrieved visit to VisiteModel
|
||||||
final visitModelList = visits
|
final visitModelList = visits
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,3 @@
|
||||||
/*
|
|
||||||
This is api provider
|
|
||||||
This page is used to get data from API
|
|
||||||
*/
|
|
||||||
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
|
@ -11,10 +6,12 @@ import 'package:crypto/crypto.dart';
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:http_parser/http_parser.dart';
|
import 'package:http_parser/http_parser.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
import 'package:mobdr/config/constant.dart';
|
import 'package:mobdr/config/constant.dart';
|
||||||
import 'package:mobdr/main.dart';
|
import 'package:mobdr/main.dart';
|
||||||
import 'package:mobdr/service/shared_prefs.dart';
|
import 'package:mobdr/service/shared_prefs.dart';
|
||||||
|
import 'package:mobdr/service/logger_util.dart';
|
||||||
|
|
||||||
class ApiProvider {
|
class ApiProvider {
|
||||||
Dio dio = Dio();
|
Dio dio = Dio();
|
||||||
|
|
@ -210,6 +207,12 @@ class ApiProvider {
|
||||||
|
|
||||||
/// Synchronize all informations about store, competitor, calendar
|
/// Synchronize all informations about store, competitor, calendar
|
||||||
Future<String> SyncCalendar() async {
|
Future<String> SyncCalendar() async {
|
||||||
|
DateTime now = DateTime.now();
|
||||||
|
DateTime end = now.add(Duration(days: 7));
|
||||||
|
|
||||||
|
String formattedStartDate = DateFormat('yyyyMMdd').format(now);
|
||||||
|
String formattedEndDate = DateFormat('yyyyMMdd').format(end);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final responseFutures = Future.wait([
|
final responseFutures = Future.wait([
|
||||||
getCrud(
|
getCrud(
|
||||||
|
|
@ -231,7 +234,11 @@ class ApiProvider {
|
||||||
ApiConstants.externalEndpoint +
|
ApiConstants.externalEndpoint +
|
||||||
ApiConstants.restEndpoint +
|
ApiConstants.restEndpoint +
|
||||||
'/mobDR/visite/calendrier',
|
'/mobDR/visite/calendrier',
|
||||||
{"id_utilisateur": 6, "start": 20230101, "end": 20230531},
|
{
|
||||||
|
"id_utilisateur": SharedPrefs().id_utilisateur,
|
||||||
|
"start": formattedStartDate,
|
||||||
|
"end": formattedEndDate
|
||||||
|
},
|
||||||
),
|
),
|
||||||
getCrud(
|
getCrud(
|
||||||
ApiConstants.baseUrl +
|
ApiConstants.baseUrl +
|
||||||
|
|
@ -313,38 +320,6 @@ class ApiProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Synchronize "Photos"
|
|
||||||
Future<String> SyncPhotos() async {
|
|
||||||
Response response;
|
|
||||||
|
|
||||||
try {
|
|
||||||
/// get "Photo typologies"
|
|
||||||
response = await getCrud(
|
|
||||||
ApiConstants.baseUrl +
|
|
||||||
ApiConstants.externalEndpoint +
|
|
||||||
ApiConstants.restEndpoint +
|
|
||||||
'/mobDR/visite/typologie',
|
|
||||||
null);
|
|
||||||
|
|
||||||
if (response.statusCode == STATUS_OK) {
|
|
||||||
// remove all objects
|
|
||||||
objectbox.PhotoTypologyBox.removeAll();
|
|
||||||
|
|
||||||
/// fill box "Photo typologies"
|
|
||||||
objectbox.addPhotoTypologies(response.data['typologies']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// all ok
|
|
||||||
if (response.statusCode == STATUS_OK) {
|
|
||||||
return 'OK';
|
|
||||||
} else {
|
|
||||||
return response.statusMessage ?? 'Unknow error ...';
|
|
||||||
}
|
|
||||||
} catch (ex) {
|
|
||||||
return ex.toString(); // return ex.response!.data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<int> uploadPhotoServlet(int id_visite, String photoPath) async {
|
Future<int> uploadPhotoServlet(int id_visite, String photoPath) async {
|
||||||
try {
|
try {
|
||||||
final url = Uri.parse(SERVLET_API);
|
final url = Uri.parse(SERVLET_API);
|
||||||
|
|
@ -476,161 +451,13 @@ class ApiProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getExample(apiToken) async {
|
/// Synchronize all informations about store, competitor, calendar
|
||||||
Response response;
|
Future<String> SyncLog() async {
|
||||||
|
try {
|
||||||
response =
|
LoggerUtil.logNStacktackDebug("Synchronisation LOG à implementer !!");
|
||||||
await getConnect(ApiConstants.baseUrl + '/example/getData', apiToken);
|
return 'OK';
|
||||||
print('res : ' + response.toString());
|
} catch (ex) {
|
||||||
return response.data.toString();
|
return ex.toString();
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> postExample(String id, apiToken) async {
|
|
||||||
Response response;
|
|
||||||
|
|
||||||
var postData = {'id': id};
|
|
||||||
response = await postConnect(
|
|
||||||
ApiConstants.baseUrl + '/example/postData', postData, apiToken);
|
|
||||||
print('res : ' + response.toString());
|
|
||||||
return response.data.toString();
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
Future<List<StudentModel>> getStudent(String sessionId, apiToken) async {
|
|
||||||
var postData = {'session_id': sessionId};
|
|
||||||
response = await postConnect(
|
|
||||||
ApiConstants.baseUrl + '/student/getStudent', postData, apiToken);
|
|
||||||
if (response.data['status'] == STATUS_OK) {
|
|
||||||
List responseList = response.data['data'];
|
|
||||||
List<StudentModel> listData =
|
|
||||||
responseList.map((f) => StudentModel.fromJson(f)).toList();
|
|
||||||
return listData;
|
|
||||||
} else {
|
|
||||||
throw response.data['msg'];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
Future<List<dynamic>> addStudent(
|
|
||||||
String sessionId,
|
|
||||||
String studentName,
|
|
||||||
String studentPhoneNumber,
|
|
||||||
String studentGender,
|
|
||||||
String studentAddress,
|
|
||||||
apiToken) async {
|
|
||||||
var postData = {
|
|
||||||
'session_id': sessionId,
|
|
||||||
'student_name': studentName,
|
|
||||||
'student_phone_number': studentPhoneNumber,
|
|
||||||
'student_gender': studentGender,
|
|
||||||
'student_address': studentAddress,
|
|
||||||
};
|
|
||||||
Response response;
|
|
||||||
|
|
||||||
response = await postConnect(
|
|
||||||
ApiConstants.baseUrl + '/student/addStudent', postData, apiToken);
|
|
||||||
if (response.data['status'] == STATUS_OK) {
|
|
||||||
List<dynamic> respList = [];
|
|
||||||
respList.add(response.data['msg']);
|
|
||||||
respList.add(response.data['data']['id']);
|
|
||||||
return respList;
|
|
||||||
} else {
|
|
||||||
throw response.data['msg'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> editStudent(
|
|
||||||
String sessionId,
|
|
||||||
int studentId,
|
|
||||||
String studentName,
|
|
||||||
String studentPhoneNumber,
|
|
||||||
String studentGender,
|
|
||||||
String studentAddress,
|
|
||||||
apiToken) async {
|
|
||||||
var postData = {
|
|
||||||
'session_id': sessionId,
|
|
||||||
'student_id': studentId,
|
|
||||||
'student_name': studentName,
|
|
||||||
'student_phone_number': studentPhoneNumber,
|
|
||||||
'student_gender': studentGender,
|
|
||||||
'student_address': studentAddress,
|
|
||||||
};
|
|
||||||
Response response;
|
|
||||||
|
|
||||||
response = await postConnect(
|
|
||||||
ApiConstants.baseUrl + '/student/editStudent', postData, apiToken);
|
|
||||||
if (response.data['status'] == STATUS_OK) {
|
|
||||||
return response.data['msg'];
|
|
||||||
} else {
|
|
||||||
throw response.data['msg'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<String> deleteStudent(
|
|
||||||
String sessionId, int studentId, apiToken) async {
|
|
||||||
var postData = {
|
|
||||||
'session_id': sessionId,
|
|
||||||
'student_id': studentId,
|
|
||||||
};
|
|
||||||
Response response;
|
|
||||||
response = await postConnect(
|
|
||||||
ApiConstants.baseUrl + '/student/deleteStudent', postData, apiToken);
|
|
||||||
if (response.data['status'] == STATUS_OK) {
|
|
||||||
return response.data['msg'];
|
|
||||||
} else {
|
|
||||||
throw response.data['msg'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Future<List<LoginModel>> login2(
|
|
||||||
String email, String password, apiToken) async {
|
|
||||||
var postData = {
|
|
||||||
'email': email,
|
|
||||||
'password': password,
|
|
||||||
};
|
|
||||||
response = await postConnect(LOGIN_API, postData, apiToken);
|
|
||||||
if (response.data['status'] == STATUS_OK) {
|
|
||||||
List responseList = response.data['data'];
|
|
||||||
List<LoginModel> listData =
|
|
||||||
responseList.map((f) => LoginModel.fromJson(f)).toList();
|
|
||||||
return listData;
|
|
||||||
} else {
|
|
||||||
throw response.data['msg'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
Future<List<ProductGridModel>> getProductGrid(
|
|
||||||
String sessionId, String skip, String limit, apiToken) async {
|
|
||||||
var postData = {'session_id': sessionId, 'skip': skip, 'limit': limit};
|
|
||||||
response = await postConnect(PRODUCT_API, postData, apiToken);
|
|
||||||
if (response.data['status'] == STATUS_OK) {
|
|
||||||
List responseList = response.data['data'];
|
|
||||||
//print('data : '+responseList.toString());
|
|
||||||
List<ProductGridModel> listData =
|
|
||||||
responseList.map((f) => ProductGridModel.fromJson(f)).toList();
|
|
||||||
return listData;
|
|
||||||
} else {
|
|
||||||
throw response.data['msg'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
Future<List<ProductListviewModel>> getProductListview(
|
|
||||||
String sessionId, String skip, String limit, apiToken) async {
|
|
||||||
var postData = {'session_id': sessionId, 'skip': skip, 'limit': limit};
|
|
||||||
response = await postConnect(PRODUCT_API, postData, apiToken);
|
|
||||||
if (response.data['status'] == STATUS_OK) {
|
|
||||||
List responseList = response.data['data'];
|
|
||||||
//print('data : '+responseList.toString());
|
|
||||||
List<ProductListviewModel> listData =
|
|
||||||
responseList.map((f) => ProductListviewModel.fromJson(f)).toList();
|
|
||||||
return listData;
|
|
||||||
} else {
|
|
||||||
throw response.data['msg'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ import 'package:mobdr/db/box_visit_tag.dart';
|
||||||
import 'package:mobdr/db/box_visit_photo.dart';
|
import 'package:mobdr/db/box_visit_photo.dart';
|
||||||
import 'package:mobdr/db/box_photo_typology.dart';
|
import 'package:mobdr/db/box_photo_typology.dart';
|
||||||
|
|
||||||
|
import 'package:mobdr/service/logger_util.dart';
|
||||||
|
|
||||||
import 'model.dart';
|
import 'model.dart';
|
||||||
import 'objectbox.g.dart'; // created by `flutter pub run build_runner build`
|
import 'objectbox.g.dart'; // created by `flutter pub run build_runner build`
|
||||||
|
|
||||||
|
|
@ -270,7 +272,7 @@ class ObjectBox {
|
||||||
return builder.find();
|
return builder.find();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Visit> getPreviousVisit() {
|
List<Visit> getActivePreviousVisit() {
|
||||||
// Get the previous date at midnight.
|
// Get the previous date at midnight.
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
final midnight = DateTime(now.year, now.month, now.day);
|
final midnight = DateTime(now.year, now.month, now.day);
|
||||||
|
|
@ -291,6 +293,24 @@ class ObjectBox {
|
||||||
return builder.find();
|
return builder.find();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Visit> getPreviousVisit() {
|
||||||
|
// Get the previous date at midnight.
|
||||||
|
final now = DateTime.now();
|
||||||
|
final midnight = DateTime(now.year, now.month, now.day);
|
||||||
|
|
||||||
|
// Convert the date to an integer.
|
||||||
|
final millisecondsSinceEpoch = midnight.millisecondsSinceEpoch;
|
||||||
|
|
||||||
|
// Query for all visits that match the date criteria, sorted by their date.
|
||||||
|
final builder = visitBox
|
||||||
|
.query(Visit_.date_visite.lessThan(millisecondsSinceEpoch))
|
||||||
|
.order(Visit_.date_visite, flags: Order.descending)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Execute the query and return the result.
|
||||||
|
return builder.find();
|
||||||
|
}
|
||||||
|
|
||||||
// A function that converts a response body list into a List<Visite>.
|
// A function that converts a response body list into a List<Visite>.
|
||||||
List<Visit> parseVisit(List responseDataList) {
|
List<Visit> parseVisit(List responseDataList) {
|
||||||
final parsed = responseDataList.cast<Map<String, dynamic>>();
|
final parsed = responseDataList.cast<Map<String, dynamic>>();
|
||||||
|
|
@ -372,6 +392,33 @@ class ObjectBox {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> DeletePreviousVisitWithoutPhoto() async {
|
||||||
|
final visits = getPreviousVisit();
|
||||||
|
|
||||||
|
for (final visit in visits) {
|
||||||
|
final photoCount = getVisitPhotoCount(visit.id_visite);
|
||||||
|
|
||||||
|
if (photoCount == 0) {
|
||||||
|
// Delete the visit
|
||||||
|
delVisitById(visit.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a Visito object from the ObjectBox database with the specified ID.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// id: The ID of the Visit object to remove.
|
||||||
|
///
|
||||||
|
/// Returns:
|
||||||
|
/// None.
|
||||||
|
void delVisitById(int id) {
|
||||||
|
if (visitBox.remove(id)) {
|
||||||
|
LoggerUtil.logNStacktackDebug("delete visit:${id}");
|
||||||
|
} else
|
||||||
|
LoggerUtil.logNStackError("delete visit:${id} KO");
|
||||||
|
}
|
||||||
|
|
||||||
int getVisitCount() {
|
int getVisitCount() {
|
||||||
return visitBox.count();
|
return visitBox.count();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,319 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:timelines/timelines.dart';
|
|
||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:mobdr/config/global_style.dart';
|
|
||||||
import 'package:mobdr/ui/reusable/reusable_widget.dart';
|
|
||||||
import 'package:mobdr/network/api_provider.dart';
|
|
||||||
import 'package:mobdr/main.dart';
|
|
||||||
|
|
||||||
const completeColor = Color(0xff5e6172);
|
|
||||||
const inProgressColor = Color(0xff5ec792);
|
|
||||||
const todoColor = Color(0xffd1d2d7);
|
|
||||||
const failedColor = Colors.red;
|
|
||||||
|
|
||||||
class TabSyncPage extends StatefulWidget {
|
|
||||||
@override
|
|
||||||
_TabSyncPageState createState() => _TabSyncPageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _TabSyncPageState extends State<TabSyncPage>
|
|
||||||
with AutomaticKeepAliveClientMixin {
|
|
||||||
// initialize global function and reusable widget
|
|
||||||
//final _globalFunction = GlobalFunction();
|
|
||||||
final _reusableWidget = ReusableWidget();
|
|
||||||
|
|
||||||
final _processes = ['Btqs', 'Params', 'Visites', 'Photos', 'Logs'];
|
|
||||||
|
|
||||||
final ApiProvider _apiProvider =
|
|
||||||
ApiProvider(); // TODO: A voir si bien positionné
|
|
||||||
|
|
||||||
// _listKey is used for AnimatedList
|
|
||||||
//final GlobalKey<AnimatedListState> _listKey = GlobalKey();
|
|
||||||
|
|
||||||
int _processIndex = 1;
|
|
||||||
|
|
||||||
Color getColor(int index) {
|
|
||||||
if (index == _processIndex) {
|
|
||||||
return inProgressColor;
|
|
||||||
} else if (index < _processIndex) {
|
|
||||||
return completeColor;
|
|
||||||
} else {
|
|
||||||
return todoColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// keep the state to do not refresh when switch navbar
|
|
||||||
@override
|
|
||||||
bool get wantKeepAlive => true;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
// if we used AutomaticKeepAliveClientMixin, we must call super.build(context);
|
|
||||||
super.build(context);
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
appBar: AppBar(
|
|
||||||
iconTheme: IconThemeData(
|
|
||||||
color: GlobalStyle.appBarIconThemeColor,
|
|
||||||
),
|
|
||||||
elevation: GlobalStyle.appBarElevation,
|
|
||||||
title: Text(
|
|
||||||
'Synchronisation',
|
|
||||||
style: GlobalStyle.appBarTitle,
|
|
||||||
),
|
|
||||||
backgroundColor: GlobalStyle.appBarBackgroundColor,
|
|
||||||
systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle,
|
|
||||||
bottom: _reusableWidget.bottomAppBar(),
|
|
||||||
),
|
|
||||||
body: Timeline.tileBuilder(
|
|
||||||
theme: TimelineThemeData(
|
|
||||||
direction: Axis.horizontal,
|
|
||||||
connectorTheme: ConnectorThemeData(
|
|
||||||
space: 30.0,
|
|
||||||
thickness: 5.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
builder: TimelineTileBuilder.connected(
|
|
||||||
connectionDirection: ConnectionDirection.before,
|
|
||||||
itemExtentBuilder: (_, __) =>
|
|
||||||
MediaQuery.of(context).size.width / _processes.length,
|
|
||||||
oppositeContentsBuilder: (context, index) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 15.0),
|
|
||||||
child: Image.asset(
|
|
||||||
'assets/images/process_timeline/status${index + 1}.png',
|
|
||||||
width: 50.0,
|
|
||||||
color: getColor(index),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
contentsBuilder: (context, index) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 15.0),
|
|
||||||
child: Text(
|
|
||||||
_processes[index],
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: getColor(index),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
indicatorBuilder: (_, index) {
|
|
||||||
var color;
|
|
||||||
var child;
|
|
||||||
if (index == _processIndex) {
|
|
||||||
color = inProgressColor;
|
|
||||||
child = Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
strokeWidth: 3.0,
|
|
||||||
valueColor: AlwaysStoppedAnimation(Colors.white),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else if (index < _processIndex) {
|
|
||||||
color = failedColor;
|
|
||||||
child = Icon(
|
|
||||||
Icons.cancel,
|
|
||||||
color: Colors.white,
|
|
||||||
size: 20.0,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
color = todoColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index <= _processIndex) {
|
|
||||||
return Stack(
|
|
||||||
children: [
|
|
||||||
CustomPaint(
|
|
||||||
size: Size(30.0, 30.0),
|
|
||||||
painter: _BezierPainter(
|
|
||||||
color: color,
|
|
||||||
drawStart: index > 0,
|
|
||||||
drawEnd: index < _processIndex,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
DotIndicator(
|
|
||||||
size: 30.0,
|
|
||||||
color: color,
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return Stack(
|
|
||||||
children: [
|
|
||||||
CustomPaint(
|
|
||||||
size: Size(15.0, 15.0),
|
|
||||||
painter: _BezierPainter(
|
|
||||||
color: color,
|
|
||||||
drawEnd: index < _processes.length - 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
OutlinedDotIndicator(
|
|
||||||
borderWidth: 4.0,
|
|
||||||
color: color,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
connectorBuilder: (_, index, type) {
|
|
||||||
if (index > 0) {
|
|
||||||
if (index == _processIndex) {
|
|
||||||
final prevColor = getColor(index - 1);
|
|
||||||
final color = getColor(index);
|
|
||||||
List<Color> gradientColors;
|
|
||||||
if (type == ConnectorType.start) {
|
|
||||||
gradientColors = [Color.lerp(prevColor, color, 0.5)!, color];
|
|
||||||
} else {
|
|
||||||
gradientColors = [
|
|
||||||
prevColor,
|
|
||||||
Color.lerp(prevColor, color, 0.5)!
|
|
||||||
];
|
|
||||||
}
|
|
||||||
return DecoratedLineConnector(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
colors: gradientColors,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return SolidLineConnector(
|
|
||||||
color: getColor(index),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
itemCount: _processes.length,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
floatingActionButton: FloatingActionButton(
|
|
||||||
child: Icon(Icons.chevron_right),
|
|
||||||
onPressed: () async {
|
|
||||||
var futures = [
|
|
||||||
//_apiProvider.SyncEtablissements(),
|
|
||||||
//_apiProvider.SyncVisites(),
|
|
||||||
_apiProvider.SyncPhotos(),
|
|
||||||
];
|
|
||||||
|
|
||||||
objectbox.etabBox.removeAll();
|
|
||||||
objectbox.etabCompetitorBox.removeAll();
|
|
||||||
objectbox.visitBox.removeAll();
|
|
||||||
objectbox.visitTagBox.removeAll();
|
|
||||||
objectbox.visitPhotoBox.removeAll();
|
|
||||||
objectbox.PhotoTypologyBox.removeAll();
|
|
||||||
|
|
||||||
var results = await Future.wait(futures);
|
|
||||||
|
|
||||||
if (results[0] == 'OK') {
|
|
||||||
print("SyncEtablissements OK");
|
|
||||||
} else {
|
|
||||||
print("SyncEtablissements Error:" + results[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (results[1] == 'OK') {
|
|
||||||
print("SyncVisites OK");
|
|
||||||
} else {
|
|
||||||
print("SyncVisites Error:" + results[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (results[0] == 'OK') {
|
|
||||||
print("SyncPhotos OK");
|
|
||||||
} else {
|
|
||||||
print("SyncPhotos Error:" + results[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
setState(() {
|
|
||||||
_processIndex = (_processIndex + 1) % _processes.length;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
backgroundColor: inProgressColor,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// hardcoded bezier painter
|
|
||||||
class _BezierPainter extends CustomPainter {
|
|
||||||
const _BezierPainter({
|
|
||||||
required this.color,
|
|
||||||
this.drawStart = true,
|
|
||||||
this.drawEnd = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
final Color color;
|
|
||||||
final bool drawStart;
|
|
||||||
final bool drawEnd;
|
|
||||||
|
|
||||||
Offset _offset(double radius, double angle) {
|
|
||||||
return Offset(
|
|
||||||
radius * cos(angle) + radius,
|
|
||||||
radius * sin(angle) + radius,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void paint(Canvas canvas, Size size) {
|
|
||||||
final paint = Paint()
|
|
||||||
..style = PaintingStyle.fill
|
|
||||||
..color = color;
|
|
||||||
|
|
||||||
final radius = size.width / 2;
|
|
||||||
|
|
||||||
var angle;
|
|
||||||
var offset1;
|
|
||||||
var offset2;
|
|
||||||
|
|
||||||
var path;
|
|
||||||
|
|
||||||
if (drawStart) {
|
|
||||||
angle = 3 * pi / 4;
|
|
||||||
offset1 = _offset(radius, angle);
|
|
||||||
offset2 = _offset(radius, -angle);
|
|
||||||
path = Path()
|
|
||||||
..moveTo(offset1.dx, offset1.dy)
|
|
||||||
..quadraticBezierTo(0.0, size.height / 2, -radius, radius)
|
|
||||||
..quadraticBezierTo(0.0, size.height / 2, offset2.dx, offset2.dy)
|
|
||||||
..close();
|
|
||||||
|
|
||||||
canvas.drawPath(path, paint);
|
|
||||||
}
|
|
||||||
if (drawEnd) {
|
|
||||||
angle = -pi / 4;
|
|
||||||
offset1 = _offset(radius, angle);
|
|
||||||
offset2 = _offset(radius, -angle);
|
|
||||||
|
|
||||||
path = Path()
|
|
||||||
..moveTo(offset1.dx, offset1.dy)
|
|
||||||
..quadraticBezierTo(
|
|
||||||
size.width, size.height / 2, size.width + radius, radius)
|
|
||||||
..quadraticBezierTo(size.width, size.height / 2, offset2.dx, offset2.dy)
|
|
||||||
..close();
|
|
||||||
|
|
||||||
canvas.drawPath(path, paint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool shouldRepaint(_BezierPainter oldDelegate) {
|
|
||||||
return oldDelegate.color != color ||
|
|
||||||
oldDelegate.drawStart != drawStart ||
|
|
||||||
oldDelegate.drawEnd != drawEnd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -25,6 +25,7 @@ class _SynchronizationPageState extends State<SynchronizationPage>
|
||||||
bool _backofficeSyncCompleted = false;
|
bool _backofficeSyncCompleted = false;
|
||||||
bool _photosSyncCompleted = false;
|
bool _photosSyncCompleted = false;
|
||||||
bool _logSyncCompleted = false;
|
bool _logSyncCompleted = false;
|
||||||
|
bool _syncLog = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
@ -134,16 +135,6 @@ class _SynchronizationPageState extends State<SynchronizationPage>
|
||||||
}
|
}
|
||||||
|
|
||||||
_photosSyncCompleted = (_totalUploaded == _visitPhotosList.length);
|
_photosSyncCompleted = (_totalUploaded == _visitPhotosList.length);
|
||||||
|
|
||||||
// Get unique id_visite values from _photosList
|
|
||||||
Set<int> uniqueIds =
|
|
||||||
_visitPhotosList.map((photo) => photo.id_visite).toSet();
|
|
||||||
|
|
||||||
// Send VisitPhotoCountEvent for each unique id_visite
|
|
||||||
for (int id_visite in uniqueIds) {
|
|
||||||
eventBus.fire(VisitPhotoCountEvent(
|
|
||||||
id_visite, objectbox.getVisitPhotoCount(id_visite)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _cleanVisitPhotoDir() async {
|
Future<void> _cleanVisitPhotoDir() async {
|
||||||
|
|
@ -221,8 +212,15 @@ class _SynchronizationPageState extends State<SynchronizationPage>
|
||||||
// upload photo to server
|
// upload photo to server
|
||||||
await _uploadVisitPhotos(_apiProvider);
|
await _uploadVisitPhotos(_apiProvider);
|
||||||
|
|
||||||
// TODO:
|
final syncLogResult = _syncLog ? await _apiProvider.SyncCalendar() : 'OK';
|
||||||
// supprimer les visites "ancienne" sans photo !!
|
|
||||||
|
// log synchronization OK ?
|
||||||
|
if (syncLogResult == 'OK') {
|
||||||
|
_logSyncCompleted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete visits without a photo!
|
||||||
|
await objectbox.DeletePreviousVisitWithoutPhoto();
|
||||||
|
|
||||||
// deletes photos that are no longer in any visits
|
// deletes photos that are no longer in any visits
|
||||||
await _cleanVisitPhotoDir();
|
await _cleanVisitPhotoDir();
|
||||||
|
|
@ -234,10 +232,7 @@ class _SynchronizationPageState extends State<SynchronizationPage>
|
||||||
SharedPrefs().lastCalendarRefresh =
|
SharedPrefs().lastCalendarRefresh =
|
||||||
DateFormat('dd/MM/yyyy HH:mm').format(DateTime.now());
|
DateFormat('dd/MM/yyyy HH:mm').format(DateTime.now());
|
||||||
|
|
||||||
// TODO: je pense que comme il y a cet event (qui reload tout) on est plus obligé
|
// send global event to refresh calendar
|
||||||
// de lancer l'autre évent pour les count() de photo.
|
|
||||||
//
|
|
||||||
// de plus il faut supprimer les visites qui n'ont plus de photo !!!
|
|
||||||
eventBus.fire(RefreshCalendarEvent(SharedPrefs().lastCalendarRefresh));
|
eventBus.fire(RefreshCalendarEvent(SharedPrefs().lastCalendarRefresh));
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|
@ -312,7 +307,25 @@ class _SynchronizationPageState extends State<SynchronizationPage>
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
SizedBox(height: 30),
|
SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Sync Logs',
|
||||||
|
style: TextStyle(fontSize: 16),
|
||||||
|
),
|
||||||
|
Switch(
|
||||||
|
value: _syncLog,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
_syncLog = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
SyncItem(
|
SyncItem(
|
||||||
icon: Icons.business,
|
icon: Icons.business,
|
||||||
title: 'Backoffice',
|
title: 'Backoffice',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue