From abb13b588b8b26e954670fb0db7fbe23025c60b4 Mon Sep 17 00:00:00 2001 From: Frederik Benoist Date: Mon, 1 May 2023 18:31:53 +0200 Subject: [PATCH] feat: eventbus to refresh --- lib/config/constant.dart | 2 +- lib/db/box_visit.dart | 17 +- lib/db/box_visit_tag.dart | 6 +- lib/events.dart | 10 + lib/model/visite_model.dart | 50 +- lib/network/api_provider.dart | 134 ++-- lib/objectbox-model.json | 238 ++++--- lib/objectbox.dart | 196 ++++-- lib/objectbox.g.dart | 625 ++++++++++-------- lib/{ui/sync => old}/tab_sync.dart | 4 +- lib/ui/home.dart | 12 +- lib/ui/home/tab_home.dart | 207 ++++-- lib/ui/reusable/reusable_widget.dart | 71 -- lib/ui/sync/sync_calendar.dart | 116 ++++ lib/ui/visit/tab_visit.dart | 332 ++++++++++ lib/ui/{home => visit}/visit_photo_tag.dart | 4 +- .../{home => visit}/visit_photo_typology.dart | 20 +- .../visit_photo_typology_detail.dart} | 4 +- .../visit_photo_typology_list.dart | 22 +- pubspec.lock | 8 + pubspec.yaml | 3 + 21 files changed, 1432 insertions(+), 649 deletions(-) rename lib/{ui/sync => old}/tab_sync.dart (95%) create mode 100644 lib/ui/sync/sync_calendar.dart create mode 100644 lib/ui/visit/tab_visit.dart rename lib/ui/{home => visit}/visit_photo_tag.dart (98%) rename lib/ui/{home => visit}/visit_photo_typology.dart (87%) rename lib/ui/{home/visite_photo_typology_detail.dart => visit/visit_photo_typology_detail.dart} (96%) rename lib/ui/{home => visit}/visit_photo_typology_list.dart (94%) diff --git a/lib/config/constant.dart b/lib/config/constant.dart index d588f19..ad2abba 100644 --- a/lib/config/constant.dart +++ b/lib/config/constant.dart @@ -9,7 +9,7 @@ import 'package:flutter/material.dart'; const String APP_NAME = 'Mobile DR'; // color for apps -const Color PRIMARY_COLOR = Color(0xFF07ac12); +const Color PRIMARY_COLOR = Color(0xff01aed6); const Color ASSENT_COLOR = Color(0xFFe75f3f); const Color CHARCOAL = Color(0xFF515151); diff --git a/lib/db/box_visit.dart b/lib/db/box_visit.dart index 59bc27e..394c0cc 100644 --- a/lib/db/box_visit.dart +++ b/lib/db/box_visit.dart @@ -5,13 +5,16 @@ import 'package:mobdr/config/constant.dart'; // ignore_for_file: public_member_api_docs @Entity() -class Visite { +class Visit { // specify the id @Id() int id = 0; int id_visite; DateTime date_visite; + DateTime date_debut; + DateTime? date_fin; + DateTime? date_validation; String type_visite; String title; bool allDay; @@ -21,10 +24,13 @@ class Visite { String url_photo_principale; String langage; - Visite( + Visit( {this.id = 0, required this.id_visite, required this.date_visite, + required this.date_debut, + required this.date_fin, + required this.date_validation, required this.type_visite, required this.title, required this.allDay, @@ -34,9 +40,14 @@ class Visite { required this.url_photo_principale, required this.langage}); - Visite.fromJson(Map json) + Visit.fromJson(Map json) : id_visite = json['id_visite'], date_visite = DateTime.parse(json['start']), + date_debut = DateTime.parse(json['start']), + date_fin = json['end'].isNotEmpty ? DateTime.parse(json['end']) : null, + date_validation = json['validation'].isNotEmpty + ? DateTime.parse(json['validation']) + : null, type_visite = json['type_visite'], title = json['title'], allDay = json['allDay'], diff --git a/lib/db/box_visit_tag.dart b/lib/db/box_visit_tag.dart index a2de5cf..7d92cad 100644 --- a/lib/db/box_visit_tag.dart +++ b/lib/db/box_visit_tag.dart @@ -4,7 +4,7 @@ import 'package:mobdr/objectbox.g.dart'; // ignore_for_file: public_member_api_docs @Entity() -class VisiteTag { +class VisitTag { // specify the id @Id() int id = 0; @@ -14,14 +14,14 @@ class VisiteTag { String libelle; String langage; - VisiteTag( + VisitTag( {this.id = 0, required this.id_visite_tag, required this.id_distrib, required this.libelle, required this.langage}); - VisiteTag.fromJson(Map json) + VisitTag.fromJson(Map json) : id_visite_tag = json['id_visite_tag'], id_distrib = json['id_distrib'], libelle = json['libelle'], diff --git a/lib/events.dart b/lib/events.dart index 35cee84..f3849cd 100644 --- a/lib/events.dart +++ b/lib/events.dart @@ -26,3 +26,13 @@ class UrlEvent extends AppEvent { @override List get props => [url]; } + +class VisitPhotoCountEvent extends AppEvent { + VisitPhotoCountEvent(this.id_visite, this.photoCount); + + final int id_visite; + final int photoCount; + + @override + List get props => [id_visite, photoCount]; +} diff --git a/lib/model/visite_model.dart b/lib/model/visite_model.dart index cc0a5de..f15a56c 100644 --- a/lib/model/visite_model.dart +++ b/lib/model/visite_model.dart @@ -22,9 +22,53 @@ class VisiteModel { required this.type_visite, required this.langage}); - static Future> getAllVisites() async { - // Retrieve all visits from the database using the getAllVisites() method - final visites = await objectbox.getAllVisites(); + static Future> getTodayVisit() async { + // Retrieve all today visits from the database + final visites = await objectbox.getTodayVisit(); + + // Map each retrieved visit to VisiteModel + final visiteModels = visites + .map((visite) => VisiteModel( + id_distrib: visite.id_distrib_visite, + id: visite.id, + id_visite: visite.id_visite, + name: visite.id_etab.toString() + ' - ' + visite.title, + photoCount: objectbox.getVisitPhotoCount(visite.id_visite), + date: visite.date_visite.toString(), + image: visite.url_photo_principale, + type_visite: visite.type_visite, + langage: visite.langage)) + .toList(); + + // Return the list of VisiteModel + return visiteModels; + } + + static Future> getPreviousVisit() async { + // Retrieve all previsous visits from the database + final visites = await objectbox.getPreviousVisit(); + + // Map each retrieved visit to VisiteModel + final visiteModels = visites + .map((visite) => VisiteModel( + id_distrib: visite.id_distrib_visite, + id: visite.id, + id_visite: visite.id_visite, + name: visite.id_etab.toString() + ' - ' + visite.title, + photoCount: objectbox.getVisitPhotoCount(visite.id_visite), + date: visite.date_visite.toString(), + image: visite.url_photo_principale, + type_visite: visite.type_visite, + langage: visite.langage)) + .toList(); + + // Return the list of VisiteModel + return visiteModels; + } + + static Future> getAllVisit() async { + // Retrieve all visits from the database + final visites = await objectbox.getAllVisit(); // Map each retrieved visit to VisiteModel final visiteModels = visites diff --git a/lib/network/api_provider.dart b/lib/network/api_provider.dart index bb176c9..323271e 100644 --- a/lib/network/api_provider.dart +++ b/lib/network/api_provider.dart @@ -201,100 +201,108 @@ class ApiProvider { } } - /// Synchronize "stores & competing stores" - Future SyncEtablissements() async { - Response response; - Response response2; - + /// Synchronize all informations about store, competitor, calendar + Future SyncCalendar() async { try { - /// get "Etablissement" list - response = await getCrud( + final responseFutures = Future.wait([ + getCrud( ApiConstants.baseUrl + ApiConstants.externalEndpoint + ApiConstants.restEndpoint + '/mobDR/etablissement', - null); - - /// get "concurrent" list - response2 = await getCrud( + null, + ), + getCrud( ApiConstants.baseUrl + ApiConstants.externalEndpoint + ApiConstants.restEndpoint + '/mobDR/etablissement/concurrent', - null); - - //await new Future.delayed(const Duration(seconds: 5)); - - if (response.statusCode == STATUS_OK) { - /// fill box "etab" - objectbox.addEtabs(response.data['boutiques']); - } - - if (response2.statusCode == STATUS_OK) { - /// fill box "concurrence" - objectbox.addPhotoCompetitors(response2.data['concurrents']); - } - - /// all ok - if (response.statusCode == STATUS_OK && - response2.statusCode == STATUS_OK) { - return 'OK'; - } else { - return response.statusMessage ?? 'Unknow error ...'; - } - } catch (ex) { - return ex.toString(); // return ex.response!.data; - } - } - - /// Synchronize "visites" - Future SyncVisites() async { - Response response; - Response response2; - - try { - var body = null; - - /// get "Visite list" - response = await getCrud( + null, + ), + getCrud( ApiConstants.baseUrl + ApiConstants.externalEndpoint + ApiConstants.restEndpoint + '/mobDR/visite/calendrier', - {"id_utilisateur": 6, "start": 20230101, "end": 20230501}); - - /// get "Visite tags" list - response2 = await getCrud( + {"id_utilisateur": 6, "start": 20230101, "end": 20230501}, + ), + getCrud( ApiConstants.baseUrl + ApiConstants.externalEndpoint + ApiConstants.restEndpoint + '/mobDR/visite/tag', - body); + null, + ), + getCrud( + ApiConstants.baseUrl + + ApiConstants.externalEndpoint + + ApiConstants.restEndpoint + + '/mobDR/visite/typologie', + null), + ]); - //await new Future.delayed(const Duration(seconds: 5)); + final List> responses = + await responseFutures.timeout(Duration(seconds: 30)); - if (response.statusCode == STATUS_OK) { - /// fill box "visite" - objectbox.addVisites(response.data['events']); + final etabResponse = responses[0]; + final competitorResponse = responses[1]; + final visitResponse = responses[2]; + final visitTagResponse = responses[3]; + final PhotoTypologyResponse = responses[4]; + + if (etabResponse.statusCode == STATUS_OK) { + // remove all objects + objectbox.etabBox.removeAll(); + + // fill box "Etabs" + objectbox.addEtabs(etabResponse.data['boutiques']); } - if (response2.statusCode == STATUS_OK) { + if (competitorResponse.statusCode == STATUS_OK) { + // remove all objects + objectbox.etabCompetitorBox.removeAll(); + + // fill box "Etabs" + objectbox.addEtabCompetitors(competitorResponse.data['concurrents']); + } + + if (visitResponse.statusCode == STATUS_OK) { + /// fill box "visite" + objectbox.syncVisits(visitResponse.data['events']); + } + + if (visitTagResponse.statusCode == STATUS_OK) { // remove all objects objectbox.visitTagBox.removeAll(); /// fill box "visiteTag" - objectbox.addVisiteTags(response2.data['tags']); + objectbox.addVisitTags(visitTagResponse.data['tags']); } - /// all ok - if (response.statusCode == STATUS_OK && - response2.statusCode == STATUS_OK) { + if (PhotoTypologyResponse.statusCode == STATUS_OK) { + // remove all objects + objectbox.PhotoTypologyBox.removeAll(); + + /// fill box "PhotoTypology" + objectbox.addPhotoTypologies(PhotoTypologyResponse.data['typologies']); + } + + if (etabResponse.statusCode == STATUS_OK && + competitorResponse.statusCode == STATUS_OK && + visitResponse.statusCode == STATUS_OK && + visitTagResponse.statusCode == STATUS_OK && + PhotoTypologyResponse.statusCode == STATUS_OK) { return 'OK'; } else { - return response.statusMessage ?? 'Unknow error ...'; + return etabResponse.statusMessage ?? + competitorResponse.statusMessage ?? + visitResponse.statusMessage ?? + visitTagResponse.statusMessage ?? + PhotoTypologyResponse.statusMessage ?? + 'Unknown error'; } } catch (ex) { - return ex.toString(); // return ex.response!.data; + return ex.toString(); } } diff --git a/lib/objectbox-model.json b/lib/objectbox-model.json index 5495486..27c8f67 100644 --- a/lib/objectbox-model.json +++ b/lib/objectbox-model.json @@ -164,104 +164,6 @@ ], "relations": [] }, - { - "id": "7:8290500625256822711", - "lastPropertyId": "14:5831680857919010234", - "name": "Visite", - "properties": [ - { - "id": "1:4764888137112024855", - "name": "id", - "type": 6, - "flags": 1 - }, - { - "id": "2:5254234731633944539", - "name": "id_visite", - "type": 6 - }, - { - "id": "3:8500424631546195124", - "name": "type_visite", - "type": 9 - }, - { - "id": "4:307259370424642142", - "name": "title", - "type": 9 - }, - { - "id": "5:3383534011872507610", - "name": "allDay", - "type": 1 - }, - { - "id": "7:3559563062004847001", - "name": "id_distrib_visite", - "type": 6 - }, - { - "id": "8:702894233960499092", - "name": "id_etab", - "type": 6 - }, - { - "id": "9:5734787836187957444", - "name": "abandon", - "type": 6 - }, - { - "id": "11:3119168728902810585", - "name": "date_visite", - "type": 10 - }, - { - "id": "13:1900114397693432703", - "name": "url_photo_principale", - "type": 9 - }, - { - "id": "14:5831680857919010234", - "name": "langage", - "type": 9 - } - ], - "relations": [] - }, - { - "id": "8:637444607663700174", - "lastPropertyId": "5:1603887098520719919", - "name": "VisiteTag", - "properties": [ - { - "id": "1:6166685814637290118", - "name": "id", - "type": 6, - "flags": 1 - }, - { - "id": "2:2059771745036116529", - "name": "id_visite_tag", - "type": 6 - }, - { - "id": "3:102253757473665009", - "name": "id_distrib", - "type": 6 - }, - { - "id": "4:1526411175344533047", - "name": "libelle", - "type": 9 - }, - { - "id": "5:1603887098520719919", - "name": "langage", - "type": 9 - } - ], - "relations": [] - }, { "id": "10:2779194860339140505", "lastPropertyId": "4:5588276375011055284", @@ -378,9 +280,122 @@ } ], "relations": [] + }, + { + "id": "14:8708899666252263604", + "lastPropertyId": "14:2141854824886685065", + "name": "Visit", + "properties": [ + { + "id": "1:1616706753952341931", + "name": "id", + "type": 6, + "flags": 1 + }, + { + "id": "2:481724387420447256", + "name": "id_visite", + "type": 6 + }, + { + "id": "3:4117089501376875705", + "name": "date_visite", + "type": 10 + }, + { + "id": "4:6960359036987777529", + "name": "date_debut", + "type": 10 + }, + { + "id": "5:2625279579039264654", + "name": "date_fin", + "type": 10 + }, + { + "id": "6:7413956781075545593", + "name": "date_validation", + "type": 10 + }, + { + "id": "7:1634711659019811476", + "name": "type_visite", + "type": 9 + }, + { + "id": "8:8990646204619539770", + "name": "title", + "type": 9 + }, + { + "id": "9:392889911199239462", + "name": "allDay", + "type": 1 + }, + { + "id": "10:7419826409064780644", + "name": "id_distrib_visite", + "type": 6 + }, + { + "id": "11:4334641330182955845", + "name": "id_etab", + "type": 6 + }, + { + "id": "12:9153440695191140990", + "name": "abandon", + "type": 6 + }, + { + "id": "13:2729810367696493366", + "name": "url_photo_principale", + "type": 9 + }, + { + "id": "14:2141854824886685065", + "name": "langage", + "type": 9 + } + ], + "relations": [] + }, + { + "id": "15:8818692504823914757", + "lastPropertyId": "5:7051299843455307303", + "name": "VisitTag", + "properties": [ + { + "id": "1:43397073361653900", + "name": "id", + "type": 6, + "flags": 1 + }, + { + "id": "2:3130658169202818866", + "name": "id_visite_tag", + "type": 6 + }, + { + "id": "3:3266967171909331007", + "name": "id_distrib", + "type": 6 + }, + { + "id": "4:1055850760280831759", + "name": "libelle", + "type": 9 + }, + { + "id": "5:7051299843455307303", + "name": "langage", + "type": 9 + } + ], + "relations": [] } ], - "lastEntityId": "13:6298506278273268036", + "lastEntityId": "15:8818692504823914757", "lastIndexId": "1:7907819717055295102", "lastRelationId": "0:0", "lastSequenceId": "0:0", @@ -390,7 +405,9 @@ 7401686910042688313, 1805690312272107423, 6788844671665652158, - 2910300629980903548 + 2910300629980903548, + 8290500625256822711, + 637444607663700174 ], "retiredIndexUids": [ 7907819717055295102 @@ -426,7 +443,26 @@ 6313899520679425027, 4752064214129719799, 319372524127232986, - 783761731187897018 + 783761731187897018, + 4764888137112024855, + 5254234731633944539, + 8500424631546195124, + 307259370424642142, + 3383534011872507610, + 3559563062004847001, + 702894233960499092, + 5734787836187957444, + 3119168728902810585, + 1900114397693432703, + 5831680857919010234, + 6072666250573656763, + 138766887652455581, + 4185186663484548877, + 6166685814637290118, + 2059771745036116529, + 102253757473665009, + 1526411175344533047, + 1603887098520719919 ], "retiredRelationUids": [], "version": 1 diff --git a/lib/objectbox.dart b/lib/objectbox.dart index d73b695..54035a7 100644 --- a/lib/objectbox.dart +++ b/lib/objectbox.dart @@ -30,10 +30,10 @@ class ObjectBox { late final Box etabCompetitorBox; /// A Box of visit - late final Box visitBox; + late final Box visitBox; /// A Box for all visit tag by distributor / language - late final Box visitTagBox; + late final Box visitTagBox; /// A Box of visit Photo late final Box visitPhotoBox; @@ -49,8 +49,8 @@ class ObjectBox { userBox = Box(store); etabBox = Box(store); etabCompetitorBox = Box(store); - visitBox = Box(store); - visitTagBox = Box(store); + visitBox = Box(store); + visitTagBox = Box(store); PhotoTypologyBox = Box(store); visitPhotoBox = Box(store); logBox = Box(store); @@ -62,12 +62,14 @@ class ObjectBox { userBox.removeAll(); - //etabBox.removeAll(); - //etabCompetitorBox.removeAll(); - //visitBox.removeAll(); - //visitTagBox.removeAll(); - //visitPhotoBox.removeAll(); - //PhotoTypologyBox.removeAll(); + /* + etabBox.removeAll(); + etabCompetitorBox.removeAll(); + visitBox.removeAll(); + visitTagBox.removeAll(); + visitPhotoBox.removeAll(); + PhotoTypologyBox.removeAll(); + */ // Add some demo data if the box is empty. if (userBox.isEmpty()) { @@ -200,41 +202,41 @@ class ObjectBox { return etabBox.count(); } - /// PHOTO COMPETITOR --------------------------------------------------------------- + /// ETAB COMPETITOR --------------------------------------------------------------- /// // A function that converts a response body list into a List. - List parsePhotoCompetitor(List responseDataList) { + List parseEtabCompetitor(List responseDataList) { final parsed = responseDataList.cast>(); return parsed .map((json) => EtabCompetitor.fromJson(json)) .toList(); } - Future addPhotoCompetitors(List _listPhotoCompetitors) => - store.runInTransactionAsync(TxMode.write, _addPhotoCompetitorsInTx, - parsePhotoCompetitor(_listPhotoCompetitors)); + Future addEtabCompetitors(List _listPhotoCompetitors) => + store.runInTransactionAsync(TxMode.write, _addEtabCompetitorsInTx, + parseEtabCompetitor(_listPhotoCompetitors)); - static void _addPhotoCompetitorsInTx(Store store, _Competitors) { + static void _addEtabCompetitorsInTx(Store store, _Competitors) { store.box().putMany(_Competitors); } - Future addPhotoCompetitor( + Future addEtabCompetitor( int _id_concurrence_lien, int _id_etab, _nom) => store.runInTransactionAsync( TxMode.write, - _addPhotoCompetitorInTx, + _addEtabCompetitorInTx, EtabCompetitor( id_concurrence_lien: _id_concurrence_lien, id_etab: _id_etab, nom: _nom, )); - static void _addPhotoCompetitorInTx(Store store, _Competitor) { + static void _addEtabCompetitorInTx(Store store, _Competitor) { store.box().put(_Competitor); } - List getPhotoCompetitorList() { + List getEtabCompetitorList() { final query = etabCompetitorBox .query(EtabCompetitor_.id_etab.equals(1417)) .order(EtabCompetitor_.nom) @@ -244,18 +246,18 @@ class ObjectBox { return photoCompetitors.toList(); } - int getPhotoCompetitorsCount() { + int getEtabCompetitorsCount() { return etabCompetitorBox.count(); } /// VISITE ------------------------------------------------------------------ /// - Stream> getVisitesStream() { + Stream> getVisitStream() { // Query for all visites, sorted by their date. // https://docs.objectbox.io/queries final builder = - visitBox.query().order(Visite_.date_visite, flags: Order.descending); + visitBox.query().order(Visit_.date_visite, flags: Order.descending); // Build and watch the query, // set triggerImmediately to emit the query immediately on listen. return builder @@ -264,34 +266,78 @@ class ObjectBox { .map((query) => query.find()); } - List getAllVisites() { + List getAllVisit() { // Query for all visites, sorted by their date. final builder = visitBox .query() - .order(Visite_.date_visite, flags: Order.descending) + .order(Visit_.date_visite, flags: Order.descending) .build(); // Execute the query and return the result. return builder.find(); } + List getTodayVisit() { + final now = DateTime.now(); + + // Get the start and end of the current day as timestamps. + final startOfDay = + DateTime(now.year, now.month, now.day).millisecondsSinceEpoch; + final endOfDay = DateTime(now.year, now.month, now.day, 23, 59, 59) + .millisecondsSinceEpoch; + + // Query for all visits that occur on the current day and have not been abandoned. + final builder = visitBox + .query(Visit_.date_visite.between(startOfDay, endOfDay) & + Visit_.abandon.equals(0)) + .order(Visit_.date_visite, flags: Order.descending) + .build(); + + // Execute the query and return the result. + return builder.find(); + } + + List 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 and abandonment criteria, sorted by their date. + final builder = visitBox + .query( + Visit_.date_visite.lessThan(millisecondsSinceEpoch) & + Visit_.abandon.equals(0), + ) + .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. - List parseVisites(List responseDataList) { + List parseVisit(List responseDataList) { final parsed = responseDataList.cast>(); - return parsed.map((json) => Visite.fromJson(json)).toList(); + return parsed.map((json) => Visit.fromJson(json)).toList(); } // TODO : Enregistre urlphotoprincipale sur le disque pour l'avoir en cache ? - Future addVisites(List _listVisites) => + Future addVisits(List _listVisits) => store.runInTransactionAsync( - TxMode.write, _addVisitesInTx, parseVisites(_listVisites)); + TxMode.write, _addVisitsInTx, parseVisit(_listVisits)); - static void _addVisitesInTx(Store store, _Visites) { - store.box().putMany(_Visites); + static void _addVisitsInTx(Store store, _Visits) { + store.box().putMany(_Visits); } - Future addVisite( + Future addVisit( int _id_visite, DateTime _date_visite, + DateTime _date_debut, + DateTime _date_fin, + DateTime _date_validation, String _type_visite, String _title, bool _allDay, @@ -304,10 +350,13 @@ class ObjectBox { String _langage) => store.runInTransactionAsync( TxMode.write, - _addVisiteInTx, - Visite( + _addVisitInTx, + Visit( id_visite: _id_visite, date_visite: _date_visite, + date_debut: _date_debut, + date_fin: _date_fin, + date_validation: _date_validation, type_visite: _type_visite, title: _title, allDay: _allDay, @@ -317,66 +366,93 @@ class ObjectBox { url_photo_principale: _url_photo_principale, langage: _langage)); - static void _addVisiteInTx(Store store, _Visite) { - store.box().put(_Visite); + static void _addVisitInTx(Store store, _Visite) { + store.box().put(_Visite); } - int getVisiteCount() { + Future syncVisits(List _listVisits) async { + final toSyncId_visite = + _listVisits.map((v) => v['id_visite']).toList(); + final existingVisitsQuery = + visitBox.query(Visit_.id_visite.oneOf(toSyncId_visite)); + final existingVisits = existingVisitsQuery.build().find(); + final existingVisitIds = + existingVisits.map((v) => v.id_visite).toList(); + + final newVisits = _listVisits + .where((v) => !existingVisitIds.contains(v['id_visite'])) + .toList(); + final updatedVisits = _listVisits + .where((v) => existingVisitIds.contains(v['id_visite'])) + .toList(); + + newVisits.forEach((v) { + visitBox.put(Visit.fromJson(v)); + }); + + updatedVisits.forEach((v) { + final existingVisit = + existingVisits.firstWhere((ev) => ev.id_visite == v['id_visite']); + final updatedVisit = Visit.fromJson(v)..id = existingVisit.id; + visitBox.put(updatedVisit); + }); + } + + int getVisitCount() { return visitBox.count(); } - //TODO changer en PHOTO TAG / mettre dans l'ordre CRUD - /// PHOTO TAG --------------------------------------------------------------- + /// VISIT TAG --------------------------------------------------------------- /// - // converts a response body list into a List. - List parseVisiteTags(List responseDataList) { + // converts a response body list into a List. + List parseVisitTags(List responseDataList) { final parsed = responseDataList.cast>(); - return parsed.map((json) => VisiteTag.fromJson(json)).toList(); + return parsed.map((json) => VisitTag.fromJson(json)).toList(); } - Future addVisiteTags(List _listVisiteTags) => + Future addVisitTags(List _listVisitTags) => store.runInTransactionAsync( - TxMode.write, _addVisiteTagsInTx, parseVisiteTags(_listVisiteTags)); + TxMode.write, _addVisitTagsInTx, parseVisitTags(_listVisitTags)); - static void _addVisiteTagsInTx(Store store, _VisiteTags) { - store.box().putMany(_VisiteTags); + static void _addVisitTagsInTx(Store store, _VisitTags) { + store.box().putMany(_VisitTags); } /// save new tag for a distributor / langage - Future addVisiteTag( - int _id_visite_tag, + Future addVisitTag( + int id_visite_tag, int _id_distrib, String _libelle, String _langage, ) => store.runInTransactionAsync( TxMode.write, - _addVisiteTagInTx, - VisiteTag( - id_visite_tag: _id_visite_tag, + _addVisitTagInTx, + VisitTag( + id_visite_tag: id_visite_tag, id_distrib: _id_distrib, libelle: _libelle, langage: _langage, )); - static void _addVisiteTagInTx(Store store, _VisiteTag) { - store.box().put(_VisiteTag); + static void _addVisitTagInTx(Store store, _VisitTag) { + store.box().put(_VisitTag); } - List getVisiteTagsLabels(int distribId, String langage) { + List getVisitTagsLabels(int distribId, String langage) { final query = visitTagBox - .query(VisiteTag_.id_distrib.equals(distribId) & - VisiteTag_.langage.equals(langage)) - //.order(VisiteTag_.libelle) + .query(VisitTag_.id_distrib.equals(distribId) & + VisitTag_.langage.equals(langage)) + //.order(VisitTag_.libelle) .build(); - PropertyQuery pq = query.property(VisiteTag_.libelle); + PropertyQuery pq = query.property(VisitTag_.libelle); pq.distinct = true; pq.caseSensitive = false; return pq.find(); } - int getVisiteTagCount() { + int getVisitTagCount() { return visitTagBox.count(); } @@ -498,7 +574,7 @@ class ObjectBox { } /* remettre les principal à zero - final queryBuilder = box.query(VisitPhoto_.visite_id.equals(idVisite) & VisitPhoto_.photo_principale.equals(1)); + final queryBuilder = box.query(VisitPhoto_.Visit_id.equals(idVisite) & VisitPhoto_.photo_principale.equals(1)); final updatedPhotos = queryBuilder.build().find(); updatedPhotos.forEach((photo) { photo.photo_principale = 0; diff --git a/lib/objectbox.g.dart b/lib/objectbox.g.dart index 53ba97b..e6cd06f 100644 --- a/lib/objectbox.g.dart +++ b/lib/objectbox.g.dart @@ -188,104 +188,6 @@ final _entities = [ ], relations: [], backlinks: []), - ModelEntity( - id: const IdUid(7, 8290500625256822711), - name: 'Visite', - lastPropertyId: const IdUid(14, 5831680857919010234), - flags: 0, - properties: [ - ModelProperty( - id: const IdUid(1, 4764888137112024855), - name: 'id', - type: 6, - flags: 1), - ModelProperty( - id: const IdUid(2, 5254234731633944539), - name: 'id_visite', - type: 6, - flags: 0), - ModelProperty( - id: const IdUid(3, 8500424631546195124), - name: 'type_visite', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(4, 307259370424642142), - name: 'title', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(5, 3383534011872507610), - name: 'allDay', - type: 1, - flags: 0), - ModelProperty( - id: const IdUid(7, 3559563062004847001), - name: 'id_distrib_visite', - type: 6, - flags: 0), - ModelProperty( - id: const IdUid(8, 702894233960499092), - name: 'id_etab', - type: 6, - flags: 0), - ModelProperty( - id: const IdUid(9, 5734787836187957444), - name: 'abandon', - type: 6, - flags: 0), - ModelProperty( - id: const IdUid(11, 3119168728902810585), - name: 'date_visite', - type: 10, - flags: 0), - ModelProperty( - id: const IdUid(13, 1900114397693432703), - name: 'url_photo_principale', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(14, 5831680857919010234), - name: 'langage', - type: 9, - flags: 0) - ], - relations: [], - backlinks: []), - ModelEntity( - id: const IdUid(8, 637444607663700174), - name: 'VisiteTag', - lastPropertyId: const IdUid(5, 1603887098520719919), - flags: 0, - properties: [ - ModelProperty( - id: const IdUid(1, 6166685814637290118), - name: 'id', - type: 6, - flags: 1), - ModelProperty( - id: const IdUid(2, 2059771745036116529), - name: 'id_visite_tag', - type: 6, - flags: 0), - ModelProperty( - id: const IdUid(3, 102253757473665009), - name: 'id_distrib', - type: 6, - flags: 0), - ModelProperty( - id: const IdUid(4, 1526411175344533047), - name: 'libelle', - type: 9, - flags: 0), - ModelProperty( - id: const IdUid(5, 1603887098520719919), - name: 'langage', - type: 9, - flags: 0) - ], - relations: [], - backlinks: []), ModelEntity( id: const IdUid(10, 2779194860339140505), name: 'PhotoTypology', @@ -402,6 +304,119 @@ final _entities = [ flags: 0) ], relations: [], + backlinks: []), + ModelEntity( + id: const IdUid(14, 8708899666252263604), + name: 'Visit', + lastPropertyId: const IdUid(14, 2141854824886685065), + flags: 0, + properties: [ + ModelProperty( + id: const IdUid(1, 1616706753952341931), + name: 'id', + type: 6, + flags: 1), + ModelProperty( + id: const IdUid(2, 481724387420447256), + name: 'id_visite', + type: 6, + flags: 0), + ModelProperty( + id: const IdUid(3, 4117089501376875705), + name: 'date_visite', + type: 10, + flags: 0), + ModelProperty( + id: const IdUid(4, 6960359036987777529), + name: 'date_debut', + type: 10, + flags: 0), + ModelProperty( + id: const IdUid(5, 2625279579039264654), + name: 'date_fin', + type: 10, + flags: 0), + ModelProperty( + id: const IdUid(6, 7413956781075545593), + name: 'date_validation', + type: 10, + flags: 0), + ModelProperty( + id: const IdUid(7, 1634711659019811476), + name: 'type_visite', + type: 9, + flags: 0), + ModelProperty( + id: const IdUid(8, 8990646204619539770), + name: 'title', + type: 9, + flags: 0), + ModelProperty( + id: const IdUid(9, 392889911199239462), + name: 'allDay', + type: 1, + flags: 0), + ModelProperty( + id: const IdUid(10, 7419826409064780644), + name: 'id_distrib_visite', + type: 6, + flags: 0), + ModelProperty( + id: const IdUid(11, 4334641330182955845), + name: 'id_etab', + type: 6, + flags: 0), + ModelProperty( + id: const IdUid(12, 9153440695191140990), + name: 'abandon', + type: 6, + flags: 0), + ModelProperty( + id: const IdUid(13, 2729810367696493366), + name: 'url_photo_principale', + type: 9, + flags: 0), + ModelProperty( + id: const IdUid(14, 2141854824886685065), + name: 'langage', + type: 9, + flags: 0) + ], + relations: [], + backlinks: []), + ModelEntity( + id: const IdUid(15, 8818692504823914757), + name: 'VisitTag', + lastPropertyId: const IdUid(5, 7051299843455307303), + flags: 0, + properties: [ + ModelProperty( + id: const IdUid(1, 43397073361653900), + name: 'id', + type: 6, + flags: 1), + ModelProperty( + id: const IdUid(2, 3130658169202818866), + name: 'id_visite_tag', + type: 6, + flags: 0), + ModelProperty( + id: const IdUid(3, 3266967171909331007), + name: 'id_distrib', + type: 6, + flags: 0), + ModelProperty( + id: const IdUid(4, 1055850760280831759), + name: 'libelle', + type: 9, + flags: 0), + ModelProperty( + id: const IdUid(5, 7051299843455307303), + name: 'langage', + type: 9, + flags: 0) + ], + relations: [], backlinks: []) ]; @@ -425,7 +440,7 @@ Future openStore( ModelDefinition getObjectBoxModel() { final model = ModelInfo( entities: _entities, - lastEntityId: const IdUid(13, 6298506278273268036), + lastEntityId: const IdUid(15, 8818692504823914757), lastIndexId: const IdUid(1, 7907819717055295102), lastRelationId: const IdUid(0, 0), lastSequenceId: const IdUid(0, 0), @@ -433,7 +448,9 @@ ModelDefinition getObjectBoxModel() { 7401686910042688313, 1805690312272107423, 6788844671665652158, - 2910300629980903548 + 2910300629980903548, + 8290500625256822711, + 637444607663700174 ], retiredIndexUids: const [7907819717055295102], retiredPropertyUids: const [ @@ -467,7 +484,26 @@ ModelDefinition getObjectBoxModel() { 6313899520679425027, 4752064214129719799, 319372524127232986, - 783761731187897018 + 783761731187897018, + 4764888137112024855, + 5254234731633944539, + 8500424631546195124, + 307259370424642142, + 3383534011872507610, + 3559563062004847001, + 702894233960499092, + 5734787836187957444, + 3119168728902810585, + 1900114397693432703, + 5831680857919010234, + 6072666250573656763, + 138766887652455581, + 4185186663484548877, + 6166685814637290118, + 2059771745036116529, + 102253757473665009, + 1526411175344533047, + 1603887098520719919 ], retiredRelationUids: const [], modelVersion: 5, @@ -645,105 +681,10 @@ ModelDefinition getObjectBoxModel() { latitude: const fb.StringReader(asciiOptimization: true) .vTableGet(buffer, rootOffset, 18, '')); - return object; - }), - Visite: EntityDefinition( - model: _entities[4], - toOneRelations: (Visite object) => [], - toManyRelations: (Visite object) => {}, - getId: (Visite object) => object.id, - setId: (Visite object, int id) { - object.id = id; - }, - objectToFB: (Visite object, fb.Builder fbb) { - final type_visiteOffset = fbb.writeString(object.type_visite); - final titleOffset = fbb.writeString(object.title); - final url_photo_principaleOffset = - fbb.writeString(object.url_photo_principale); - final langageOffset = fbb.writeString(object.langage); - fbb.startTable(15); - fbb.addInt64(0, object.id); - fbb.addInt64(1, object.id_visite); - fbb.addOffset(2, type_visiteOffset); - fbb.addOffset(3, titleOffset); - fbb.addBool(4, object.allDay); - fbb.addInt64(6, object.id_distrib_visite); - fbb.addInt64(7, object.id_etab); - fbb.addInt64(8, object.abandon); - fbb.addInt64(10, object.date_visite.millisecondsSinceEpoch); - fbb.addOffset(12, url_photo_principaleOffset); - fbb.addOffset(13, langageOffset); - fbb.finish(fbb.endTable()); - return object.id; - }, - objectFromFB: (Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - - final object = Visite( - id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0), - id_visite: - const fb.Int64Reader().vTableGet(buffer, rootOffset, 6, 0), - date_visite: DateTime.fromMillisecondsSinceEpoch( - const fb.Int64Reader().vTableGet(buffer, rootOffset, 24, 0)), - type_visite: const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 8, ''), - title: const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 10, ''), - allDay: const fb.BoolReader() - .vTableGet(buffer, rootOffset, 12, false), - id_distrib_visite: - const fb.Int64Reader().vTableGet(buffer, rootOffset, 16, 0), - id_etab: - const fb.Int64Reader().vTableGet(buffer, rootOffset, 18, 0), - abandon: - const fb.Int64Reader().vTableGet(buffer, rootOffset, 20, 0), - url_photo_principale: const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 28, ''), - langage: - const fb.StringReader(asciiOptimization: true).vTableGet(buffer, rootOffset, 30, '')); - - return object; - }), - VisiteTag: EntityDefinition( - model: _entities[5], - toOneRelations: (VisiteTag object) => [], - toManyRelations: (VisiteTag object) => {}, - getId: (VisiteTag object) => object.id, - setId: (VisiteTag object, int id) { - object.id = id; - }, - objectToFB: (VisiteTag object, fb.Builder fbb) { - final libelleOffset = fbb.writeString(object.libelle); - final langageOffset = fbb.writeString(object.langage); - fbb.startTable(6); - fbb.addInt64(0, object.id); - fbb.addInt64(1, object.id_visite_tag); - fbb.addInt64(2, object.id_distrib); - fbb.addOffset(3, libelleOffset); - fbb.addOffset(4, langageOffset); - fbb.finish(fbb.endTable()); - return object.id; - }, - objectFromFB: (Store store, ByteData fbData) { - final buffer = fb.BufferContext(fbData); - final rootOffset = buffer.derefObject(0); - - final object = VisiteTag( - id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0), - id_visite_tag: - const fb.Int64Reader().vTableGet(buffer, rootOffset, 6, 0), - id_distrib: - const fb.Int64Reader().vTableGet(buffer, rootOffset, 8, 0), - libelle: const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 10, ''), - langage: const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 12, '')); - return object; }), PhotoTypology: EntityDefinition( - model: _entities[6], + model: _entities[4], toOneRelations: (PhotoTypology object) => [], toManyRelations: (PhotoTypology object) => {}, getId: (PhotoTypology object) => object.id, @@ -776,7 +717,7 @@ ModelDefinition getObjectBoxModel() { return object; }), EtabCompetitor: EntityDefinition( - model: _entities[7], + model: _entities[5], toOneRelations: (EtabCompetitor object) => [], toManyRelations: (EtabCompetitor object) => {}, getId: (EtabCompetitor object) => object.id, @@ -809,7 +750,7 @@ ModelDefinition getObjectBoxModel() { return object; }), VisitPhoto: EntityDefinition( - model: _entities[8], + model: _entities[6], toOneRelations: (VisitPhoto object) => [], toManyRelations: (VisitPhoto object) => {}, getId: (VisitPhoto object) => object.id, @@ -858,6 +799,111 @@ ModelDefinition getObjectBoxModel() { uploaded: const fb.Int64Reader().vTableGet(buffer, rootOffset, 22, 0)); + return object; + }), + Visit: EntityDefinition( + model: _entities[7], + toOneRelations: (Visit object) => [], + toManyRelations: (Visit object) => {}, + getId: (Visit object) => object.id, + setId: (Visit object, int id) { + object.id = id; + }, + objectToFB: (Visit object, fb.Builder fbb) { + final type_visiteOffset = fbb.writeString(object.type_visite); + final titleOffset = fbb.writeString(object.title); + final url_photo_principaleOffset = + fbb.writeString(object.url_photo_principale); + final langageOffset = fbb.writeString(object.langage); + fbb.startTable(15); + fbb.addInt64(0, object.id); + fbb.addInt64(1, object.id_visite); + fbb.addInt64(2, object.date_visite.millisecondsSinceEpoch); + fbb.addInt64(3, object.date_debut.millisecondsSinceEpoch); + fbb.addInt64(4, object.date_fin?.millisecondsSinceEpoch); + fbb.addInt64(5, object.date_validation?.millisecondsSinceEpoch); + fbb.addOffset(6, type_visiteOffset); + fbb.addOffset(7, titleOffset); + fbb.addBool(8, object.allDay); + fbb.addInt64(9, object.id_distrib_visite); + fbb.addInt64(10, object.id_etab); + fbb.addInt64(11, object.abandon); + fbb.addOffset(12, url_photo_principaleOffset); + fbb.addOffset(13, langageOffset); + fbb.finish(fbb.endTable()); + return object.id; + }, + objectFromFB: (Store store, ByteData fbData) { + final buffer = fb.BufferContext(fbData); + final rootOffset = buffer.derefObject(0); + final date_finValue = + const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 12); + final date_validationValue = + const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 14); + final object = Visit( + id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0), + id_visite: + const fb.Int64Reader().vTableGet(buffer, rootOffset, 6, 0), + date_visite: DateTime.fromMillisecondsSinceEpoch( + const fb.Int64Reader().vTableGet(buffer, rootOffset, 8, 0)), + date_debut: DateTime.fromMillisecondsSinceEpoch( + const fb.Int64Reader().vTableGet(buffer, rootOffset, 10, 0)), + date_fin: date_finValue == null + ? null + : DateTime.fromMillisecondsSinceEpoch(date_finValue), + date_validation: date_validationValue == null + ? null + : DateTime.fromMillisecondsSinceEpoch(date_validationValue), + type_visite: const fb.StringReader(asciiOptimization: true) + .vTableGet(buffer, rootOffset, 16, ''), + title: const fb.StringReader(asciiOptimization: true) + .vTableGet(buffer, rootOffset, 18, ''), + allDay: const fb.BoolReader() + .vTableGet(buffer, rootOffset, 20, false), + id_distrib_visite: + const fb.Int64Reader().vTableGet(buffer, rootOffset, 22, 0), + id_etab: const fb.Int64Reader().vTableGet(buffer, rootOffset, 24, 0), + abandon: const fb.Int64Reader().vTableGet(buffer, rootOffset, 26, 0), + url_photo_principale: const fb.StringReader(asciiOptimization: true).vTableGet(buffer, rootOffset, 28, ''), + langage: const fb.StringReader(asciiOptimization: true).vTableGet(buffer, rootOffset, 30, '')); + + return object; + }), + VisitTag: EntityDefinition( + model: _entities[8], + toOneRelations: (VisitTag object) => [], + toManyRelations: (VisitTag object) => {}, + getId: (VisitTag object) => object.id, + setId: (VisitTag object, int id) { + object.id = id; + }, + objectToFB: (VisitTag object, fb.Builder fbb) { + final libelleOffset = fbb.writeString(object.libelle); + final langageOffset = fbb.writeString(object.langage); + fbb.startTable(6); + fbb.addInt64(0, object.id); + fbb.addInt64(1, object.id_visite_tag); + fbb.addInt64(2, object.id_distrib); + fbb.addOffset(3, libelleOffset); + fbb.addOffset(4, langageOffset); + fbb.finish(fbb.endTable()); + return object.id; + }, + objectFromFB: (Store store, ByteData fbData) { + final buffer = fb.BufferContext(fbData); + final rootOffset = buffer.derefObject(0); + + final object = VisitTag( + id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0), + id_visite_tag: + const fb.Int64Reader().vTableGet(buffer, rootOffset, 6, 0), + id_distrib: + const fb.Int64Reader().vTableGet(buffer, rootOffset, 8, 0), + libelle: const fb.StringReader(asciiOptimization: true) + .vTableGet(buffer, rootOffset, 10, ''), + langage: const fb.StringReader(asciiOptimization: true) + .vTableGet(buffer, rootOffset, 12, '')); + return object; }) }; @@ -955,150 +1001,161 @@ class Etab_ { QueryStringProperty(_entities[3].properties[7]); } -/// [Visite] entity fields to define ObjectBox queries. -class Visite_ { - /// see [Visite.id] - static final id = QueryIntegerProperty(_entities[4].properties[0]); - - /// see [Visite.id_visite] - static final id_visite = - QueryIntegerProperty(_entities[4].properties[1]); - - /// see [Visite.type_visite] - static final type_visite = - QueryStringProperty(_entities[4].properties[2]); - - /// see [Visite.title] - static final title = QueryStringProperty(_entities[4].properties[3]); - - /// see [Visite.allDay] - static final allDay = - QueryBooleanProperty(_entities[4].properties[4]); - - /// see [Visite.id_distrib_visite] - static final id_distrib_visite = - QueryIntegerProperty(_entities[4].properties[5]); - - /// see [Visite.id_etab] - static final id_etab = - QueryIntegerProperty(_entities[4].properties[6]); - - /// see [Visite.abandon] - static final abandon = - QueryIntegerProperty(_entities[4].properties[7]); - - /// see [Visite.date_visite] - static final date_visite = - QueryIntegerProperty(_entities[4].properties[8]); - - /// see [Visite.url_photo_principale] - static final url_photo_principale = - QueryStringProperty(_entities[4].properties[9]); - - /// see [Visite.langage] - static final langage = - QueryStringProperty(_entities[4].properties[10]); -} - -/// [VisiteTag] entity fields to define ObjectBox queries. -class VisiteTag_ { - /// see [VisiteTag.id] - static final id = QueryIntegerProperty(_entities[5].properties[0]); - - /// see [VisiteTag.id_visite_tag] - static final id_visite_tag = - QueryIntegerProperty(_entities[5].properties[1]); - - /// see [VisiteTag.id_distrib] - static final id_distrib = - QueryIntegerProperty(_entities[5].properties[2]); - - /// see [VisiteTag.libelle] - static final libelle = - QueryStringProperty(_entities[5].properties[3]); - - /// see [VisiteTag.langage] - static final langage = - QueryStringProperty(_entities[5].properties[4]); -} - /// [PhotoTypology] entity fields to define ObjectBox queries. class PhotoTypology_ { /// see [PhotoTypology.id] static final id = - QueryIntegerProperty(_entities[6].properties[0]); + QueryIntegerProperty(_entities[4].properties[0]); /// see [PhotoTypology.id_photo_typologie] static final id_photo_typologie = - QueryIntegerProperty(_entities[6].properties[1]); + QueryIntegerProperty(_entities[4].properties[1]); /// see [PhotoTypology.libelle] static final libelle = - QueryStringProperty(_entities[6].properties[2]); + QueryStringProperty(_entities[4].properties[2]); /// see [PhotoTypology.ordre] static final ordre = - QueryIntegerProperty(_entities[6].properties[3]); + QueryIntegerProperty(_entities[4].properties[3]); } /// [EtabCompetitor] entity fields to define ObjectBox queries. class EtabCompetitor_ { /// see [EtabCompetitor.id] static final id = - QueryIntegerProperty(_entities[7].properties[0]); + QueryIntegerProperty(_entities[5].properties[0]); /// see [EtabCompetitor.id_concurrence_lien] static final id_concurrence_lien = - QueryIntegerProperty(_entities[7].properties[1]); + QueryIntegerProperty(_entities[5].properties[1]); /// see [EtabCompetitor.id_etab] static final id_etab = - QueryIntegerProperty(_entities[7].properties[2]); + QueryIntegerProperty(_entities[5].properties[2]); /// see [EtabCompetitor.nom] static final nom = - QueryStringProperty(_entities[7].properties[3]); + QueryStringProperty(_entities[5].properties[3]); } /// [VisitPhoto] entity fields to define ObjectBox queries. class VisitPhoto_ { /// see [VisitPhoto.id] static final id = - QueryIntegerProperty(_entities[8].properties[0]); + QueryIntegerProperty(_entities[6].properties[0]); /// see [VisitPhoto.id_visite] static final id_visite = - QueryIntegerProperty(_entities[8].properties[1]); + QueryIntegerProperty(_entities[6].properties[1]); /// see [VisitPhoto.id_photo_typologie] static final id_photo_typologie = - QueryIntegerProperty(_entities[8].properties[2]); + QueryIntegerProperty(_entities[6].properties[2]); /// see [VisitPhoto.image_name] static final image_name = - QueryStringProperty(_entities[8].properties[3]); + QueryStringProperty(_entities[6].properties[3]); /// see [VisitPhoto.date_photo] static final date_photo = - QueryIntegerProperty(_entities[8].properties[4]); + QueryIntegerProperty(_entities[6].properties[4]); /// see [VisitPhoto.id_photo_mp4] static final id_photo_mp4 = - QueryIntegerProperty(_entities[8].properties[5]); + QueryIntegerProperty(_entities[6].properties[5]); /// see [VisitPhoto.photo_privee] static final photo_privee = - QueryIntegerProperty(_entities[8].properties[6]); + QueryIntegerProperty(_entities[6].properties[6]); /// see [VisitPhoto.photo_principale] static final photo_principale = - QueryIntegerProperty(_entities[8].properties[7]); + QueryIntegerProperty(_entities[6].properties[7]); /// see [VisitPhoto.tags] static final tags = - QueryStringProperty(_entities[8].properties[8]); + QueryStringProperty(_entities[6].properties[8]); /// see [VisitPhoto.uploaded] static final uploaded = - QueryIntegerProperty(_entities[8].properties[9]); + QueryIntegerProperty(_entities[6].properties[9]); +} + +/// [Visit] entity fields to define ObjectBox queries. +class Visit_ { + /// see [Visit.id] + static final id = QueryIntegerProperty(_entities[7].properties[0]); + + /// see [Visit.id_visite] + static final id_visite = + QueryIntegerProperty(_entities[7].properties[1]); + + /// see [Visit.date_visite] + static final date_visite = + QueryIntegerProperty(_entities[7].properties[2]); + + /// see [Visit.date_debut] + static final date_debut = + QueryIntegerProperty(_entities[7].properties[3]); + + /// see [Visit.date_fin] + static final date_fin = + QueryIntegerProperty(_entities[7].properties[4]); + + /// see [Visit.date_validation] + static final date_validation = + QueryIntegerProperty(_entities[7].properties[5]); + + /// see [Visit.type_visite] + static final type_visite = + QueryStringProperty(_entities[7].properties[6]); + + /// see [Visit.title] + static final title = QueryStringProperty(_entities[7].properties[7]); + + /// see [Visit.allDay] + static final allDay = QueryBooleanProperty(_entities[7].properties[8]); + + /// see [Visit.id_distrib_visite] + static final id_distrib_visite = + QueryIntegerProperty(_entities[7].properties[9]); + + /// see [Visit.id_etab] + static final id_etab = + QueryIntegerProperty(_entities[7].properties[10]); + + /// see [Visit.abandon] + static final abandon = + QueryIntegerProperty(_entities[7].properties[11]); + + /// see [Visit.url_photo_principale] + static final url_photo_principale = + QueryStringProperty(_entities[7].properties[12]); + + /// see [Visit.langage] + static final langage = + QueryStringProperty(_entities[7].properties[13]); +} + +/// [VisitTag] entity fields to define ObjectBox queries. +class VisitTag_ { + /// see [VisitTag.id] + static final id = QueryIntegerProperty(_entities[8].properties[0]); + + /// see [VisitTag.id_visite_tag] + static final id_visite_tag = + QueryIntegerProperty(_entities[8].properties[1]); + + /// see [VisitTag.id_distrib] + static final id_distrib = + QueryIntegerProperty(_entities[8].properties[2]); + + /// see [VisitTag.libelle] + static final libelle = + QueryStringProperty(_entities[8].properties[3]); + + /// see [VisitTag.langage] + static final langage = + QueryStringProperty(_entities[8].properties[4]); } diff --git a/lib/ui/sync/tab_sync.dart b/lib/old/tab_sync.dart similarity index 95% rename from lib/ui/sync/tab_sync.dart rename to lib/old/tab_sync.dart index 90ff768..43a5307 100644 --- a/lib/ui/sync/tab_sync.dart +++ b/lib/old/tab_sync.dart @@ -207,8 +207,8 @@ class _TabSyncPageState extends State child: Icon(Icons.chevron_right), onPressed: () async { var futures = [ - _apiProvider.SyncEtablissements(), - _apiProvider.SyncVisites(), + //_apiProvider.SyncEtablissements(), + //_apiProvider.SyncVisites(), _apiProvider.SyncPhotos(), ]; diff --git a/lib/ui/home.dart b/lib/ui/home.dart index 26f218e..15a87f2 100644 --- a/lib/ui/home.dart +++ b/lib/ui/home.dart @@ -5,7 +5,7 @@ import 'package:mobdr/main.dart'; import 'package:mobdr/ui/account/tab_account.dart'; import 'package:mobdr/ui/home/tab_home.dart'; import 'package:mobdr/ui/mp4/tab_mp4.dart'; -import 'package:mobdr/ui/sync/tab_sync.dart'; +import 'package:mobdr/ui/visit/tab_visit.dart'; import 'package:mobdr/config/constant.dart'; import 'package:mobdr/events.dart'; @@ -18,7 +18,7 @@ class HomePage extends StatefulWidget { class _HomePageState extends State with SingleTickerProviderStateMixin { late PageController _pageController; - late StreamSubscription sub; // déclaration de la variable sub + late StreamSubscription subUrlEvent; int _currentIndex = 0; @@ -28,7 +28,7 @@ class _HomePageState extends State void initState() { _contentPages = [ TabHomePage(), - TabSyncPage(), + TabVisitListPage(), TabMP4Page(), TabAccountPage(), ]; @@ -38,7 +38,7 @@ class _HomePageState extends State _pageController.addListener(_handleTabSelection); // Listen particular event - sub = eventBus.on().listen((e) { + subUrlEvent = eventBus.on().listen((e) { setState(() { _currentIndex = 2; _pageController.jumpToPage(_currentIndex); @@ -55,7 +55,7 @@ class _HomePageState extends State @override void dispose() { - sub.cancel(); + subUrlEvent.cancel(); _pageController.dispose(); super.dispose(); } @@ -95,7 +95,7 @@ class _HomePageState extends State icon: Icon(Icons.home, color: _currentIndex == 0 ? PRIMARY_COLOR : CHARCOAL)), BottomNavigationBarItem( - label: 'Sync', + label: 'Visits', icon: Icon(Icons.sync, color: _currentIndex == 1 ? ASSENT_COLOR : CHARCOAL)), BottomNavigationBarItem( diff --git a/lib/ui/home/tab_home.dart b/lib/ui/home/tab_home.dart index 28054ae..8238769 100644 --- a/lib/ui/home/tab_home.dart +++ b/lib/ui/home/tab_home.dart @@ -3,20 +3,27 @@ This is home page we used AutomaticKeepAliveClientMixin to keep the state when moving from 1 navbar to another navbar, so the page is not refresh overtime */ +import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:badges/badges.dart' as badges; +import 'package:mobdr/main.dart'; import 'package:mobdr/config/constant.dart'; import 'package:mobdr/cubit/language/language_cubit.dart'; import 'package:mobdr/cubit/language/app_localizations.dart'; import 'package:mobdr/service/shared_prefs.dart'; import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/events.dart'; +import 'package:mobdr/ui/sync/sync_calendar.dart'; import 'package:mobdr/model/visite_model.dart'; +import 'package:mobdr/ui/visit/visit_photo_typology.dart'; import 'package:mobdr/ui/general/chat_us.dart'; import 'package:mobdr/ui/general/notification.dart'; import 'package:mobdr/ui/reusable/reusable_widget.dart'; +import 'package:mobdr/ui/reusable/cache_image_network.dart'; class TabHomePage extends StatefulWidget { @override @@ -28,8 +35,7 @@ class _TabHomePageState extends State // initialize global function and reusable widget final _reusableWidget = ReusableWidget(); - // _listKey is used for AnimatedList - final GlobalKey _listKey = GlobalKey(); + late StreamSubscription subVisitPhotoCountEvent; // keep the state to do not refresh when switch navbar @override @@ -42,8 +48,8 @@ class _TabHomePageState extends State bool _isLoading = true; String _errorMessage = ''; - late List todayVisitsData = []; - late List previousVisitsData = []; + late List todayVisitData = []; + late List previousVisitData = []; @override void initState() { @@ -60,12 +66,32 @@ class _TabHomePageState extends State loadData().then((_) { setState(() { _isLoading = false; + + // Listen particular event + subVisitPhotoCountEvent = + eventBus.on().listen((e) { + setState(() { + for (int i = 0; i < todayVisitData.length; i++) { + if (todayVisitData[i].id_visite == e.id_visite) { + todayVisitData[i].photoCount = e.photoCount; + break; + } + } + for (int i = 0; i < previousVisitData.length; i++) { + if (previousVisitData[i].id_visite == e.id_visite) { + previousVisitData[i].photoCount = e.photoCount; + break; + } + } + }); + }); }); }); } @override void dispose() { + subVisitPhotoCountEvent.cancel(); super.dispose(); } @@ -139,35 +165,48 @@ class _TabHomePageState extends State mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('Today Visits', style: GlobalStyle.horizontalTitle), - GestureDetector( - onTap: () { - //Navigator.push(context, MaterialPageRoute(builder: (context) => RestaurantListPage(title: 'Food Arround You'))); - }, - child: Text('View All', - style: GlobalStyle.viewAll, textAlign: TextAlign.end), - ) ], ), ), - Container( + if (todayVisitData.length == 0) + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox(height: 16), // Ajout de l'espace ici + Text('Aucune visite ce jour'), + ElevatedButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => SyncCalendarPage()), + ); + }, + child: Text('Synchroniser'), + ), + ], + ) + else + Container( margin: EdgeInsets.only(top: 8), height: boxImageSize * GlobalStyle.cardHeightMultiplication, child: ListView.builder( padding: EdgeInsets.symmetric(horizontal: 12), scrollDirection: Axis.horizontal, - itemCount: todayVisitsData.length, + itemCount: todayVisitData.length, itemBuilder: (BuildContext context, int index) { - return _reusableWidget.buildHorizontalVisitListCard( - context, - todayVisitsData[index], - ); + return buildHorizontalVisitListCard( + context, todayVisitData[index]); }, - )), + ), + ), ], ); } Widget _builPreviousVisits(boxImageSize) { + if (previousVisitData.length == 0) { + return SizedBox.shrink(); // Rien ne sera affiché + } return Column( children: [ Container( @@ -176,38 +215,130 @@ class _TabHomePageState extends State mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('Previous Visits', style: GlobalStyle.horizontalTitle), - GestureDetector( - onTap: () { - //Navigator.push(context, MaterialPageRoute(builder: (context) => RestaurantListPage(title: 'Food Arround You'))); - }, - child: Text('View All', - style: GlobalStyle.viewAll, textAlign: TextAlign.end), - ) ], ), ), Container( - margin: EdgeInsets.only(top: 8), - height: boxImageSize * GlobalStyle.cardHeightMultiplication, - child: ListView.builder( - padding: EdgeInsets.symmetric(horizontal: 12), - scrollDirection: Axis.horizontal, - itemCount: previousVisitsData.length, - itemBuilder: (BuildContext context, int index) { - return _reusableWidget.buildHorizontalVisitListCard( - context, previousVisitsData[index]); - }, - )), + margin: EdgeInsets.only(top: 8), + height: boxImageSize * GlobalStyle.cardHeightMultiplication, + child: ListView.builder( + padding: EdgeInsets.symmetric(horizontal: 12), + scrollDirection: Axis.horizontal, + itemCount: previousVisitData.length, + itemBuilder: (BuildContext context, int index) { + return buildHorizontalVisitListCard( + context, previousVisitData[index]); + }, + ), + ), ], ); } + Widget buildHorizontalVisitListCard(context, data) { + final double imageWidth = (MediaQuery.of(context).size.width / 2.3); + final double imageheight = (MediaQuery.of(context).size.width / 3.07); + + return Container( + width: imageWidth, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + elevation: 2, + color: Colors.white, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + Route route = MaterialPageRoute( + builder: (context) => VisitPhotoTypologyPage( + pp_id_distrib: data.id_distrib, + pp_langage: data.langage, + pp_id_visite: data.id_visite, + pp_name: data.name, + onRefreshVisit: (int photoCount) {}, + ), + ); + Navigator.push(context, route); + }, + child: Stack( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(6)), + child: buildCacheNetworkImage( + width: imageWidth, + height: imageheight, + url: data.image)), + Container( + margin: EdgeInsets.all(8), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + height: 36, + child: Text(data.name, + style: GlobalStyle.cardTitle, + maxLines: 2, + overflow: TextOverflow.ellipsis), + ), + ], + ), + ), + ], + ), + Positioned( + bottom: 4, + right: 4, // alignement à droite + child: GestureDetector( + onTap: () { + // TODO si visite validée ce n'est pas la meme url (on il mettre date validation dans le json frederik + SharedPrefs().urlMP4 = + '${ApiConstants.baseUrl}/MobilePortal4/index.html#ajax/visite_modification.html?visite=${data.id_visite}'; + eventBus.fire(UrlEvent(SharedPrefs().urlMP4)); + }, + child: Image.asset( + "assets/images/logo_mp4.png", + width: 24, + height: 24, + ), + ), + ), + if (data.photoCount != 0) + Positioned( + bottom: 0, + left: 2, // alignement à gauche + child: Container( + margin: EdgeInsets.symmetric(vertical: 2), + child: badges.Badge( + badgeStyle: badges.BadgeStyle( + badgeColor: Colors.blue, + padding: EdgeInsets.all(data.photoCount >= 10 ? 2 : 6), + ), + badgeContent: Text(data.photoCount.toString(), + style: TextStyle(color: Colors.white)), + child: Icon(Icons.camera_alt_sharp), + ), + ), + ), + ], + ), + ), + ), + ); + } + /// Initializes data when the page loads. Future loadData() async { try { // visite model initialisation - todayVisitsData = await VisiteModel.getAllVisites(); - previousVisitsData = await VisiteModel.getAllVisites(); + todayVisitData = await VisiteModel.getTodayVisit(); + previousVisitData = await VisiteModel.getPreviousVisit(); } catch (e) { // set errorMessage for debug _errorMessage = 'Error loading visites : $e'; diff --git a/lib/ui/reusable/reusable_widget.dart b/lib/ui/reusable/reusable_widget.dart index bd0c4a3..37695ec 100644 --- a/lib/ui/reusable/reusable_widget.dart +++ b/lib/ui/reusable/reusable_widget.dart @@ -231,75 +231,4 @@ class ReusableWidget { ), ); } - - Widget buildHorizontalVisitListCard(context, data) { - final double imageWidth = (MediaQuery.of(context).size.width / 2.3); - final double imageheight = (MediaQuery.of(context).size.width / 3.07); - return Container( - width: imageWidth, - child: Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(6), - ), - elevation: 2, - color: Colors.white, - child: GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () { - //Navigator.push(context, MaterialPageRoute(builder: (context) => DetailRestaurantPage())); - }, - child: Stack( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ClipRRect( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(10), - topRight: Radius.circular(6)), - child: buildCacheNetworkImage( - width: imageWidth, - height: imageheight, - url: data.image)), - Container( - margin: EdgeInsets.all(8), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - height: 36, - child: Text(data.name, - style: GlobalStyle.cardTitle, - maxLines: 2, - overflow: TextOverflow.ellipsis), - ), - ], - ), - ), - ], - ), - Positioned( - bottom: 4, //ajout de cette ligne pour placer l'icone en bas - right: 4, //ajout de cette ligne pour placer l'icone à droite - child: GestureDetector( - onTap: () { - // TODO si visite validée ce n'est pas la meme url (on il mettre date validation dans le json - SharedPrefs().urlMP4 = - '${ApiConstants.baseUrl}/MobilePortal4/index.html#ajax/visite_modification.html?visite=${data.id_visite}'; - eventBus.fire(UrlEvent(SharedPrefs().urlMP4)); - }, - child: Image.asset( - "assets/images/logo_mp4.png", - width: 24, - height: 24, - ), - ), - ), - ], - ), - ), - ), - ); - } } diff --git a/lib/ui/sync/sync_calendar.dart b/lib/ui/sync/sync_calendar.dart new file mode 100644 index 0000000..a354f6c --- /dev/null +++ b/lib/ui/sync/sync_calendar.dart @@ -0,0 +1,116 @@ +import 'package:flutter/material.dart'; +import 'package:mobdr/network/api_provider.dart'; + +class SyncCalendarPage extends StatefulWidget { + @override + _SyncCalendarPageState createState() => _SyncCalendarPageState(); +} + +class _SyncCalendarPageState extends State { + bool _isSyncing = false; + bool _syncSuccessful = false; + String? _syncErrorMessage; + + Future _syncData() async { + setState(() { + _isSyncing = true; + }); + + final ApiProvider _apiProvider = ApiProvider(); + + final syncResult = await _apiProvider.SyncCalendar(); + + if (syncResult == 'OK') { + setState(() { + _isSyncing = false; + _syncSuccessful = true; + }); + + // Wait for 2 seconds before navigating back to the previous screen. + await Future.delayed(Duration(seconds: 2)); + + _popScreen(); + } else { + setState(() { + _isSyncing = false; + _syncSuccessful = false; + _syncErrorMessage = syncResult; + }); + } + } + + void _popScreen() { + if (mounted) { + Navigator.of(context).pop(); + } + } + + @override + Widget build(BuildContext context) { + return WillPopScope( + // Disable the back button while the synchronization is in progress. + onWillPop: () async => !_isSyncing, + child: Scaffold( + appBar: AppBar( + title: Text('Synchronisation'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (!_isSyncing && !_syncSuccessful && _syncErrorMessage == null) + Text( + 'Appuyez sur le bouton pour lancer la synchronisation.', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18.0), + ), + if (_isSyncing) + Text( + 'Synchronisation en cours...', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18.0), + ), + if (_syncSuccessful) + Column( + children: [ + Icon(Icons.check_circle, size: 100.0, color: Colors.green), + SizedBox(height: 16.0), + ElevatedButton( + onPressed: _popScreen, + child: Text('Synchronisation réussie'), + ), + ], + ), + if (_syncErrorMessage != null) + Column( + children: [ + Icon(Icons.error, size: 100.0, color: Colors.red), + SizedBox(height: 16.0), + Text( + _syncErrorMessage!, + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18.0), + ), + SizedBox(height: 16.0), + ElevatedButton( + onPressed: _isSyncing ? null : _syncData, + child: _isSyncing + ? CircularProgressIndicator() + : Text('Relancer la synchronisation'), + ), + ], + ), + if (!_isSyncing && !_syncSuccessful && _syncErrorMessage == null) + ElevatedButton( + onPressed: _isSyncing ? null : _syncData, + child: _isSyncing + ? CircularProgressIndicator() + : Text('Lancer la synchronisation'), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/ui/visit/tab_visit.dart b/lib/ui/visit/tab_visit.dart new file mode 100644 index 0000000..7aeb7a4 --- /dev/null +++ b/lib/ui/visit/tab_visit.dart @@ -0,0 +1,332 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; + +import 'package:mobdr/main.dart'; +import 'package:mobdr/config/constant.dart'; +import 'package:mobdr/config/global_style.dart'; +import 'package:mobdr/events.dart'; +import 'package:mobdr/model/visite_model.dart'; +import 'package:mobdr/ui/visit/visit_photo_typology.dart'; +import 'package:mobdr/ui/reusable/cache_image_network.dart'; + +class TabVisitListPage extends StatefulWidget { + @override + _TabVisitListPageState createState() => _TabVisitListPageState(); +} + +class _TabVisitListPageState extends State + with AutomaticKeepAliveClientMixin { + // _listKey is used for AnimatedList + final GlobalKey _listKey = GlobalKey(); + + // keep the state to do not refresh when switch navbar + @override + bool get wantKeepAlive => true; + + bool _isLoading = true; + String _errorMessage = ''; + + late List modelData = []; + + late StreamSubscription subVisitPhotoCountEvent; + + @override + void initState() { + super.initState(); + + // Listen particular event + subVisitPhotoCountEvent = eventBus.on().listen((e) { + setState(() { + for (int i = 0; i < modelData.length; i++) { + if (modelData[i].id_visite == e.id_visite) { + modelData[i].photoCount = e.photoCount; + break; + } + } + }); + }); + + loadData().then((_) { + setState(() { + _isLoading = false; + }); + }); + } + + @override + void dispose() { + subVisitPhotoCountEvent.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + // if we used AutomaticKeepAliveClientMixin, we must call super.build(context); + super.build(context); + final double boxImageSize = (MediaQuery.of(context).size.width / 4); + if (_isLoading) { + return Center(child: CircularProgressIndicator()); + } else if (modelData.isEmpty) { + return Center( + child: Text('Aucune visite trouvée.'), + ); + } + return Scaffold( + appBar: AppBar( + iconTheme: IconThemeData( + color: GlobalStyle.appBarIconThemeColor, + ), + elevation: GlobalStyle.appBarElevation, + title: Text( + 'Visit List', + style: GlobalStyle.appBarTitle, + ), + backgroundColor: GlobalStyle.appBarBackgroundColor, + systemOverlayStyle: GlobalStyle.appBarSystemOverlayStyle), + body: Column(children: [ + Flexible( + child: AnimatedList( + key: _listKey, + initialItemCount: modelData.length, + physics: AlwaysScrollableScrollPhysics(), + itemBuilder: (context, index, animation) { + return _buildVisitelistCard( + modelData[index], boxImageSize, animation, index); + }, + )), + Container( + padding: EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.grey, + offset: Offset(0.0, 1.0), //(x,y) + blurRadius: 2.0, + ), + ], + ), + child: Row( + children: [ + Container( + child: GestureDetector( + onTap: () { + // TODO functionality to be implemented + /*` + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ChatUsPage())); + */ + }, + child: ClipOval( + child: Container( + color: SOFT_BLUE, + padding: EdgeInsets.all(9), + child: Icon(Icons.filter_list, + color: Colors.white, size: 16)), + ), + ), + ), + SizedBox( + width: 10, + ), + Expanded( + child: GestureDetector( + onTap: () {}, + child: Container( + alignment: Alignment.center, + padding: EdgeInsets.fromLTRB(12, 8, 12, 8), + margin: EdgeInsets.only(right: 8), + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(width: 1, color: SOFT_BLUE), + borderRadius: BorderRadius.all(Radius.circular( + 10) // <--- border radius here + )), + child: Text('Synchronize ALL', + style: TextStyle( + color: SOFT_BLUE, fontWeight: FontWeight.bold)), + ), + ), + ), + ], + ), + ) + ])); + } + + Widget _buildVisitelistCard( + VisiteModel visiteData, boxImageSize, animation, index) { + return SizeTransition( + sizeFactor: animation, + child: GestureDetector( + onTap: () { + Route route = MaterialPageRoute( + builder: (context) => VisitPhotoTypologyPage( + pp_id_distrib: visiteData.id_distrib, + pp_langage: visiteData.langage, + pp_id_visite: visiteData.id_visite, + pp_name: visiteData.name, + onRefreshVisit: (int photoCount) {}, + )); + Navigator.push(context, route); + }, + child: Container( + margin: EdgeInsets.fromLTRB(12, 6, 12, 0), + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + elevation: 2, + color: Colors.white, + child: Container( + margin: EdgeInsets.all(8), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(10)), + child: buildCacheNetworkImage( + width: boxImageSize, + height: boxImageSize, + url: visiteData.image, + ), + ), + SizedBox( + width: 10, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + children: [ + Text(visiteData.name, + style: TextStyle( + fontSize: 11, color: SOFT_GREY)) + ], + ), + ), + Container(height: 8), + Text( + visiteData.date, + style: GlobalStyle.productSale, + ), + Container( + margin: EdgeInsets.only(top: 5), + child: Text( + '${visiteData.photoCount} Photo(s)', + style: GlobalStyle.productPrice, + ), + ), + Container(height: 8), + Container( + margin: EdgeInsets.only(top: 5), + child: Row( + children: [ + Icon( + Icons.store, + color: SOFT_GREY, + size: 20, + ), + Text( + ' ' + visiteData.type_visite, + style: GlobalStyle.productName.copyWith( + fontSize: 13, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ) + ], + ), + ), + ], + ), + ) + ], + ), + Container( + margin: EdgeInsets.only(top: 12), + child: Row( + children: [ + Expanded( + child: (visiteData.photoCount == 0) + ? TextButton( + style: ButtonStyle( + minimumSize: + MaterialStateProperty.all(Size(0, 30)), + backgroundColor: MaterialStateProperty + .resolveWith( + (Set states) => + Colors.grey[300]!, + ), + overlayColor: MaterialStateProperty.all( + Colors.transparent), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.0), + )), + ), + onPressed: null, + child: Text( + 'Synchronize', + style: TextStyle( + color: Colors.grey[600], + fontWeight: FontWeight.bold, + fontSize: 13), + textAlign: TextAlign.center, + )) + : OutlinedButton( + onPressed: () {}, + style: ButtonStyle( + minimumSize: MaterialStateProperty.all( + Size(0, 30)), + overlayColor: MaterialStateProperty.all( + Colors.transparent), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(5.0), + )), + side: MaterialStateProperty.all( + BorderSide( + color: SOFT_BLUE, width: 1.0), + )), + child: Text( + 'Synchronize', + style: TextStyle( + color: SOFT_BLUE, + fontWeight: FontWeight.bold, + fontSize: 13), + textAlign: TextAlign.center, + )), + ), + ], + ), + ) + ], + ), + ), + ), + ), + ), + ); + } + + /// Initializes data when the page loads. + Future loadData() async { + try { + // data initialisation with today visits + modelData = await VisiteModel.getAllVisit(); + } catch (e) { + // set errorMessage for debug + _errorMessage = 'Error loading visits : $e'; + } + } +} diff --git a/lib/ui/home/visit_photo_tag.dart b/lib/ui/visit/visit_photo_tag.dart similarity index 98% rename from lib/ui/home/visit_photo_tag.dart rename to lib/ui/visit/visit_photo_tag.dart index 7c3edf9..f29c20a 100644 --- a/lib/ui/home/visit_photo_tag.dart +++ b/lib/ui/visit/visit_photo_tag.dart @@ -210,7 +210,7 @@ class _PhotoTagPageState extends State { } Future loadData() async { - allTagsList = await objectbox.getVisiteTagsLabels( + allTagsList = await objectbox.getVisitTagsLabels( widget.pp_id_distrib, widget.pp_langage); _selectedTags = List.from(widget.pp_currentTags); } @@ -236,7 +236,7 @@ class _PhotoTagPageState extends State { for (final tag in newTags) { // insert new tag in the database - objectbox.addVisiteTag(0, distribId, tag, langage); + objectbox.addVisitTag(0, distribId, tag, langage); } } diff --git a/lib/ui/home/visit_photo_typology.dart b/lib/ui/visit/visit_photo_typology.dart similarity index 87% rename from lib/ui/home/visit_photo_typology.dart rename to lib/ui/visit/visit_photo_typology.dart index 2e00cd0..d8291b1 100644 --- a/lib/ui/home/visit_photo_typology.dart +++ b/lib/ui/visit/visit_photo_typology.dart @@ -1,11 +1,12 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:badges/badges.dart' as badges; import 'package:mobdr/main.dart'; import 'package:mobdr/config/global_style.dart'; import 'package:mobdr/config/constant.dart'; import 'package:mobdr/db/box_photo_typology.dart'; -import 'package:mobdr/ui/home/visit_photo_typology_list.dart'; +import 'package:mobdr/ui/visit/visit_photo_typology_list.dart'; class VisitPhotoTypologyPage extends StatefulWidget { final int pp_id_distrib; @@ -88,14 +89,15 @@ class _VisitPhotoTypologyPageState extends State { ), ), Container( - padding: EdgeInsets.symmetric(horizontal: 6, vertical: 4), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - color: Colors.blue, - ), - child: Text( - photoCount.toString(), - style: TextStyle(color: Colors.white), + margin: EdgeInsets.symmetric(vertical: 0, horizontal: 10), + child: badges.Badge( + badgeStyle: badges.BadgeStyle( + badgeColor: Colors.blue, + padding: EdgeInsets.all(photoCount >= 10 ? 2 : 6), + ), + badgeContent: Text(photoCount.toString(), + style: TextStyle(color: Colors.white)), + child: Icon(Icons.camera_alt_sharp), ), ), ], diff --git a/lib/ui/home/visite_photo_typology_detail.dart b/lib/ui/visit/visit_photo_typology_detail.dart similarity index 96% rename from lib/ui/home/visite_photo_typology_detail.dart rename to lib/ui/visit/visit_photo_typology_detail.dart index 7e4ac81..29a83ec 100644 --- a/lib/ui/home/visite_photo_typology_detail.dart +++ b/lib/ui/visit/visit_photo_typology_detail.dart @@ -9,7 +9,7 @@ import 'package:mobdr/service/shared_prefs.dart'; import 'package:mobdr/config/global_style.dart'; import 'package:mobdr/main.dart'; import 'package:mobdr/ui/general/chat_us.dart'; -import 'package:mobdr/ui/home/visit_photo_tag.dart'; +import 'package:mobdr/ui/visit/visit_photo_tag.dart'; import 'package:mobdr/db/box_photo.dart'; import 'package:mobdr/db/box_photo_typology.dart'; @@ -529,7 +529,7 @@ class _VisitPhotoTypologyDetailPageState tagList = tags.isEmpty ? [] : _photo.tags.split(","); // competitor initialization - _competitorsList = objectbox.getPhotoCompetitorList(); + _competitorsList = objectbox.getEtabCompetitorList(); _competitor = ""; } catch (e) { // set errorMessage for debug diff --git a/lib/ui/home/visit_photo_typology_list.dart b/lib/ui/visit/visit_photo_typology_list.dart similarity index 94% rename from lib/ui/home/visit_photo_typology_list.dart rename to lib/ui/visit/visit_photo_typology_list.dart index fcaf407..3d4bbe2 100644 --- a/lib/ui/home/visit_photo_typology_list.dart +++ b/lib/ui/visit/visit_photo_typology_list.dart @@ -17,8 +17,9 @@ import 'package:mobdr/service/shared_prefs.dart'; import 'package:mobdr/main.dart'; import 'package:mobdr/ui/reusable/global_widget.dart'; import 'package:mobdr/ui/home/photo_camera.dart'; -import 'package:mobdr/ui/home/visite_photo_typology_detail.dart'; +import 'package:mobdr/ui/visit/visit_photo_typology_detail.dart'; import 'package:mobdr/db/box_photo.dart'; +import 'package:mobdr/events.dart'; // TODO Il faut supprimer les possibles photos du répertoire cache ! @@ -59,6 +60,8 @@ class _VisitPhotoTypologyListPageState Color _color1 = Color(0xff777777); Color _color2 = Color(0xFF515151); + late int visitPhotoCount = 0; + // _listKey is used for AnimatedList var _listKey = GlobalKey(); @@ -448,6 +451,9 @@ class _VisitPhotoTypologyListPageState void loadData() { _visitPhotoData = objectbox.getAllVisitTypologyPhotos( widget.pp_id_visite, widget.pp_id_typologie); + + // number of photos of the visit + visitPhotoCount = objectbox.getVisitPhotoCount(widget.pp_id_visite); } /// Removes the image at the specified [imageURL] from the cache of the [NetworkImage] provider. @@ -480,6 +486,14 @@ class _VisitPhotoTypologyListPageState /// insert photo(s) in widget at the beginning (0) _visitPhotoData.insertAll(0, addedPhotos); + /// if new photos are taken + if (addedPhotos.length > 0) { + visitPhotoCount += addedPhotos.length; + // a global refresh event is sent + eventBus + .fire(VisitPhotoCountEvent(widget.pp_id_visite, visitPhotoCount)); + } + /// refresh widget setState(() { _listKey = GlobalKey(); @@ -523,6 +537,11 @@ class _VisitPhotoTypologyListPageState int removeIndex = index; var removedItem = _visitPhotoData.removeAt(removeIndex); + // a global refresh event is sent + visitPhotoCount -= 1; + eventBus + .fire(VisitPhotoCountEvent(widget.pp_id_visite, visitPhotoCount)); + // delete file on database objectbox.delPhoto(removedItem.image_name); @@ -540,6 +559,7 @@ class _VisitPhotoTypologyListPageState _listKey.currentState!.removeItem(removeIndex, builder); Navigator.pop(context); + Fluttertoast.showToast( msg: 'Photo has been deleted from your visit', toastLength: Toast.LENGTH_SHORT); diff --git a/pubspec.lock b/pubspec.lock index b694e37..6f91b2a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -41,6 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.10.0" + badges: + dependency: "direct main" + description: + name: badges + sha256: "6e7f3ec561ec08f47f912cfe349d4a1707afdc8dda271e17b046aa6d42c89e77" + url: "https://pub.dev" + source: hosted + version: "3.1.1" bloc: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c107850..7d0c82f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -87,6 +87,9 @@ dependencies: # https://pub.dev/packages/event_bus_plus/ event_bus_plus: ^0.6.1 + # https://pub.dev/packages/badges + badges: ^3.1.1 + dev_dependencies: flutter_test: sdk: flutter