diff --git a/src/components/CommonUtil.vue b/src/components/CommonUtil.vue index 45b003c71924748f94c67a10a02288c0b46da47d..164afde41d4f464928f950e76346df6cc576d839 100644 --- a/src/components/CommonUtil.vue +++ b/src/components/CommonUtil.vue @@ -1,82 +1,82 @@ <template></template> <script> -import CommonUtilLocal from '@/components/CommonUtilLocal' -export default { - CONST_EVENT_LOGIN_USER_DETAIL : 'eventloginuserdetail', - - CONST_STATUS_PENDING : 1, - - CONST_STORAGE_UUID : 'store-user-uuid', - CONST_STORAGE_USER_DETAIL : 'store-user-detail', - CONST_STORE_TIMESTAMP : 'store-time-stamp', - CONST_STORAGE_CROP_CATEGORY : 'store-crop-category', - CONST_STORAGE_CROP_ID_LIST : 'store-crop-id-list', - CONST_STORAGE_CROP_LIST : 'store-crop-list', - CONST_STORAGE_PEST_LIST : 'store-pest-list', - CONST_STORAGE_CROP_PEST_LIST : 'store-crop-pest-list', - CONST_STORAGE_OBSERVATION_LIST : 'store-observation-list', - CONST_STORAGE_IMAGE_LIST : 'store-image-list', - CONST_STORAGE_POI_LIST : 'store-poi-list', - CONST_STORAGE_VISIBILITY_POLYGON : 'store-polygon', - CONST_POI_TYPES : [ - {"point_of_interest_type_id":0,"default_name":"General", "is_user_selectable": true}, - {"point_of_interest_type_id":2,"default_name":"Farm", "is_user_selectable": true}, - {"point_of_interest_type_id":3,"default_name":"Field", "is_user_selectable": true}, - {"point_of_interest_type_id":1,"default_name":"Weather station", "is_user_selectable": false} - ], - CONST_POI_TYPE_DEFAULT : 3, - CONST_IMAGE_WIDTH : 100, - CONST_IMAGE_HEIGHT : 100, - CONST_IMAGE_CANVAS_WIDTH : 100, - CONST_IMAGE_CANVAS_HEIGHT : 100, - - CONST_CROP_CATEGORY_ID : 'cropCategoryId', - CONST_ORGANISM_ID : 'organismId', - - CONST_OBSERVATION_COUNT_START_ID : -1, - CONST_POI_COUNT_START_ID : -1, - - CONST_URL_DOMAIN : CommonUtilLocal.CONST_URL_DOMAIN, // Moved to CommonUtilLocal.vue (not in Git repo) - - CONST_URL_AUTH_UUID : '/rest/auth/uuid', - CONST_URL_AUTH_LOGIN : '/rest/auth/login', - CONST_URL_LAST_TIMESTAMP : '/rest/observation/organismsystemupdated', - - CONST_URL_CROP_CATEGORY : '/rest/organism/cropcategory/', - CONST_URL_CROP_LIST : '/rest/organism/crop/list', - CONST_URL_PEST_LIST : '/rest/organism/pest/list/', - CONST_URL_CROP_PEST_LIST : '/rest/organism/crop/pest/list', - - CONST_URL_SYNC_UPDATE_OBSERVATIONS : '/rest/observation/syncobservationlistfromapp', - CONST_URL_SYNC_UPDATE_OBSERVATION : '/rest/observation/syncobservationfromapp', - - CONST_URL_SYNC_UPDATE_POI : '/rest/poi/syncpoifromapp', - - CONST_URL_USER_OBSERVATION_LIST : '/rest/observation/list/user', - CONST_URL_USER_POI : '/rest/poi/user', - CONST_URL_POLYGON_SERVICES : '/rest/observation/polygonservices/', - - CONST_URL_STATIC_IMAGE_PATH : '/static/images/observations/', - - CONST_GPS_URL_NORWAY_MAP : 'https://opencache.statkart.no/gatekeeper/gk/gk.open_wmts?Version=1.0.0&service=wmts&request=getcapabilities', - CONST_GPS_DEFAULT_LATITUDE_NORWAY : 16, - CONST_GPS_DEFAULT_LONGITUDE_NORWAY : 63, - - CONST_GPS_DEFAULT_LATITUDE_02_NORWAY : 63, - CONST_GPS_DEFAULT_LONGITUDE_02_NORWAY : 16, - - CONST_GPS_DEFAULT_ZOOM : 4.2, - CONST_GPS_OBSERVATION_ZOOM : 10, - - CONST_DB_NAME : 'db-index-observation', - CONST_DB_VERSION : 1, - CONST_DB_ENTITY_PHOTO : 'entityPhoto', - CONST_DB_INDEX_NAME_OBSERVATION_ID : 'observationId', - - CONST_FIELD_MANDATORY : '<font color=red>*</font>', - - setHeaderTitle : function(headerTitle) { - document.getElementById("appHeader").innerHTML = headerTitle; + import CommonUtilLocal from '@/components/CommonUtilLocal' + export default { + CONST_EVENT_LOGIN_USER_DETAIL: 'eventloginuserdetail', + + CONST_STATUS_PENDING: 1, + + CONST_STORAGE_UUID: 'store-user-uuid', + CONST_STORAGE_USER_DETAIL: 'store-user-detail', + CONST_STORE_TIMESTAMP: 'store-time-stamp', + CONST_STORAGE_CROP_CATEGORY: 'store-crop-category', + CONST_STORAGE_CROP_ID_LIST: 'store-crop-id-list', + CONST_STORAGE_CROP_LIST: 'store-crop-list', + CONST_STORAGE_PEST_LIST: 'store-pest-list', + CONST_STORAGE_CROP_PEST_LIST: 'store-crop-pest-list', + CONST_STORAGE_OBSERVATION_LIST: 'store-observation-list', + CONST_STORAGE_IMAGE_LIST: 'store-image-list', + CONST_STORAGE_POI_LIST: 'store-poi-list', + CONST_STORAGE_VISIBILITY_POLYGON: 'store-polygon', + CONST_POI_TYPES: [ + {"point_of_interest_type_id": 0, "default_name": "General", "is_user_selectable": true}, + {"point_of_interest_type_id": 2, "default_name": "Farm", "is_user_selectable": true}, + {"point_of_interest_type_id": 3, "default_name": "Field", "is_user_selectable": true}, + {"point_of_interest_type_id": 1, "default_name": "Weather station", "is_user_selectable": false} + ], + CONST_POI_TYPE_DEFAULT: 3, + CONST_IMAGE_WIDTH: 100, + CONST_IMAGE_HEIGHT: 100, + CONST_IMAGE_CANVAS_WIDTH: 100, + CONST_IMAGE_CANVAS_HEIGHT: 100, + + CONST_CROP_CATEGORY_ID: 'cropCategoryId', + CONST_ORGANISM_ID: 'organismId', + + CONST_OBSERVATION_COUNT_START_ID: -1, + CONST_POI_COUNT_START_ID: -1, + + CONST_URL_DOMAIN: CommonUtilLocal.CONST_URL_DOMAIN, // Moved to CommonUtilLocal.vue (not in Git repo) + + CONST_URL_AUTH_UUID: '/rest/auth/uuid', + CONST_URL_AUTH_LOGIN: '/rest/auth/login', + CONST_URL_LAST_TIMESTAMP: '/rest/observation/organismsystemupdated', + + CONST_URL_CROP_CATEGORY: '/rest/organism/cropcategory/', + CONST_URL_CROP_LIST: '/rest/organism/crop/list', + CONST_URL_PEST_LIST: '/rest/organism/pest/list/', + CONST_URL_CROP_PEST_LIST: '/rest/organism/crop/pest/list', + + CONST_URL_SYNC_UPDATE_OBSERVATIONS: '/rest/observation/syncobservationlistfromapp', + CONST_URL_SYNC_UPDATE_OBSERVATION: '/rest/observation/syncobservationfromapp', + + CONST_URL_SYNC_UPDATE_POI: '/rest/poi/syncpoifromapp', + + CONST_URL_USER_OBSERVATION_LIST: '/rest/observation/list/user', + CONST_URL_USER_POI: '/rest/poi/user', + CONST_URL_POLYGON_SERVICES: '/rest/observation/polygonservices/', + + CONST_URL_STATIC_IMAGE_PATH: '/static/images/observations/', + + CONST_GPS_URL_NORWAY_MAP: 'https://opencache.statkart.no/gatekeeper/gk/gk.open_wmts?Version=1.0.0&service=wmts&request=getcapabilities', + CONST_GPS_DEFAULT_LATITUDE_NORWAY: 16, + CONST_GPS_DEFAULT_LONGITUDE_NORWAY: 63, + + CONST_GPS_DEFAULT_LATITUDE_02_NORWAY: 63, + CONST_GPS_DEFAULT_LONGITUDE_02_NORWAY: 16, + + CONST_GPS_DEFAULT_ZOOM: 4.2, + CONST_GPS_OBSERVATION_ZOOM: 10, + + CONST_DB_NAME: 'db-index-observation', + CONST_DB_VERSION: 1, + CONST_DB_ENTITY_PHOTO: 'entityPhoto', + CONST_DB_INDEX_NAME_OBSERVATION_ID: 'observationId', + + CONST_FIELD_MANDATORY: '<font color=red>*</font>', + + setHeaderTitle: function (headerTitle) { + document.getElementById("appHeader").innerHTML = headerTitle; + } } -} </script> \ No newline at end of file diff --git a/src/components/CropCategory.vue b/src/components/CropCategory.vue index a1e47b44e77fd8492dc44e69f21bec15be030c65..d2f097c1ed3af467c856d41c327046bd88f0dad3 100644 --- a/src/components/CropCategory.vue +++ b/src/components/CropCategory.vue @@ -1,4 +1,4 @@ - <!-- +<!-- Copyright (c) 2022 NIBIO <http://www.nibio.no/>. @@ -21,84 +21,75 @@ --> <template> - <div id="divCropCategory"> - <a v-show="listSelectedIds.length > 0" class="vips-btn" @click="$router.go(-1)">{{ $t("save.label") }}</a> - <div v-show="listSelectedIds.length == 0" class="alert alert-info" role="alert"> - {{ $t("crop.category.empty.alert") }} - </div> - <ul class="list-group" style="text-align: left"> - <li class="list-group-item" v-for="item in listCropCategoris"> - <input - type="checkbox" - v-bind:value="item.cropCategoryId" - v-model="listSelectedIds" - /> - {{ getCropCategoryLocalName(item) }} - </li> - </ul> - <!-- <span>Checked names: {{ listSelectedIds }}</span> --> - </div> + <div id="divCropCategory"> + <a v-show="listSelectedIds.length > 0" class="vips-btn" @click="$router.go(-1)">{{ $t("save.label") }}</a> + <div v-show="listSelectedIds.length == 0" class="alert alert-info" role="alert"> + {{ $t("crop.category.empty.alert") }} + </div> + <ul class="list-group" style="text-align: left"> + <li class="list-group-item" v-for="item in listCropCategoris"> + <input type="checkbox" v-bind:value="item.cropCategoryId" v-model="listSelectedIds" /> + {{ getCropCategoryLocalName(item) }} + </li> + </ul> + <!-- <span>Checked names: {{ listSelectedIds }}</span> --> + </div> </template> <script> -import CommonUtil from '@/components/CommonUtil' -export default { - name: "cropCategory", - data() { - return { - isCropIdsNotAvailable : true, - listSelectedIds: [], - listCropCategoris: [], - }; - }, - watch: { - listSelectedIds: function (newVal,val) { - localStorage.setItem(CommonUtil.CONST_STORAGE_CROP_ID_LIST, newVal); - }, - }, - methods: { - getCropCategoryLocalName: function(cropCategory) { - if(cropCategory.cropCategoryLocalSet != undefined) - { - for(var i=0;i<cropCategory.cropCategoryLocalSet.length;i++) - { - if(cropCategory.cropCategoryLocalSet[i].cropCategoryLocalPK.locale == this.$i18n.locale) - { - return cropCategory.cropCategoryLocalSet[i].localName; + import CommonUtil from '@/components/CommonUtil' + export default { + name: "cropCategory", + data() { + return { + isCropIdsNotAvailable: true, + listSelectedIds: [], + listCropCategoris: [], + }; + }, + watch: { + listSelectedIds: function (newVal, val) { + localStorage.setItem(CommonUtil.CONST_STORAGE_CROP_ID_LIST, newVal); + }, + }, + methods: { + getCropCategoryLocalName: function (cropCategory) { + if (cropCategory.cropCategoryLocalSet != undefined) { + for (var i = 0; i < cropCategory.cropCategoryLocalSet.length; i++) { + if (cropCategory.cropCategoryLocalSet[i].cropCategoryLocalPK.locale == this.$i18n.locale) { + return cropCategory.cropCategoryLocalSet[i].localName; + } + } + } + return cropCategory.defaultName; + }, + }, + mounted() { + CommonUtil.setHeaderTitle(this.$i18n.t("cropcategory")); + if (localStorage.getItem(CommonUtil.CONST_STORAGE_CROP_CATEGORY)) { + this.listCropCategoris = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_CROP_CATEGORY)); + } + + if ( + null != localStorage.getItem(CommonUtil.CONST_STORAGE_CROP_ID_LIST) && + localStorage.getItem(CommonUtil.CONST_STORAGE_CROP_ID_LIST) != "" && + typeof localStorage.getItem(CommonUtil.CONST_STORAGE_CROP_ID_LIST) != "undefined" + ) { + this.listSelectedIds = localStorage.getItem(CommonUtil.CONST_STORAGE_CROP_ID_LIST).split(","); + if ((this.listSelectedIds).length > 0) { + this.isCropIdsNotAvailable = false; } } - } - return cropCategory.defaultName; - }, - }, - mounted() { - CommonUtil.setHeaderTitle(this.$i18n.t("cropcategory")); - if(localStorage.getItem(CommonUtil.CONST_STORAGE_CROP_CATEGORY) ) - { - this.listCropCategoris = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_CROP_CATEGORY)); - } - if ( - null != localStorage.getItem(CommonUtil.CONST_STORAGE_CROP_ID_LIST) && - localStorage.getItem(CommonUtil.CONST_STORAGE_CROP_ID_LIST) != "" && - typeof localStorage.getItem(CommonUtil.CONST_STORAGE_CROP_ID_LIST) != "undefined" - ) { - this.listSelectedIds = localStorage.getItem(CommonUtil.CONST_STORAGE_CROP_ID_LIST).split(","); - if((this.listSelectedIds).length > 0) - { - this.isCropIdsNotAvailable = false; - } - } - - }, -}; + }, + }; </script> -<style scoped> -.custom-checkbox .custom-control-input:checked~.custom-control-label::before{ - background-color:green; -} +<style scoped> + .custom-checkbox .custom-control-input:checked~.custom-control-label::before { + background-color: green; + } </style> \ No newline at end of file diff --git a/src/components/LoginSystem.vue b/src/components/LoginSystem.vue index 25c29331d24336cb56eaf353491c0dc44f891c58..041e22d37d7698d70b3995bc06f74de6e9b6fd0c 100644 --- a/src/components/LoginSystem.vue +++ b/src/components/LoginSystem.vue @@ -1,4 +1,4 @@ - <!-- +<!-- Copyright (c) 2022 NIBIO <http://www.nibio.no/>. @@ -22,265 +22,244 @@ --> <template> - <div> - <div v-if="$root.sharedState.uuid" style="margin-top: 15px;"> - <font-awesome-icon style="font-size: large; color: #3d8052; padding-left: 5px;" icon="fa-user" /> <span v-text="userLoggedInName"></span> - <br /> - <a class="vips-btn" v-on:click="handleLogout()">{{ $t("logout.button.label")}}</a> - </div> - <div v-else> - <form class="my-2 my-lg-0"> - <div class="form-group"> - <input - class="form-control mr-sm-2" - type="text" - :placeholder="$t('login.username.field.placeholder')" - :aria-label="$t('login.username.field.placeholder')" - v-model="username" - v-on:keyup.enter="handleLogin()" - /> - </div> - <div class="form-group"> - <input - class="form-control mr-sm-2" - type="password" - :placeholder="$t('login.pwd.field.placeholder')" - :aria-label="$t('login.pwd.field.placeholder')" - v-model="password" - v-on:keyup.enter="handleLogin()" - /> - </div> + <div> + <div v-if="$root.sharedState.uuid" style="margin-top: 15px;"> + <font-awesome-icon style="font-size: large; color: #3d8052; padding-left: 5px;" icon="fa-user" /> <span + v-text="userLoggedInName"></span> + <br /> + <a class="vips-btn" v-on:click="handleLogout()">{{ $t("logout.button.label")}}</a> + </div> + <div v-else> + <form class="my-2 my-lg-0"> + <div class="form-group"> + <input class="form-control mr-sm-2" type="text" + :placeholder="$t('login.username.field.placeholder')" + :aria-label="$t('login.username.field.placeholder')" v-model="username" + v-on:keyup.enter="handleLogin()" /> + </div> + <div class="form-group"> + <input class="form-control mr-sm-2" type="password" :placeholder="$t('login.pwd.field.placeholder')" + :aria-label="$t('login.pwd.field.placeholder')" v-model="password" + v-on:keyup.enter="handleLogin()" /> + </div> - <button class="btn btn-primary" type="button" v-on:click="handleLogin()" v-on:keyup.enter="handleLogin()"> - {{ $t("login.button.label")}} - </button> - <div v-show="errMsg" class="alert alert-warning alert-dismissible fade show"> - {{errMsg}} - <button type="button" class="close" data-dismiss="alert" aria-label="Close"> - <span aria-hidden="true">×</span> - </button> - </div> - <div v-show="isLogginFail" class="text-danger"> - {{ $t("login.systems.wrong.credential") }} - </div> - </form> + <button class="btn btn-primary" type="button" v-on:click="handleLogin()" + v-on:keyup.enter="handleLogin()"> + {{ $t("login.button.label")}} + </button> + <div v-show="errMsg" class="alert alert-warning alert-dismissible fade show"> + {{errMsg}} + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> + <span aria-hidden="true">×</span> + </button> + </div> + <div v-show="isLogginFail" class="text-danger"> + {{ $t("login.systems.wrong.credential") }} + </div> + </form> - </div> + </div> + + <!-- <Sync :isSyncNeeded="isSyncNeeded"/> --> + <Sync ref="Sync" /> + <common-util ref="CommonUtil" /> + </div> - <!-- <Sync :isSyncNeeded="isSyncNeeded"/> --> - <Sync ref="Sync"/> - <common-util ref="CommonUtil"/> - </div> - </template> <script> -import CommonUtil from "@/components/CommonUtil"; -import Sync from '@/components/Sync'; + import CommonUtil from "@/components/CommonUtil"; + import Sync from '@/components/Sync'; + + + export default { + name: "LoginSystem", + components: {Sync, CommonUtil}, + props: { + isWelcome: Boolean, + }, + data() { + return { + CONST_URL_DOMAIN: '', + jsonServerResponse: '', + userLoggedInName: this.$root.sharedState.user.firstName + " " + this.$root.sharedState.user.lastName, + username: "", + password: "", + appUser: {}, + isSyncNeeded: false, + errMsg: '', + isLogginFail: false, + }; + }, + //emits: {}, + methods: { + /** Get user details from local storage */ + getUserFromStorage() { -export default { - name: "LoginSystem", - components : {Sync,CommonUtil}, - props:{ - isWelcome : Boolean, - - }, - data() { - return { - CONST_URL_DOMAIN : '', - jsonServerResponse : '', - userLoggedInName: this.$root.sharedState.user.firstName + " " + this.$root.sharedState.user.lastName, - username: "", - password: "", - appUser:{}, - isSyncNeeded:false, - errMsg : '', - isLogginFail : false, - }; - }, - //emits: {}, - methods: { - /** Get user details from local storage */ - getUserFromStorage() { - - let strUser = localStorage.getItem(CommonUtil.CONST_STORAGE_USER_DETAIL); - if(strUser && strUser != 'undefined') - { - let user = JSON.parse(strUser); - this.appUser = user; //This user will require in Sync process - this.$root.sharedState.userId = user.userId; - this.$root.sharedState.uuid = user.userUuid; - this.$root.sharedState.user.firstName = user.firstName; - this.$root.sharedState.user.lastName = user.lastName; - this.userLoggedInName = this.$root.sharedState.user.firstName + " " + this.$root.sharedState.user.lastName; - } - /** Firing event to parent (main.js) */ - //this.$emit(CommonUtil.CONST_EVENT_LOGIN_USER_DETAIL,user.userUuid, user.firstName,user.lastName); - }, + let strUser = localStorage.getItem(CommonUtil.CONST_STORAGE_USER_DETAIL); + if (strUser && strUser != 'undefined') { + let user = JSON.parse(strUser); + this.appUser = user; //This user will require in Sync process + this.$root.sharedState.userId = user.userId; + this.$root.sharedState.uuid = user.userUuid; + this.$root.sharedState.user.firstName = user.firstName; + this.$root.sharedState.user.lastName = user.lastName; + this.userLoggedInName = this.$root.sharedState.user.firstName + " " + this.$root.sharedState.user.lastName; + } + /** Firing event to parent (main.js) */ + //this.$emit(CommonUtil.CONST_EVENT_LOGIN_USER_DETAIL,user.userUuid, user.firstName,user.lastName); + }, - /** Check uuid first */ - checkValidUUID() { - if(localStorage.getItem(CommonUtil.CONST_STORAGE_UUID)) - { - let userUUID = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID); - /** Fetch to get details */ - let jsonHeader = { Authorization: userUUID }; + /** Check uuid first */ + checkValidUUID() { + if (localStorage.getItem(CommonUtil.CONST_STORAGE_UUID)) { + let userUUID = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID); + /** Fetch to get details */ + let jsonHeader = {Authorization: userUUID}; - fetch(this.CONST_URL_DOMAIN + CommonUtil.CONST_URL_AUTH_UUID, { - method: "GET", - headers: jsonHeader, - }).then((response) => { - - if (response.status != 200 && response.status != 201) { - this.$root.sharedState.uuid = ''; - } else { - - this.getUserFromStorage(); - /** Sync Operation for valid timestamp */ - this.$refs.Sync.syncOneWayDifferentTimeStamp(this.appUser); - } - }); - } - }, + fetch(this.CONST_URL_DOMAIN + CommonUtil.CONST_URL_AUTH_UUID, { + method: "GET", + headers: jsonHeader, + }).then((response) => { - /** Login process . sending user credential */ - handleLogin(){ - let This = this; - - this.$root.sharedState.uuid = ''; - localStorage.setItem(CommonUtil.CONST_STORAGE_UUID,""); - let jsonBody = JSON.stringify({"username": this.username, "password":this.password}); - /** Fetch to get UUID */ - fetch( - this.CONST_URL_DOMAIN + CommonUtil.CONST_URL_AUTH_LOGIN, - { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body : jsonBody - } - ) - .then(function(response){ - if(response.status === 200 || response.status === 201) - { - This.isLogginFail = false; - return response.json(); - } - else - { - This.isLogginFail = true; - return false; - } - }) - .then((data) => { - let jsonServerResponse = ''; - this.jsonServerResponse = data; - if(this.jsonServerResponse.success) - { - this.username=''; - this.password=''; - localStorage.setItem(CommonUtil.CONST_STORAGE_UUID,data.UUID); - } - - /** Fetch to get details */ - let uuid = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID); - if(uuid != null && uuid != undefined && uuid.trim() != "") - { - let jsonHeader = {Authorization:uuid}; - fetch( - this.CONST_URL_DOMAIN + CommonUtil.CONST_URL_AUTH_UUID, - { - crossDomain:true, - method:"GET", - headers: { - "Content-Type": "application/json", - 'Authorization' : uuid - } - } - ) - .then(response => response.json()) - .then((data) => { - let loggedUser = data; - if(loggedUser || loggedUser != 'undefined') - { - localStorage.setItem(CommonUtil.CONST_STORAGE_USER_DETAIL,JSON.stringify(loggedUser)); - this.getUserFromStorage(); - $('.offcanvas-collapse').removeClass('open'); - this.$refs.Sync.isSyncOnewayNeeded(loggedUser); - this.$refs.Sync.syncTwoWayAtLogin(); - localStorage.setItem(CommonUtil.CONST_STORAGE_UUID,uuid); - if(this.$router.currentRoute.path != "/") + if (response.status != 200 && response.status != 201) { + this.$root.sharedState.uuid = ''; + } else { + + this.getUserFromStorage(); + /** Sync Operation for valid timestamp */ + this.$refs.Sync.syncOneWayDifferentTimeStamp(this.appUser); + } + }); + } + }, + + /** Login process . sending user credential */ + handleLogin() { + let This = this; + + this.$root.sharedState.uuid = ''; + localStorage.setItem(CommonUtil.CONST_STORAGE_UUID, ""); + let jsonBody = JSON.stringify({"username": this.username, "password": this.password}); + /** Fetch to get UUID */ + fetch( + this.CONST_URL_DOMAIN + CommonUtil.CONST_URL_AUTH_LOGIN, { - this.$router.push('/') + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: jsonBody } - } - } - ); // END inner fetch - } - }); // END outer fetch - }, - handleLogout() - { - this.$root.sharedState.uuid = ''; // remove global uuid for other (e.g. menu items etc) - /** Remove stored data from app in local system - server data still preserve */ - this.removeStoredData(); + ) + .then(function (response) { + if (response.status === 200 || response.status === 201) { + This.isLogginFail = false; + return response.json(); + } + else { + This.isLogginFail = true; + return false; + } + }) + .then((data) => { + let jsonServerResponse = ''; + this.jsonServerResponse = data; + if (this.jsonServerResponse.success) { + this.username = ''; + this.password = ''; + localStorage.setItem(CommonUtil.CONST_STORAGE_UUID, data.UUID); + } + + /** Fetch to get details */ + let uuid = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID); + if (uuid != null && uuid != undefined && uuid.trim() != "") { + let jsonHeader = {Authorization: uuid}; + fetch( + this.CONST_URL_DOMAIN + CommonUtil.CONST_URL_AUTH_UUID, + { + crossDomain: true, + method: "GET", + headers: { + "Content-Type": "application/json", + 'Authorization': uuid + } + } + ) + .then(response => response.json()) + .then((data) => { + let loggedUser = data; + if (loggedUser || loggedUser != 'undefined') { + localStorage.setItem(CommonUtil.CONST_STORAGE_USER_DETAIL, JSON.stringify(loggedUser)); + this.getUserFromStorage(); + $('.offcanvas-collapse').removeClass('open'); + this.$refs.Sync.isSyncOnewayNeeded(loggedUser); + this.$refs.Sync.syncTwoWayAtLogin(); + localStorage.setItem(CommonUtil.CONST_STORAGE_UUID, uuid); + if (this.$router.currentRoute.path != "/") { + this.$router.push('/') + } + } + } + ); // END inner fetch + } + }); // END outer fetch + }, + handleLogout() { + this.$root.sharedState.uuid = ''; // remove global uuid for other (e.g. menu items etc) + /** Remove stored data from app in local system - server data still preserve */ + this.removeStoredData(); - this.$emit(CommonUtil.CONST_EVENT_LOGIN_USER_DETAIL,''); - $('.offcanvas-collapse').removeClass('open'); - this.$router.push('/logout'); - }, - /** Remove stored data on logout */ - removeStoredData() - { - /* Remove localstorage */ - localStorage.removeItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST); - localStorage.removeItem(CommonUtil.CONST_STORAGE_UUID); - localStorage.removeItem(CommonUtil.CONST_STORAGE_USER_DETAIL); - localStorage.removeItem(CommonUtil.CONST_STORE_TIMESTAMP); - localStorage.removeItem(CommonUtil.CONST_STORAGE_CROP_CATEGORY); - localStorage.removeItem(CommonUtil.CONST_STORAGE_CROP_ID_LIST); - localStorage.removeItem(CommonUtil.CONST_STORAGE_CROP_LIST); - localStorage.removeItem(CommonUtil.CONST_STORAGE_PEST_LIST); - localStorage.removeItem(CommonUtil.CONST_STORAGE_CROP_PEST_LIST); - localStorage.removeItem(CommonUtil.CONST_STORAGE_IMAGE_LIST); - localStorage.removeItem(CommonUtil.CONST_STORAGE_POI_LIST); - localStorage.removeItem(CommonUtil.CONST_STORAGE_VISIBILITY_POLYGON); + this.$emit(CommonUtil.CONST_EVENT_LOGIN_USER_DETAIL, ''); + $('.offcanvas-collapse').removeClass('open'); + this.$router.push('/logout'); + }, + /** Remove stored data on logout */ + removeStoredData() { + /* Remove localstorage */ + localStorage.removeItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST); + localStorage.removeItem(CommonUtil.CONST_STORAGE_UUID); + localStorage.removeItem(CommonUtil.CONST_STORAGE_USER_DETAIL); + localStorage.removeItem(CommonUtil.CONST_STORE_TIMESTAMP); + localStorage.removeItem(CommonUtil.CONST_STORAGE_CROP_CATEGORY); + localStorage.removeItem(CommonUtil.CONST_STORAGE_CROP_ID_LIST); + localStorage.removeItem(CommonUtil.CONST_STORAGE_CROP_LIST); + localStorage.removeItem(CommonUtil.CONST_STORAGE_PEST_LIST); + localStorage.removeItem(CommonUtil.CONST_STORAGE_CROP_PEST_LIST); + localStorage.removeItem(CommonUtil.CONST_STORAGE_IMAGE_LIST); + localStorage.removeItem(CommonUtil.CONST_STORAGE_POI_LIST); + localStorage.removeItem(CommonUtil.CONST_STORAGE_VISIBILITY_POLYGON); - /* Remove from IndexedDB */ - let dbRequest = null; - dbRequest = indexedDB.open(CommonUtil.CONST_DB_NAME, CommonUtil.CONST_DB_VERSION); - dbRequest.onsuccess = function(evt) { - let db = evt.target.result; - db.close(); - } + /* Remove from IndexedDB */ + let dbRequest = null; + dbRequest = indexedDB.open(CommonUtil.CONST_DB_NAME, CommonUtil.CONST_DB_VERSION); + dbRequest.onsuccess = function (evt) { + let db = evt.target.result; + db.close(); + } - let delReq = indexedDB.deleteDatabase(CommonUtil.CONST_DB_NAME); - delReq.onerror = function() - { - console.info('could not delete database'); - } - delReq.onblocked = function() - { - console.info('delete DB not successful because of operation block'); - } + let delReq = indexedDB.deleteDatabase(CommonUtil.CONST_DB_NAME); + delReq.onerror = function () { + console.info('could not delete database'); + } + delReq.onblocked = function () { + console.info('delete DB not successful because of operation block'); + } - } + } - }, - mounted() { - this.CONST_URL_DOMAIN = CommonUtil.CONST_URL_DOMAIN; - if(navigator.onLine) - { - this.checkValidUUID(); - } - else - { - this.getUserFromStorage(); - } - }, -}; + }, + mounted() { + this.CONST_URL_DOMAIN = CommonUtil.CONST_URL_DOMAIN; + if (navigator.onLine) { + this.checkValidUUID(); + } + else { + this.getUserFromStorage(); + } + }, + }; </script> <style scoped> diff --git a/src/components/Logout.vue b/src/components/Logout.vue index ed879b809972af841f0618e7d9bb15f0d2b27e40..6aa2fe2cc36d23affa0057259ba3bea1f1250d7c 100644 --- a/src/components/Logout.vue +++ b/src/components/Logout.vue @@ -1,4 +1,4 @@ - <!-- +<!-- Copyright (c) 2022 NIBIO <http://www.nibio.no/>. @@ -22,10 +22,10 @@ --> <template> - <div> - <h1>{{$t("logout.header.text")}}</h1> - <div class="alert alert-warning" role="alert"> - {{$t("logout.success.text")}} - </div> - </div> + <div> + <h1>{{$t("logout.header.text")}}</h1> + <div class="alert alert-warning" role="alert"> + {{$t("logout.success.text")}} + </div> + </div> </template> \ No newline at end of file diff --git a/src/components/MapObservation.vue b/src/components/MapObservation.vue index 7c9b00de6d552f2523893acf1ca80bebd524904c..73e7cf77d0305993ff5ce945dbe74faf7be485f2 100644 --- a/src/components/MapObservation.vue +++ b/src/components/MapObservation.vue @@ -1,4 +1,4 @@ - <!-- +<!-- Copyright (c) 2022 NIBIO <http://www.nibio.no/>. @@ -22,574 +22,559 @@ --> <template> - <div> - <div id='map-observation'> - <div v-if="isMyMapPanelVisible"> - <div v-if="isMapClicked"> - <div id="btnBack"> - <router-link id='btnBack_1' :to="{name: 'Observation', params:{observationId:myObservationId, paramGeoinfo:initGeoInfo, paramObservation:mapObservation }}" class="vips-btn"> - {{ $t("map.link.cancel.label") }} <i class="fas fa-times"></i> - </router-link> - - <router-link id='btnBack_2' :to="{name: 'Observation', params:{observationId:myObservationId, paramGeoinfo:myGeoInfo, paramObservation:mapObservation}}" class="vips-btn"> - {{ $t("map.link.OK.label") }} <i class="fas fa-check"></i> - </router-link> - </div> - </div> - <div v-else> - <router-link id='btnBack' :to="{name: 'Observation', params:{observationId:myObservationId, paramGeoinfo:myGeoInfo, paramObservation:mapObservation}}" class="vips-btn"> - {{ $t("map.link.back.label") }} - </router-link> - </div> - </div> - <div v-if="isMyMapPanelVisible" id='map-mylocation' style="margin: 5px;"> - <a v-on:click="myposition" style="cursor:pointer; "><font-awesome-icon style="font-size: x-large; padding: 5px; color: white; background-color:#3d8052;" icon="location-crosshairs" /></a> - </div> - </div> - - - - <div id="ObservationMapPanel" v-if="isMyMapPanelVisible" ref='ObservationMapPanel'> - <div> - - <select class="form-control" v-model="poi.pointOfInterestId" v-on:change="selectPOI($event)" ref="dropDownPOI"> - <option v-for="poi in lstPOI" v-bind:value="poi.pointOfInterestId" >{{poi.name}}</option> - </select> - <div id="poiMarker"> - <img src="@/assets/map_icon.png"> - </div> - - </div> - </div> - <div v-else > - <div id='divPrivacy' ref='divPrivacy' class="form-group"> - <visibility :locationIsPrivate="locationIsPrivate" :polygonService="polygonService" v-on:visibilityMapAction="visibilityMapAction"/> - </div> - </div> - <common-util ref="CommonUtil"/> - </div> + <div> + <div id='map-observation'> + <div v-if="isMyMapPanelVisible"> + <div v-if="isMapClicked"> + <div id="btnBack"> + <router-link id='btnBack_1' + :to="{name: 'Observation', params:{observationId:myObservationId, paramGeoinfo:initGeoInfo, paramObservation:mapObservation }}" + class="vips-btn"> + {{ $t("map.link.cancel.label") }} <i class="fas fa-times"></i> + </router-link> + + <router-link id='btnBack_2' + :to="{name: 'Observation', params:{observationId:myObservationId, paramGeoinfo:myGeoInfo, paramObservation:mapObservation}}" + class="vips-btn"> + {{ $t("map.link.OK.label") }} <i class="fas fa-check"></i> + </router-link> + </div> + </div> + <div v-else> + <router-link id='btnBack' + :to="{name: 'Observation', params:{observationId:myObservationId, paramGeoinfo:myGeoInfo, paramObservation:mapObservation}}" + class="vips-btn"> + {{ $t("map.link.back.label") }} + </router-link> + </div> + </div> + <div v-if="isMyMapPanelVisible" id='map-mylocation' style="margin: 5px;"> + <a v-on:click="myposition" style="cursor:pointer; "> + <font-awesome-icon style="font-size: x-large; padding: 5px; color: white; background-color:#3d8052;" + icon="location-crosshairs" /></a> + </div> + </div> + + + + <div id="ObservationMapPanel" v-if="isMyMapPanelVisible" ref='ObservationMapPanel'> + <div> + + <select class="form-control" v-model="poi.pointOfInterestId" v-on:change="selectPOI($event)" + ref="dropDownPOI"> + <option v-for="poi in lstPOI" v-bind:value="poi.pointOfInterestId">{{poi.name}}</option> + </select> + <div id="poiMarker"> + <img src="@/assets/map_icon.png"> + </div> + + </div> + </div> + <div v-else> + <div id='divPrivacy' ref='divPrivacy' class="form-group"> + <visibility :locationIsPrivate="locationIsPrivate" :polygonService="polygonService" + v-on:visibilityMapAction="visibilityMapAction" /> + </div> + </div> + <common-util ref="CommonUtil" /> + </div> </template> <script> -import CommonUtil from '@/components/CommonUtil' -import 'ol/ol.css'; -import Map from 'ol/Map'; -import OSM from 'ol/source/OSM'; -import TileLayer from 'ol/layer/Tile'; -import View from 'ol/View'; -import WMTS, {optionsFromCapabilities} from 'ol/source/WMTS'; -import WMTSCapabilities from 'ol/format/WMTSCapabilities'; - -import {Vector as VectorSource} from 'ol/source'; -import {Vector as VectorLayer} from 'ol/layer'; -import Collection from 'ol/Collection'; -import {fromLonLat} from 'ol/proj'; - -import Circle from 'ol/geom/Circle'; -import {Circle as CircleStyle, Fill, Stroke, Style, Icon} from 'ol/style'; -import Feature from 'ol/Feature'; -import GeoJSON from 'ol/format/GeoJSON'; - -import {toStringXY} from 'ol/coordinate'; -import {transform} from 'ol/proj'; -import Point from 'ol/geom/Point'; -import {Modify} from 'ol/interaction'; -import Draw from 'ol/interaction/Draw'; -import Overlay from 'ol/Overlay'; -import Geolocation from 'ol/Geolocation'; -import Visibility from '@/components/Visibility' - -import i18n from '@/App' - - - -let parser = new WMTSCapabilities(); - - -export default { - name : 'MapObservation', - props : ['observationId','geoinfo','isMapPanelVisible','locationPointOfInterestId','locationIsPrivate','polygonService','observation'], - components : {CommonUtil,Visibility}, - data(){ - return { - CONST_URL_DOMAIN : '', - isMyMapPanelVisible : '', - myGeoInfo : '', - initGeoInfo : '', - latitude : 0, - longitude : 0, - mapZoom : 0, - mapInteractions : '', - lstPOI : [], - poi : { pointOfInterestId:'undefined', name: this.$i18n.t("mapobservation.label.selectPOI") }, - myMap : '', - myObservationId : '', - mapObservation : {}, - isMapClicked : false, - } - }, - methods : { - visibilityMapAction(paramPrivate, paramPolygonService){ - this.$emit('visibilityObservationAction', paramPrivate, paramPolygonService); - }, - /** My current location */ - myposition() - { - let options = { enableHighAccuracy: true }; - navigator.geolocation.getCurrentPosition(this.geolocationSuccess, this.geolocationError, options); - - }, - /** - * Get GeoInfo - * */ - geolocationSuccess(pos) { - this.poi.pointOfInterestId='undefined'; - this.longitude= pos.coords.latitude; - this.latitude = pos.coords.longitude; - let coord = [pos.coords.longitude,pos.coords.latitude]; - let transFormCord = transform(coord, 'EPSG:3857','EPSG:4326'); - this.myMap.getView().setCenter(fromLonLat(coord)); - - this.clearMapLayers(); - - let myImage = this.myImage(); - - let vectorSource = new VectorSource({}); - var iconFeature = new Feature({ - geometry: new Point(fromLonLat(coord)) - }); - - vectorSource.addFeature(iconFeature); - var vectorLayer = new VectorLayer({ - source: vectorSource, - style: new Style({ - image: myImage, - }), - }); - - - - let geoJSON = new GeoJSON(); - let resultGeoJSON = geoJSON.writeFeatures(vectorLayer.getSource().getFeatures()); - this.myGeoInfo = JSON.parse(resultGeoJSON); - this.myGeoInfo.features[0].geometry.coordinates=coord; - - this.myMap.addLayer(vectorLayer); - - this.myMap.getView().setZoom(CommonUtil.CONST_GPS_OBSERVATION_ZOOM); - - }, - geolocationError(error){ - console.log('geolocation error : '+geolocationError); - }, - - /** Display of Map */ - initMap(myLatitude,myLongitude){ - - let pointOfInterestId = this.poi.pointOfInterestId; - let This = this; - let urlMap = CommonUtil.CONST_GPS_URL_NORWAY_MAP; // Norway Map - - let myGeoInfo = this.myGeoInfo; - let latitude = myLatitude; - let longitude = myLongitude; - let mapZoom = this.mapZoom; - - let image = this.myImage(); - - - let vectorSource = this.myVectorGeoSource(); - - let vectorGeoLayer = this.myVectorGeoLayer(vectorSource,image); - - let mapInteractions = this.myInteractions(this.mapInteractions); - - let mapView = this.myView(latitude,longitude,mapZoom); - - - let localIsMyMapPanelVisible = this.isMyMapPanelVisible; - let coordinate = [latitude,longitude]; - - let pointMarker = this.myOverLay (coordinate); - let pointMarkerCoord = this.myOverLayCoord(latitude,longitude); - - /* Fetch Norway Map */ - - fetch(urlMap) - .then(function (response){ - return response.text(); - }) - .then(function(text){ - let parser = new WMTSCapabilities(); - let result = parser.read(text); - - let options = optionsFromCapabilities(result, { - layer : 'topo4', - matrixSet: 'EPSG:3857', - }); - - - This.myMap = new Map({ - layers: [ - - new TileLayer({ - opacity : 1, - source : new WMTS(options), - }), - vectorGeoLayer - - ], - controls: [], - interactions : mapInteractions, - - target: 'map-observation', - view : mapView, - overlays: [pointMarkerCoord], - renderer: 'canvas', - }); - - - - /* On click event of map to get the location */ - This.myMap.on(['singleclick'],function(event){ - if(localIsMyMapPanelVisible) - { - This.isMapClicked = true; - - This.poi.pointOfInterestId='undefined'; - - let transFormCord = transform(event.coordinate,'EPSG:3857','EPSG:4326'); - This.latitude=transFormCord[0]; - This.longitude=transFormCord[1]; - let mapNewCord = toStringXY(transform(event.coordinate,'EPSG:3857','EPSG:4326'),4); - - - This.myMap.getView().setCenter(fromLonLat(transFormCord)); - - /******* Below code for vector marker positioning */ - - var iconFeature = new Feature({ - geometry: new Point(fromLonLat(transFormCord)) - }); - This.clearMapLayers(); - if(vectorSource){} - else{ - vectorSource = new VectorSource({}); - } - - if(vectorSource) - { - vectorSource.addFeature(iconFeature); - - var vectorLayer = new VectorLayer({ - source: vectorSource, - style: new Style({ - image: image, - }), - }); - - This.myMap.addLayer(vectorLayer); - - let geoGSON = new GeoJSON(); - let resultGeoGSON = geoGSON.writeFeatures(vectorLayer.getSource().getFeatures(), { - dataProjection: 'EPSG:4326', - featureProjection: 'EPSG:3857' - }); - This.myGeoInfo = JSON.parse(resultGeoGSON); - - } - } - - }); - - }) - - - }, - - - myVectorGeoSource(){ - if(this.myGeoInfo) - { - return new VectorSource({ - features : new GeoJSON({dataProjection:"EPSG:4326", - featureProjection:"EPSG:3857"}).readFeatures(this.myGeoInfo), - }) - } - }, - - myVectorGeoLayer(vectorSource,image){ - return new VectorLayer({ - source : vectorSource, - style : new Style({ - image: image, - }), - }) - }, - myView(latitude,longitude,mapZoom){ - return new View ({ - center:fromLonLat([latitude, longitude]), - zoom : mapZoom - }) - }, - - myImage() - { - var fill = new Fill({ - color: 'white' - }); - - return new CircleStyle({ - radius: 15, - fill: fill, - stroke: new Stroke({color: '#3d8052', width: 15}), - }); - }, - - - myOverLay(coordinates){ - return new Overlay({ - position : fromLonLat(coordinates) , - positioning: 'bottom-center', - element: document.getElementById('poiMarker'), - stopEvent: false - }); - }, - - - myOverLayCoord(latitude,longitude) - { - let coordinate = [latitude,longitude]; - return this.myOverLay(coordinate); - }, - - - myStyleFunction(styles,feature) - { - return styles[feature.getGeometry().getType()]; - }, - - myInteractions(mapInteractions) - { - return (mapInteractions) ? [] : ''; - }, - - /* Get point of interest */ - getMyPointOfInterst(lstPOI) - { - let userUUID = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID); - let jsonHeader = { Authorization: userUUID }; - - lstPOI.push({pointOfInterestId:'undefined',name: this.$i18n.t("mapobservation.label.noPOIselected")}); - - fetch(this.CONST_URL_DOMAIN +CommonUtil.CONST_URL_USER_POI, - { - method: "GET", - headers: jsonHeader, - }).then((response) => { - if(response.status == 200) - { - return response.json() - } - }) - .then((jsonLstPOI) => { - $.each(jsonLstPOI, function(index, poi){ - let myPOI = poi; - lstPOI.push(myPOI); - }) - - }) - }, - - /* Clear existing sources in map layers */ - clearMapLayers() - { - let myLayers = this.myMap.getLayers(); - if(myLayers) - { - this.myMap.getLayers().forEach(function (layer){ - let source = layer.get('source'); - if(source) - { - source.clear(); - } - - }) - } - }, - - selectPOI(event) - { - let myPOI = this.poi; - if(event.target.value != 0) - { - $.each(this.lstPOI, function(index, poi){ - if ( poi.pointOfInterestId === JSON.parse(event.target.value)) - { - myPOI=poi; - return false; - } - }) - - this.myGeoInfo = JSON.parse(myPOI.geoJSON); - let coordinate = this.myGeoInfo.features[0].geometry.coordinates; - this.latitude = coordinate[0]; - this.longitude = coordinate[1]; - - - - let myImage = this.myImage(); - let transFormCord = [this.latitude,this.longitude]; - //let vectorSource = this.myVectorGeoSource(); - let vectorSource = new VectorSource({}); - - var iconFeature = new Feature({ - geometry: new Point(fromLonLat(transFormCord)) - }); - - vectorSource.addFeature(iconFeature); - - var vectorLayer = new VectorLayer({ - source: vectorSource, - style: new Style({ - image: myImage, - }), - }); - - this.clearMapLayers(); - this.myMap.addLayer(vectorLayer); - - this.myMap.getView().setCenter(fromLonLat(coordinate)); - this.myMap.getView().setZoom(CommonUtil.CONST_GPS_OBSERVATION_ZOOM); - } - - - - } - - }, - mounted() { - - this.CONST_URL_DOMAIN = CommonUtil.CONST_URL_DOMAIN; - this.latitude = CommonUtil.CONST_GPS_DEFAULT_LATITUDE_NORWAY; - this.longitude = CommonUtil.CONST_GPS_DEFAULT_LONGITUDE_NORWAY; - - let routeParam=this.$route.params; - - // This ensures that the map fills the entire viewport - var mapDiv = document.getElementById("map-observation"); - var navDiv = document.getElementById("vipsobsappmenu"); - var appDiv = document.getElementById("app"); - var panelObDiv = document.getElementById("ObservationMapPanel"); - var divPrivacy = document.getElementById("divPrivacy"); - - if(routeParam.observation) - { - this.mapObservation = routeParam.observation; - } - - if(routeParam.observationId) - { - this.myObservationId = routeParam.observationId; - } - if(this.observationId) - { - this.myObservationId = this.observationId; - } - - if(routeParam.isMapPanelVisible) - { - this.isMyMapPanelVisible = routeParam.isMapPanelVisible; - - } - if(this.isMapPanelVisible) - { - this.isMyMapPanelVisible = this.isMapPanelVisible; - } - - if(this.isMyMapPanelVisible) - { - mapDiv.style.height = (screen.height - navDiv.offsetHeight) + "px"; - this.mapInteractions=''; - - appDiv.style.marginTop="0"; - appDiv.style.paddingRight="0"; - appDiv.style.paddingLeft="0"; - - this.getMyPointOfInterst(this.lstPOI); - } - else - { - mapDiv.style.height = 300 +"px"; - this.mapInteractions='interactions:[]'; - } - - if(routeParam.geoinfo) - { - this.myGeoInfo = routeParam.geoinfo; - this.initGeoInfo = routeParam.geoinfo; - - } - if(this.geoinfo) - { - this.myGeoInfo = this.geoinfo; - this.initGeoInfo = this.geoinfo; - } - - - if(this.myGeoInfo) - { - this.latitude = this.myGeoInfo.features[0].geometry.coordinates[0]; - this.longitude = this.myGeoInfo.features[0].geometry.coordinates[1]; - this.mapZoom = CommonUtil.CONST_GPS_OBSERVATION_ZOOM; - } - else - { - this.mapZoom = CommonUtil.CONST_GPS_DEFAULT_ZOOM; - //this.mapZoom = CommonUtil.CONST_GPS_OBSERVATION_ZOOM; - } - - if(routeParam.locationPointOfInterestId) - { - this.poi.pointOfInterestId = routeParam.locationPointOfInterestId; - } - - this.$nextTick(function () { - this.initMap(this.latitude, this.longitude); - }); - }, - beforeDestroy() { - // This resets the container layout when leaving the router page - var appDiv = document.getElementById("app"); - //appDiv.style.marginTop="60px"; - appDiv.style.paddingRight="15px"; - appDiv.style.paddingLeft="15px"; - }, - -} + import CommonUtil from '@/components/CommonUtil' + import 'ol/ol.css'; + import Map from 'ol/Map'; + import OSM from 'ol/source/OSM'; + import TileLayer from 'ol/layer/Tile'; + import View from 'ol/View'; + import WMTS, {optionsFromCapabilities} from 'ol/source/WMTS'; + import WMTSCapabilities from 'ol/format/WMTSCapabilities'; + + import {Vector as VectorSource} from 'ol/source'; + import {Vector as VectorLayer} from 'ol/layer'; + import Collection from 'ol/Collection'; + import {fromLonLat} from 'ol/proj'; + + import Circle from 'ol/geom/Circle'; + import {Circle as CircleStyle, Fill, Stroke, Style, Icon} from 'ol/style'; + import Feature from 'ol/Feature'; + import GeoJSON from 'ol/format/GeoJSON'; + + import {toStringXY} from 'ol/coordinate'; + import {transform} from 'ol/proj'; + import Point from 'ol/geom/Point'; + import {Modify} from 'ol/interaction'; + import Draw from 'ol/interaction/Draw'; + import Overlay from 'ol/Overlay'; + import Geolocation from 'ol/Geolocation'; + import Visibility from '@/components/Visibility' + + import i18n from '@/App' + + + + let parser = new WMTSCapabilities(); + + + export default { + name: 'MapObservation', + props: ['observationId', 'geoinfo', 'isMapPanelVisible', 'locationPointOfInterestId', 'locationIsPrivate', 'polygonService', 'observation'], + components: {CommonUtil, Visibility}, + data() { + return { + CONST_URL_DOMAIN: '', + isMyMapPanelVisible: '', + myGeoInfo: '', + initGeoInfo: '', + latitude: 0, + longitude: 0, + mapZoom: 0, + mapInteractions: '', + lstPOI: [], + poi: {pointOfInterestId: 'undefined', name: this.$i18n.t("mapobservation.label.selectPOI")}, + myMap: '', + myObservationId: '', + mapObservation: {}, + isMapClicked: false, + } + }, + methods: { + visibilityMapAction(paramPrivate, paramPolygonService) { + this.$emit('visibilityObservationAction', paramPrivate, paramPolygonService); + }, + /** My current location */ + myposition() { + let options = {enableHighAccuracy: true}; + navigator.geolocation.getCurrentPosition(this.geolocationSuccess, this.geolocationError, options); + + }, + /** + * Get GeoInfo + * */ + geolocationSuccess(pos) { + this.poi.pointOfInterestId = 'undefined'; + this.longitude = pos.coords.latitude; + this.latitude = pos.coords.longitude; + let coord = [pos.coords.longitude, pos.coords.latitude]; + let transFormCord = transform(coord, 'EPSG:3857', 'EPSG:4326'); + this.myMap.getView().setCenter(fromLonLat(coord)); + + this.clearMapLayers(); + + let myImage = this.myImage(); + + let vectorSource = new VectorSource({}); + var iconFeature = new Feature({ + geometry: new Point(fromLonLat(coord)) + }); + + vectorSource.addFeature(iconFeature); + var vectorLayer = new VectorLayer({ + source: vectorSource, + style: new Style({ + image: myImage, + }), + }); + + + + let geoJSON = new GeoJSON(); + let resultGeoJSON = geoJSON.writeFeatures(vectorLayer.getSource().getFeatures()); + this.myGeoInfo = JSON.parse(resultGeoJSON); + this.myGeoInfo.features[0].geometry.coordinates = coord; + + this.myMap.addLayer(vectorLayer); + + this.myMap.getView().setZoom(CommonUtil.CONST_GPS_OBSERVATION_ZOOM); + + }, + geolocationError(error) { + console.log('geolocation error : ' + geolocationError); + }, + + /** Display of Map */ + initMap(myLatitude, myLongitude) { + + let pointOfInterestId = this.poi.pointOfInterestId; + let This = this; + let urlMap = CommonUtil.CONST_GPS_URL_NORWAY_MAP; // Norway Map + + let myGeoInfo = this.myGeoInfo; + let latitude = myLatitude; + let longitude = myLongitude; + let mapZoom = this.mapZoom; + + let image = this.myImage(); + + + let vectorSource = this.myVectorGeoSource(); + + let vectorGeoLayer = this.myVectorGeoLayer(vectorSource, image); + + let mapInteractions = this.myInteractions(this.mapInteractions); + + let mapView = this.myView(latitude, longitude, mapZoom); + + + let localIsMyMapPanelVisible = this.isMyMapPanelVisible; + let coordinate = [latitude, longitude]; + + let pointMarker = this.myOverLay(coordinate); + let pointMarkerCoord = this.myOverLayCoord(latitude, longitude); + + /* Fetch Norway Map */ + + fetch(urlMap) + .then(function (response) { + return response.text(); + }) + .then(function (text) { + let parser = new WMTSCapabilities(); + let result = parser.read(text); + + let options = optionsFromCapabilities(result, { + layer: 'topo4', + matrixSet: 'EPSG:3857', + }); + + + This.myMap = new Map({ + layers: [ + + new TileLayer({ + opacity: 1, + source: new WMTS(options), + }), + vectorGeoLayer + + ], + controls: [], + interactions: mapInteractions, + + target: 'map-observation', + view: mapView, + overlays: [pointMarkerCoord], + renderer: 'canvas', + }); + + + + /* On click event of map to get the location */ + This.myMap.on(['singleclick'], function (event) { + if (localIsMyMapPanelVisible) { + This.isMapClicked = true; + + This.poi.pointOfInterestId = 'undefined'; + + let transFormCord = transform(event.coordinate, 'EPSG:3857', 'EPSG:4326'); + This.latitude = transFormCord[0]; + This.longitude = transFormCord[1]; + let mapNewCord = toStringXY(transform(event.coordinate, 'EPSG:3857', 'EPSG:4326'), 4); + + + This.myMap.getView().setCenter(fromLonLat(transFormCord)); + + /******* Below code for vector marker positioning */ + + var iconFeature = new Feature({ + geometry: new Point(fromLonLat(transFormCord)) + }); + This.clearMapLayers(); + if (vectorSource) {} + else { + vectorSource = new VectorSource({}); + } + + if (vectorSource) { + vectorSource.addFeature(iconFeature); + + var vectorLayer = new VectorLayer({ + source: vectorSource, + style: new Style({ + image: image, + }), + }); + + This.myMap.addLayer(vectorLayer); + + let geoGSON = new GeoJSON(); + let resultGeoGSON = geoGSON.writeFeatures(vectorLayer.getSource().getFeatures(), { + dataProjection: 'EPSG:4326', + featureProjection: 'EPSG:3857' + }); + This.myGeoInfo = JSON.parse(resultGeoGSON); + + } + } + + }); + + }) + + + }, + + + myVectorGeoSource() { + if (this.myGeoInfo) { + return new VectorSource({ + features: new GeoJSON({ + dataProjection: "EPSG:4326", + featureProjection: "EPSG:3857" + }).readFeatures(this.myGeoInfo), + }) + } + }, + + myVectorGeoLayer(vectorSource, image) { + return new VectorLayer({ + source: vectorSource, + style: new Style({ + image: image, + }), + }) + }, + myView(latitude, longitude, mapZoom) { + return new View({ + center: fromLonLat([latitude, longitude]), + zoom: mapZoom + }) + }, + + myImage() { + var fill = new Fill({ + color: 'white' + }); + + return new CircleStyle({ + radius: 15, + fill: fill, + stroke: new Stroke({color: '#3d8052', width: 15}), + }); + }, + + + myOverLay(coordinates) { + return new Overlay({ + position: fromLonLat(coordinates), + positioning: 'bottom-center', + element: document.getElementById('poiMarker'), + stopEvent: false + }); + }, + + + myOverLayCoord(latitude, longitude) { + let coordinate = [latitude, longitude]; + return this.myOverLay(coordinate); + }, + + + myStyleFunction(styles, feature) { + return styles[feature.getGeometry().getType()]; + }, + + myInteractions(mapInteractions) { + return (mapInteractions) ? [] : ''; + }, + + /* Get point of interest */ + getMyPointOfInterst(lstPOI) { + let userUUID = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID); + let jsonHeader = {Authorization: userUUID}; + + lstPOI.push({pointOfInterestId: 'undefined', name: this.$i18n.t("mapobservation.label.noPOIselected")}); + + fetch(this.CONST_URL_DOMAIN + CommonUtil.CONST_URL_USER_POI, + { + method: "GET", + headers: jsonHeader, + }).then((response) => { + if (response.status == 200) { + return response.json() + } + }) + .then((jsonLstPOI) => { + $.each(jsonLstPOI, function (index, poi) { + let myPOI = poi; + lstPOI.push(myPOI); + }) + + }) + }, + + /* Clear existing sources in map layers */ + clearMapLayers() { + let myLayers = this.myMap.getLayers(); + if (myLayers) { + this.myMap.getLayers().forEach(function (layer) { + let source = layer.get('source'); + if (source) { + source.clear(); + } + + }) + } + }, + + selectPOI(event) { + let myPOI = this.poi; + if (event.target.value != 0) { + $.each(this.lstPOI, function (index, poi) { + if (poi.pointOfInterestId === JSON.parse(event.target.value)) { + myPOI = poi; + return false; + } + }) + + this.myGeoInfo = JSON.parse(myPOI.geoJSON); + let coordinate = this.myGeoInfo.features[0].geometry.coordinates; + this.latitude = coordinate[0]; + this.longitude = coordinate[1]; + + + + let myImage = this.myImage(); + let transFormCord = [this.latitude, this.longitude]; + //let vectorSource = this.myVectorGeoSource(); + let vectorSource = new VectorSource({}); + + var iconFeature = new Feature({ + geometry: new Point(fromLonLat(transFormCord)) + }); + + vectorSource.addFeature(iconFeature); + + var vectorLayer = new VectorLayer({ + source: vectorSource, + style: new Style({ + image: myImage, + }), + }); + + this.clearMapLayers(); + this.myMap.addLayer(vectorLayer); + + this.myMap.getView().setCenter(fromLonLat(coordinate)); + this.myMap.getView().setZoom(CommonUtil.CONST_GPS_OBSERVATION_ZOOM); + } + + + + } + + }, + mounted() { + + this.CONST_URL_DOMAIN = CommonUtil.CONST_URL_DOMAIN; + this.latitude = CommonUtil.CONST_GPS_DEFAULT_LATITUDE_NORWAY; + this.longitude = CommonUtil.CONST_GPS_DEFAULT_LONGITUDE_NORWAY; + + let routeParam = this.$route.params; + + // This ensures that the map fills the entire viewport + var mapDiv = document.getElementById("map-observation"); + var navDiv = document.getElementById("vipsobsappmenu"); + var appDiv = document.getElementById("app"); + var panelObDiv = document.getElementById("ObservationMapPanel"); + var divPrivacy = document.getElementById("divPrivacy"); + + if (routeParam.observation) { + this.mapObservation = routeParam.observation; + } + + if (routeParam.observationId) { + this.myObservationId = routeParam.observationId; + } + if (this.observationId) { + this.myObservationId = this.observationId; + } + + if (routeParam.isMapPanelVisible) { + this.isMyMapPanelVisible = routeParam.isMapPanelVisible; + + } + if (this.isMapPanelVisible) { + this.isMyMapPanelVisible = this.isMapPanelVisible; + } + + if (this.isMyMapPanelVisible) { + mapDiv.style.height = (screen.height - navDiv.offsetHeight) + "px"; + this.mapInteractions = ''; + + appDiv.style.marginTop = "0"; + appDiv.style.paddingRight = "0"; + appDiv.style.paddingLeft = "0"; + + this.getMyPointOfInterst(this.lstPOI); + } + else { + mapDiv.style.height = 300 + "px"; + this.mapInteractions = 'interactions:[]'; + } + + if (routeParam.geoinfo) { + this.myGeoInfo = routeParam.geoinfo; + this.initGeoInfo = routeParam.geoinfo; + + } + if (this.geoinfo) { + this.myGeoInfo = this.geoinfo; + this.initGeoInfo = this.geoinfo; + } + + + if (this.myGeoInfo) { + this.latitude = this.myGeoInfo.features[0].geometry.coordinates[0]; + this.longitude = this.myGeoInfo.features[0].geometry.coordinates[1]; + this.mapZoom = CommonUtil.CONST_GPS_OBSERVATION_ZOOM; + } + else { + this.mapZoom = CommonUtil.CONST_GPS_DEFAULT_ZOOM; + //this.mapZoom = CommonUtil.CONST_GPS_OBSERVATION_ZOOM; + } + + if (routeParam.locationPointOfInterestId) { + this.poi.pointOfInterestId = routeParam.locationPointOfInterestId; + } + + this.$nextTick(function () { + this.initMap(this.latitude, this.longitude); + }); + }, + beforeDestroy() { + // This resets the container layout when leaving the router page + var appDiv = document.getElementById("app"); + //appDiv.style.marginTop="60px"; + appDiv.style.paddingRight = "15px"; + appDiv.style.paddingLeft = "15px"; + }, + + } </script> <style scoped> - - html, body, #map-observation { - margin: 0; - width: 100%; - } - - #ObservationMapPanel { - position: absolute; - bottom: 0; - left: 0; - } - - #btnBack{ - position: fixed; - z-index: 2000; - } - #map-mylocation { - position: fixed; - right: 0; - z-index: 2000; - } - + html, + body, + #map-observation { + margin: 0; + width: 100%; + } + + #ObservationMapPanel { + position: absolute; + bottom: 0; + left: 0; + } + + #btnBack { + position: fixed; + z-index: 2000; + } + + #map-mylocation { + position: fixed; + right: 0; + z-index: 2000; + } </style> \ No newline at end of file diff --git a/src/components/MapPOI.vue b/src/components/MapPOI.vue index 2d0d49f5763ae67ebf8f53ce9e95eeeb87bd82e7..ba5724dc90325ba8f7ca20c985744b2cb03d674c 100644 --- a/src/components/MapPOI.vue +++ b/src/components/MapPOI.vue @@ -1,4 +1,4 @@ - <!-- +<!-- Copyright (c) 2022 NIBIO <http://www.nibio.no/>. @@ -22,581 +22,551 @@ --> <template> - <div> - <router-link id='btnBack' :to="{name:'PlacesList', params: {}}" class="vips-btn"> - {{ $t("map.link.back.label") }} - </router-link> - <div v-show="userCanEditPOI" id='map-mylocation' style="margin: 5px;"> - <a v-on:click="myposition" style="cursor:pointer; "><font-awesome-icon style="font-size: x-large; padding: 5px; color: white; background-color:#3d8052;" icon="location-crosshairs" /></a> - </div> - <div id='map-poi'></div> - - <div id='divPoiData' class="container" > - <div class="form-group"><input class="form-control" :placeholder="$t('mapPOI.poiName.label')" id='poiName' ref='poiName' v-model="poi.name" :disabled="!userCanEditPOI"/></div> - - <div class='form-group'> - <select class="form-control" v-model="poi.pointOfInterestTypeId" :disabled="!userCanEditPOI"> - <option v-for="poiType in poiTypes" v-bind:key="poiType.point_of_interest_type_id" :value='poiType.point_of_interest_type_id' :disabled="!poiType.is_user_selectable">{{$t(poiType.default_name)}}</option> - </select> - </div> - <div class="form-group" v-show="userCanEditPOI"> - <div class="float-right"> - <a class="vips-btn" v-on:click="validate">{{$t("save.label")}}</a> - <a v-show="POIExists" class="vips-btn danger" v-on:click="callForRemovePOI">{{$t("delete.label")}}</a> - </div> + <div> + <router-link id='btnBack' :to="{name:'PlacesList', params: {}}" class="vips-btn"> + {{ $t("map.link.back.label") }} + </router-link> + <div v-show="userCanEditPOI" id='map-mylocation' style="margin: 5px;"> + <a v-on:click="myposition" style="cursor:pointer; "> + <font-awesome-icon style="font-size: x-large; padding: 5px; color: white; background-color:#3d8052;" + icon="location-crosshairs" /></a> + </div> + <div id='map-poi'></div> + + <div id='divPoiData' class="container"> + <div class="form-group"><input class="form-control" :placeholder="$t('mapPOI.poiName.label')" id='poiName' + ref='poiName' v-model="poi.name" :disabled="!userCanEditPOI" /></div> + + <div class='form-group'> + <select class="form-control" v-model="poi.pointOfInterestTypeId" :disabled="!userCanEditPOI"> + <option v-for="poiType in poiTypes" v-bind:key="poiType.point_of_interest_type_id" + :value='poiType.point_of_interest_type_id' :disabled="!poiType.is_user_selectable"> + {{$t(poiType.default_name)}}</option> + </select> + </div> + <div class="form-group" v-show="userCanEditPOI"> + <div class="float-right"> + <a class="vips-btn" v-on:click="validate">{{$t("save.label")}}</a> + <a v-show="POIExists" class="vips-btn danger" + v-on:click="callForRemovePOI">{{$t("delete.label")}}</a> </div> - </div> - <div id="poiMarker" style="display:none"> - <img src="@/assets/map_icon.png"> - </div> - <sync ref="sync"/> - </div> + </div> + </div> + <div id="poiMarker" style="display:none"> + <img src="@/assets/map_icon.png"> + </div> + <sync ref="sync" /> + </div> </template> <script> -import CommonUtil from '@/components/CommonUtil'; -import Sync from '@/components/Sync' - -import 'ol/ol.css'; -import Map from 'ol/Map'; -import OSM from 'ol/source/OSM'; -import TileLayer from 'ol/layer/Tile'; -import View from 'ol/View'; -import WMTS, {optionsFromCapabilities} from 'ol/source/WMTS'; -import WMTSCapabilities from 'ol/format/WMTSCapabilities'; - -import {Vector as VectorSource} from 'ol/source'; -import {Vector as VectorLayer} from 'ol/layer'; -import Collection from 'ol/Collection'; -import {fromLonLat} from 'ol/proj'; - -import Circle from 'ol/geom/Circle'; -import {Circle as CircleStyle, Fill, Stroke, Style} from 'ol/style'; -import Feature from 'ol/Feature'; -import GeoJSON from 'ol/format/GeoJSON'; - -import {toStringXY} from 'ol/coordinate'; -import {transform} from 'ol/proj'; -import Point from 'ol/geom/Point'; -import {Modify} from 'ol/interaction'; -import Draw from 'ol/interaction/Draw'; -import Overlay from 'ol/Overlay'; -import Geolocation from 'ol/Geolocation'; - -import i18n from '@/App' - - -export default{ - name : 'MapPOI', - components : {Sync}, - props : ['pointOfInterestId'], - - data() { - return { - poi : {}, - mapZoom : 0, - poiTypes : [], - msgErr : '', - map : '', - - - } - }, - computed: { - userCanEditPOI: function(){ - return this.isPOIOwnedByUser(); + import CommonUtil from '@/components/CommonUtil'; + import Sync from '@/components/Sync' + + import 'ol/ol.css'; + import Map from 'ol/Map'; + import OSM from 'ol/source/OSM'; + import TileLayer from 'ol/layer/Tile'; + import View from 'ol/View'; + import WMTS, {optionsFromCapabilities} from 'ol/source/WMTS'; + import WMTSCapabilities from 'ol/format/WMTSCapabilities'; + + import {Vector as VectorSource} from 'ol/source'; + import {Vector as VectorLayer} from 'ol/layer'; + import Collection from 'ol/Collection'; + import {fromLonLat} from 'ol/proj'; + + import Circle from 'ol/geom/Circle'; + import {Circle as CircleStyle, Fill, Stroke, Style} from 'ol/style'; + import Feature from 'ol/Feature'; + import GeoJSON from 'ol/format/GeoJSON'; + + import {toStringXY} from 'ol/coordinate'; + import {transform} from 'ol/proj'; + import Point from 'ol/geom/Point'; + import {Modify} from 'ol/interaction'; + import Draw from 'ol/interaction/Draw'; + import Overlay from 'ol/Overlay'; + import Geolocation from 'ol/Geolocation'; + + import i18n from '@/App' + + + export default { + name: 'MapPOI', + components: {Sync}, + props: ['pointOfInterestId'], + + data() { + return { + poi: {}, + mapZoom: 0, + poiTypes: [], + msgErr: '', + map: '', + + + } }, - POIExists: function() { - return this.poi.pointOfInterestId; - } - }, - methods : { - - isPOIOwnedByUser(){ - return !this.poi.pointOfInterestId || (this.poi.userId && this.poi.userId == this.$root.sharedState.userId); + computed: { + userCanEditPOI: function () { + return this.isPOIOwnedByUser(); + }, + POIExists: function () { + return this.poi.pointOfInterestId; + } }, - callForRemovePOI() - { - if(confirm(this.$i18n.t("mapPOI.modal.label.deleteprompt") + " " + this.poi.name + "?")) - { - this.deletePOI(); + methods: { + + isPOIOwnedByUser() { + return !this.poi.pointOfInterestId || (this.poi.userId && this.poi.userId == this.$root.sharedState.userId); + }, + callForRemovePOI() { + if (confirm(this.$i18n.t("mapPOI.modal.label.deleteprompt") + " " + this.poi.name + "?")) { + this.deletePOI(); + } + }, + deletePOI() { + if (this.poi.pointOfInterestId) { + if (this.poi.pointOfInterestId < 0) { + /** Just remove it locally */ + this.removeLocalPOI(this.poi.pointOfInterestId); + this.$router.replace({path: '/places'}); } - }, - deletePOI() - { - if(this.poi.pointOfInterestId) - { - if(this.poi.pointOfInterestId < 0) - { - /** Just remove it locally */ - this.removeLocalPOI(this.poi.pointOfInterestId); - this.$router.replace({path:'/places'}); - } - else - { - /** Mark the record - for sending to server */ - this.poi.deleted=true; - this.saveToStore(); - } - - } - }, - removeLocalPOI(id) - { - let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)); - let indexPosition = null; - $.each(lstPOI, function(index, poi){ - if(poi.pointOfInterestId===id) - { - indexPosition = index; - return false; - } - }); - - if(indexPosition) - { - - lstPOI.splice(indexPosition,1); - localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST,JSON.stringify(lstPOI)); - - } - - }, - validate() - { - if((!this.poi.name) || (this.trimString(this.poi.name) === '')) - { - alert(this.$i18n.t("mapPOI.modal.alert.missingPOIName")); - return; - } - if(!this.poi.geoJSON) - { - alert(this.$i18n.t("mapPOI.modal.alert.missingGeoInfo")); - return; + else { + /** Mark the record - for sending to server */ + this.poi.deleted = true; + this.saveToStore(); } - // TODO Simplify this logic - if (!this.poi.pointOfInterestTypeId) - { - if(this.poi.pointOfInterestTypeId === 0) - { - // Pass - } - else - { - alert(this.$i18n.t("mapPOI.modal.alert.missingPOIType")); - return false; - } - } - - if(confirm(this.$i18n.t("mapPOI.modal.label.saveprompt") + " " + this.poi.name + "?")) - { - this.saveToStore(); + } + }, + removeLocalPOI(id) { + let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)); + let indexPosition = null; + $.each(lstPOI, function (index, poi) { + if (poi.pointOfInterestId === id) { + indexPosition = index; + return false; } - }, - getPointOfInterest(id){ - let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)); - let poi = lstPOI.find(({pointOfInterestId}) => pointOfInterestId === id); - - this.poi = poi; - - }, - saveToStore(){ - let This = this; - let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)); - if(this.poi.pointOfInterestId) - { - $.each(lstPOI, function(index, poi){ - if(poi.pointOfInterestId === This.poi.pointOfInterestId) - { - poi.latitude=This.poi.latitude; - poi.longitude=This.poi.longitude; - poi.name=This.poi.name; - poi.pointOfInterestTypeId=This.poi.pointOfInterestTypeId; - poi.geoJSON=This.poi.geoJSON - poi.uploaded=false; - if(This.poi.deleted) - { - poi.deleted = This.poi.deleted; - } - - } - }) - } - else - { - this.poi.pointOfInterestId = this.getNewPoiId(lstPOI); - this.poi.userId = this.$root.sharedState.userId; - this.poi.uploaded=false; - if(lstPOI) - { - lstPOI.push(this.poi); - } - else - { - lstPOI = []; - lstPOI.push(this.poi); - } - - } - localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST,JSON.stringify(lstPOI)); - this.$refs.sync.syncTwoWay(); - this.$router.push('/places'); - }, - /** new POI pointOfInterestId */ - getNewPoiId(lstPOI) - { - let newId = 0; - let poiIds=[]; - - - if(lstPOI) - { - $.each(lstPOI, function(index, poi){ - if(poi.pointOfInterestId < 0) - { - poiIds.push(Math.abs(poi.pointOfInterestId)); - } - }); - if(poiIds.length === 0) - { - newId = CommonUtil.CONST_POI_COUNT_START_ID; - } - else - { - let largestValue = Math.max.apply(null, poiIds); - newId = -Math.abs(largestValue + 1); - } - } - else - { - newId = CommonUtil.CONST_POI_COUNT_START_ID; - } - - return newId; - }, - - - - - myImage() - { - var fill = new Fill({ - color: 'white' - }); - - return new CircleStyle({ - radius: 15, - fill: fill, - stroke: new Stroke({color: '#3d8052', width: 15}), - }); - }, - myVectorGeoSource(geoInfo){ - if(geoInfo) - { - return new VectorSource({ - features : new GeoJSON({dataProjection:"EPSG:4326", - featureProjection:"EPSG:3857"}).readFeatures(geoInfo), - }) - } - }, - myVectorGeoLayer(vectorSource,image){ - return new VectorLayer({ - source : vectorSource, - style : new Style({ - image: image, - }), - }) - }, - - myView(longitude,latitude,mapZoom){ - return new View ({ - center:fromLonLat([longitude,latitude]), - zoom : mapZoom - }) - }, - myOverLayCoord(longitude,latitude) - { - let coordinate = [longitude,latitude]; - return this.myOverLay(coordinate); - }, - myOverLay(coordinates){ - return new Overlay({ - position : fromLonLat(coordinates) , - positioning: 'bottom-center', - element: document.getElementById('poiMarker'), - stopEvent: false - }); - }, - myInteractions(mapInteractions) - { - return (mapInteractions) ? [] : ''; - }, - - /** My current location */ - myposition() - { - let options = { enableHighAccuracy: true }; - navigator.geolocation.getCurrentPosition(this.geolocationSuccess, this.geolocationError, options); - //navigator.geolocation.getCurrentPosition(this.geolocationSuccess, this.geolocationError, this.geolocationOptions); - - - }, - - geolocationOptions(options){ - console.log('geolocation options : '+options); - }, - geolocationError(error){ - console.log('geolocation error : '+geolocationError); - }, - - geolocationSuccess(pos) { - let This = this; - This.poi.latitude = pos.coords.latitude; - This.poi.longitude = pos.coords.longitude; - let coord = [pos.coords.longitude,pos.coords.latitude]; - - // This is the marker icon for the location of the POI - let vectorLayer = new VectorLayer({ - source : new VectorSource ({ - features : [ - new Feature({ - geometry : new Point(fromLonLat(coord)) - }) - ], - }), - style : new Style({ - image : This.myImage() - }) - }); - let geoJSON = new GeoJSON(); - let resultGeoJSON = geoJSON.writeFeatures(vectorLayer.getSource().getFeatures(),{ - dataProjection: 'EPSG:4326', - featureProjection: 'EPSG:3857' - }); - this.poi.geoJSON = resultGeoJSON; - - if(This.map) - { - let mapLayers = This.map.getLayers(); - mapLayers.forEach(function(layer){ - let source = layer.get('source'); - source.clear(); - }) - This.map.addLayer(vectorLayer); - This.map.getView().setCenter(fromLonLat(coord)); - This.map.getView().setZoom(CommonUtil.CONST_GPS_OBSERVATION_ZOOM); - } - }, - - mapInit() - { - let This = this; - let urlMap = CommonUtil.CONST_GPS_URL_NORWAY_MAP; - let latitude = this.poi.latitude; - let longitude = this.poi.longitude; - let mapZoom = this.mapZoom; - - let geoInfo = ''; - if(this.poi && this.poi.geoJSON) - { - geoInfo = JSON.parse(this.poi.geoJSON); - } - else - { - let image = this.myImage(); - latitude = CommonUtil.CONST_GPS_DEFAULT_LATITUDE_02_NORWAY; - longitude = CommonUtil.CONST_GPS_DEFAULT_LONGITUDE_02_NORWAY; - mapZoom = CommonUtil.CONST_GPS_DEFAULT_ZOOM; - - let coord = [CommonUtil.CONST_GPS_DEFAULT_LONGITUDE_02_NORWAY,CommonUtil.CONST_GPS_DEFAULT_LATITUDE_02_NORWAY]; - let transFormCord = transform(coord, 'EPSG:3857','EPSG:4326'); - let iconFeature = new Feature({ - geometry: new Point(fromLonLat(transFormCord)) - }); - - let vectorSource = new VectorSource({}); - vectorSource.addFeature(iconFeature); - let vectorLayer = new VectorLayer({ - source: vectorSource, - style: new Style({ - image: image, - }), - }); - - let geoGSON = new GeoJSON(); - let resultGeoGSON = JSON.parse(geoGSON.writeFeatures(vectorLayer.getSource().getFeatures())); - //this.poi.geoJSON = JSON.stringify(resultGeoGSON); - geoInfo = resultGeoGSON; - - } + }); + + if (indexPosition) { + + lstPOI.splice(indexPosition, 1); + localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST, JSON.stringify(lstPOI)); + + } + + }, + validate() { + if ((!this.poi.name) || (this.trimString(this.poi.name) === '')) { + alert(this.$i18n.t("mapPOI.modal.alert.missingPOIName")); + return; + } + if (!this.poi.geoJSON) { + alert(this.$i18n.t("mapPOI.modal.alert.missingGeoInfo")); + return; + } + + // TODO Simplify this logic + if (!this.poi.pointOfInterestTypeId) { + if (this.poi.pointOfInterestTypeId === 0) { + // Pass + } + else { + alert(this.$i18n.t("mapPOI.modal.alert.missingPOIType")); + return false; + } + } + + if (confirm(this.$i18n.t("mapPOI.modal.label.saveprompt") + " " + this.poi.name + "?")) { + this.saveToStore(); + } + }, + getPointOfInterest(id) { + let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)); + let poi = lstPOI.find(({pointOfInterestId}) => pointOfInterestId === id); + + this.poi = poi; + + }, + saveToStore() { + let This = this; + let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)); + if (this.poi.pointOfInterestId) { + $.each(lstPOI, function (index, poi) { + if (poi.pointOfInterestId === This.poi.pointOfInterestId) { + poi.latitude = This.poi.latitude; + poi.longitude = This.poi.longitude; + poi.name = This.poi.name; + poi.pointOfInterestTypeId = This.poi.pointOfInterestTypeId; + poi.geoJSON = This.poi.geoJSON + poi.uploaded = false; + if (This.poi.deleted) { + poi.deleted = This.poi.deleted; + } + + } + }) + } + else { + this.poi.pointOfInterestId = this.getNewPoiId(lstPOI); + this.poi.userId = this.$root.sharedState.userId; + this.poi.uploaded = false; + if (lstPOI) { + lstPOI.push(this.poi); + } + else { + lstPOI = []; + lstPOI.push(this.poi); + } + + } + localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST, JSON.stringify(lstPOI)); + this.$refs.sync.syncTwoWay(); + this.$router.push('/places'); + }, + /** new POI pointOfInterestId */ + getNewPoiId(lstPOI) { + let newId = 0; + let poiIds = []; + + + if (lstPOI) { + $.each(lstPOI, function (index, poi) { + if (poi.pointOfInterestId < 0) { + poiIds.push(Math.abs(poi.pointOfInterestId)); + } + }); + if (poiIds.length === 0) { + newId = CommonUtil.CONST_POI_COUNT_START_ID; + } + else { + let largestValue = Math.max.apply(null, poiIds); + newId = -Math.abs(largestValue + 1); + } + } + else { + newId = CommonUtil.CONST_POI_COUNT_START_ID; + } + + return newId; + }, + + + + + myImage() { + var fill = new Fill({ + color: 'white' + }); + + return new CircleStyle({ + radius: 15, + fill: fill, + stroke: new Stroke({color: '#3d8052', width: 15}), + }); + }, + myVectorGeoSource(geoInfo) { + if (geoInfo) { + return new VectorSource({ + features: new GeoJSON({ + dataProjection: "EPSG:4326", + featureProjection: "EPSG:3857" + }).readFeatures(geoInfo), + }) + } + }, + myVectorGeoLayer(vectorSource, image) { + return new VectorLayer({ + source: vectorSource, + style: new Style({ + image: image, + }), + }) + }, + + myView(longitude, latitude, mapZoom) { + return new View({ + center: fromLonLat([longitude, latitude]), + zoom: mapZoom + }) + }, + myOverLayCoord(longitude, latitude) { + let coordinate = [longitude, latitude]; + return this.myOverLay(coordinate); + }, + myOverLay(coordinates) { + return new Overlay({ + position: fromLonLat(coordinates), + positioning: 'bottom-center', + element: document.getElementById('poiMarker'), + stopEvent: false + }); + }, + myInteractions(mapInteractions) { + return (mapInteractions) ? [] : ''; + }, + + /** My current location */ + myposition() { + let options = {enableHighAccuracy: true}; + navigator.geolocation.getCurrentPosition(this.geolocationSuccess, this.geolocationError, options); + //navigator.geolocation.getCurrentPosition(this.geolocationSuccess, this.geolocationError, this.geolocationOptions); + + + }, + + geolocationOptions(options) { + console.log('geolocation options : ' + options); + }, + geolocationError(error) { + console.log('geolocation error : ' + geolocationError); + }, + + geolocationSuccess(pos) { + let This = this; + This.poi.latitude = pos.coords.latitude; + This.poi.longitude = pos.coords.longitude; + let coord = [pos.coords.longitude, pos.coords.latitude]; + + // This is the marker icon for the location of the POI + let vectorLayer = new VectorLayer({ + source: new VectorSource({ + features: [ + new Feature({ + geometry: new Point(fromLonLat(coord)) + }) + ], + }), + style: new Style({ + image: This.myImage() + }) + }); + let geoJSON = new GeoJSON(); + let resultGeoJSON = geoJSON.writeFeatures(vectorLayer.getSource().getFeatures(), { + dataProjection: 'EPSG:4326', + featureProjection: 'EPSG:3857' + }); + this.poi.geoJSON = resultGeoJSON; + + if (This.map) { + let mapLayers = This.map.getLayers(); + mapLayers.forEach(function (layer) { + let source = layer.get('source'); + source.clear(); + }) + This.map.addLayer(vectorLayer); + This.map.getView().setCenter(fromLonLat(coord)); + This.map.getView().setZoom(CommonUtil.CONST_GPS_OBSERVATION_ZOOM); + } + }, + + mapInit() { + let This = this; + let urlMap = CommonUtil.CONST_GPS_URL_NORWAY_MAP; + let latitude = this.poi.latitude; + let longitude = this.poi.longitude; + let mapZoom = this.mapZoom; + + let geoInfo = ''; + if (this.poi && this.poi.geoJSON) { + geoInfo = JSON.parse(this.poi.geoJSON); + } + else { + let image = this.myImage(); + latitude = CommonUtil.CONST_GPS_DEFAULT_LATITUDE_02_NORWAY; + longitude = CommonUtil.CONST_GPS_DEFAULT_LONGITUDE_02_NORWAY; + mapZoom = CommonUtil.CONST_GPS_DEFAULT_ZOOM; + + let coord = [CommonUtil.CONST_GPS_DEFAULT_LONGITUDE_02_NORWAY, CommonUtil.CONST_GPS_DEFAULT_LATITUDE_02_NORWAY]; + let transFormCord = transform(coord, 'EPSG:3857', 'EPSG:4326'); + let iconFeature = new Feature({ + geometry: new Point(fromLonLat(transFormCord)) + }); + + let vectorSource = new VectorSource({}); + vectorSource.addFeature(iconFeature); + let vectorLayer = new VectorLayer({ + source: vectorSource, + style: new Style({ + image: image, + }), + }); + + let geoGSON = new GeoJSON(); + let resultGeoGSON = JSON.parse(geoGSON.writeFeatures(vectorLayer.getSource().getFeatures())); + //this.poi.geoJSON = JSON.stringify(resultGeoGSON); + geoInfo = resultGeoGSON; + + } // Set default POI type to field - if(! this.poi.pointOfInterestTypeId) - { + if (!this.poi.pointOfInterestTypeId) { this.poi.pointOfInterestTypeId = CommonUtil.CONST_POI_TYPE_DEFAULT; } - fetch(urlMap) - .then(function (response) { - return response.text(); - }) - .then(function (text) { - let parser = new WMTSCapabilities(); - var result = parser.read(text); - var options = optionsFromCapabilities(result, { - layer: 'topo4', - matrixSet: 'EPSG:3857', - }); - - This.map = new Map({ - layers: [ - new TileLayer({ - opacity: 1, - source: new WMTS(options), - }) , - new VectorLayer({ - source : new VectorSource({ - features : new GeoJSON({ - dataProjection:"EPSG:4326", - featureProjection:"EPSG:3857" - }).readFeatures(geoInfo), - }), - style : new Style({ - image: This.myImage(), - }), - }), - - ], - controls : [], - target : 'map-poi', - view : new View({ - center : fromLonLat([longitude,latitude]), - zoom : mapZoom, - }), - }); - - /** Remove map pointer for first time newly poi */ - if(!This.poi.longitude) - { - - let mapLayers = This.map.getLayers(); - mapLayers.forEach(function(layer){ - let source = layer.get('source'); - source.clear(); - }) - } - - This.map.on(['singleclick'],function(event){ - if(!This.userCanEditPOI) - { - return; - } - let transFormCord = transform(event.coordinate, 'EPSG:3857','EPSG:4326'); - This.poi.longitude = transFormCord[0]; - This.poi.latitude = transFormCord[1]; - - This.map.getView().setCenter(fromLonLat(transFormCord)); - - let mapLayers = This.map.getLayers(); - mapLayers.forEach(function(layer){ - let source = layer.get('source'); - source.clear(); - }) - - let vectorLayer = new VectorLayer({ - source : new VectorSource ({ - features : [ - new Feature({ - geometry : new Point(fromLonLat(transFormCord)) - }) - ], - }), - style : new Style({ - image : This.myImage() - }) - }); - - This.map.addLayer(vectorLayer); - - let geoGSON = new GeoJSON(); - let resultGeoGSON = geoGSON.writeFeatures(vectorLayer.getSource().getFeatures(), { - dataProjection : 'EPSG:4326', - featureProjection : 'EPSG:3857' - }) - This.poi.geoJSON = resultGeoGSON; - - }) - - - - - }); - - }, - trimString(param) - { - return param.replace(/\s*/g,"") - } - - - }, - filters: { - - trim: function(string) { - return string.trim() - } - - }, - mounted() { - var appDiv = document.getElementById("app"); - var navDiv = document.getElementById("vipsobsappmenu"); - var mapDiv = document.getElementById("map-poi"); - appDiv.style.marginTop = "0"; - appDiv.style.paddingRight = "0"; - appDiv.style.paddingLeft = "0"; - mapDiv.style.height = (screen.height - navDiv.offsetHeight) + "px"; - - this.mapZoom = CommonUtil.CONST_GPS_OBSERVATION_ZOOM; - this.poiTypes = CommonUtil.CONST_POI_TYPES; + fetch(urlMap) + .then(function (response) { + return response.text(); + }) + .then(function (text) { + let parser = new WMTSCapabilities(); + var result = parser.read(text); + var options = optionsFromCapabilities(result, { + layer: 'topo4', + matrixSet: 'EPSG:3857', + }); + + This.map = new Map({ + layers: [ + new TileLayer({ + opacity: 1, + source: new WMTS(options), + }), + new VectorLayer({ + source: new VectorSource({ + features: new GeoJSON({ + dataProjection: "EPSG:4326", + featureProjection: "EPSG:3857" + }).readFeatures(geoInfo), + }), + style: new Style({ + image: This.myImage(), + }), + }), + + ], + controls: [], + target: 'map-poi', + view: new View({ + center: fromLonLat([longitude, latitude]), + zoom: mapZoom, + }), + }); + + /** Remove map pointer for first time newly poi */ + if (!This.poi.longitude) { + + let mapLayers = This.map.getLayers(); + mapLayers.forEach(function (layer) { + let source = layer.get('source'); + source.clear(); + }) + } + + This.map.on(['singleclick'], function (event) { + if (!This.userCanEditPOI) { + return; + } + let transFormCord = transform(event.coordinate, 'EPSG:3857', 'EPSG:4326'); + This.poi.longitude = transFormCord[0]; + This.poi.latitude = transFormCord[1]; + + This.map.getView().setCenter(fromLonLat(transFormCord)); + + let mapLayers = This.map.getLayers(); + mapLayers.forEach(function (layer) { + let source = layer.get('source'); + source.clear(); + }) + + let vectorLayer = new VectorLayer({ + source: new VectorSource({ + features: [ + new Feature({ + geometry: new Point(fromLonLat(transFormCord)) + }) + ], + }), + style: new Style({ + image: This.myImage() + }) + }); + + This.map.addLayer(vectorLayer); + + let geoGSON = new GeoJSON(); + let resultGeoGSON = geoGSON.writeFeatures(vectorLayer.getSource().getFeatures(), { + dataProjection: 'EPSG:4326', + featureProjection: 'EPSG:3857' + }) + This.poi.geoJSON = resultGeoGSON; + + }) + + + + + }); + + }, + trimString(param) { + return param.replace(/\s*/g, "") + } + + + }, + filters: { + + trim: function (string) { + return string.trim() + } + + }, + mounted() { + var appDiv = document.getElementById("app"); + var navDiv = document.getElementById("vipsobsappmenu"); + var mapDiv = document.getElementById("map-poi"); + appDiv.style.marginTop = "0"; + appDiv.style.paddingRight = "0"; + appDiv.style.paddingLeft = "0"; + mapDiv.style.height = (screen.height - navDiv.offsetHeight) + "px"; + + this.mapZoom = CommonUtil.CONST_GPS_OBSERVATION_ZOOM; + this.poiTypes = CommonUtil.CONST_POI_TYPES; //console.info(this.poiTypes); - if(this.$route.params.pointOfInterestId) - { - this.getPointOfInterest(this.$route.params.pointOfInterestId); - } - this.mapInit(); - - - }, - beforeDestroy() { - // This resets the container layout when leaving the router page - var appDiv = document.getElementById("app"); - appDiv.style.marginTop="15px"; - appDiv.style.paddingRight="15px"; - appDiv.style.paddingLeft="15px"; - }, -} + if (this.$route.params.pointOfInterestId) { + this.getPointOfInterest(this.$route.params.pointOfInterestId); + } + this.mapInit(); + + + }, + beforeDestroy() { + // This resets the container layout when leaving the router page + var appDiv = document.getElementById("app"); + appDiv.style.marginTop = "15px"; + appDiv.style.paddingRight = "15px"; + appDiv.style.paddingLeft = "15px"; + }, + } </script> <style> - html, body, #map-poi { - margin: 0; - width: 100%; - } - - #btnBack{ - position: fixed; - z-index: 1000; - } - #map-mylocation { - position: fixed; - right: 0; - z-index: 2000; - } - #divPoiData { - position: fixed; - z-index: 1100; - bottom: 0; - } -</style> + html, + body, + #map-poi { + margin: 0; + width: 100%; + } + + #btnBack { + position: fixed; + z-index: 1000; + } + + #map-mylocation { + position: fixed; + right: 0; + z-index: 2000; + } + + #divPoiData { + position: fixed; + z-index: 1100; + bottom: 0; + } +</style> \ No newline at end of file diff --git a/src/components/ObservationIllustration.vue b/src/components/ObservationIllustration.vue index 8424f45c94fbdd8041180fb91554fe6cdd2fe49d..201fdeb42c9599f7e982b0ef09da678596e17378 100644 --- a/src/components/ObservationIllustration.vue +++ b/src/components/ObservationIllustration.vue @@ -1,4 +1,4 @@ - <!-- +<!-- Copyright (c) 2022 NIBIO <http://www.nibio.no/>. @@ -25,198 +25,180 @@ <template> <div> <div style="position:relative;"> - <img :src="imageBase64Data" style="width: 100%; margin-bottom: 10px;" @click="handleImageClick"/> - <font-awesome-icon style="font-size: large; color: white; background-color:#3d8052; position:absolute; top: 5px; left: 5px; padding: 4px;" icon="fa-regular fa-trash-can" @click="handleDeleteImageRequest" /> + <img :src="imageBase64Data" style="width: 100%; margin-bottom: 10px;" @click="handleImageClick" /> + <font-awesome-icon + style="font-size: large; color: white; background-color:#3d8052; position:absolute; top: 5px; left: 5px; padding: 4px;" + icon="fa-regular fa-trash-can" @click="handleDeleteImageRequest" /> </div> - + </div> </template> <script> -import CommonUtil from '@/components/CommonUtil'; -import imageCompression from 'browser-image-compression'; + import CommonUtil from '@/components/CommonUtil'; + import imageCompression from 'browser-image-compression'; -export default { - name: "ObservationIllustration", - components: {CommonUtil}, - props: {observationIllustration: Object}, - data() { - return { - imageBase64Data: "", - maxImageSizeMB: 6.0 - }; - }, - watch: { - // We need to do this because the parent component (ObservationIllustrationBoard) - // re-uses the components, and when the prop (observationIllustration) changes, - // this does not trigger the mounted() function, since the component is already - // mounted - observationIllustration: function(val, oldVal){ - this.getObservationIllustrationDataFromStore(); - - }, - imageBase64Data(newVal, oldVal) - { - // Check image size and compress to max 10MB - if(newVal.length / 1024**2 > this.maxImageSizeMB) - { - console.info("Image is too big. Compressing"); - this.compressImage(); - } - } - }, - methods: { - async compressImage(){ - //https://ionicframework.com/blog/converting-a-base64-string-to-a-blob-in-javascript/ - const base64Response = await fetch(this.imageBase64Data); - const imageBlob = await base64Response.blob(); - //console.info(imageBlob); - // https://github.com/Donaldcwl/browser-image-compression - const blob = await imageCompression( - imageBlob, - { - maxSizeMB: this.maxImageSizeMB * 0.75 // Approximate size difference between jpg and base64 - } - ); - var convertBlobToBase64 = (blob) => new Promise((resolve, reject) => { - const reader = new FileReader; - reader.onerror = reject; - reader.onload = () => { - resolve(reader.result); - }; - reader.readAsDataURL(blob); - }); - - const base64String = await convertBlobToBase64(blob); - this.imageBase64Data = base64String; - this.updateImageInIndexedDB(); - //console.info("New image size=" + this.imageBase64Data.length / 1024**2); + export default { + name: "ObservationIllustration", + components: {CommonUtil}, + props: {observationIllustration: Object}, + data() { + return { + imageBase64Data: "", + maxImageSizeMB: 6.0 + }; }, - handleDeleteImageRequest(){ - if(confirm(this.$i18n.t("photo.modal.deleteprompt"))) - { - this.deleteImageFromIndexedDB(); - } - }, - // This is called after a successful file data deletion from IndexedDB - propagateMetaDataDelete() { - // We handle this in the parent component - this.$emit("deleteObservationIllustrationMetaData", this.observationIllustration.observationIllustrationPK); - }, - handleImageClick() { - this.$emit("illustrationClicked", this.imageBase64Data); - document.getElementById("observationModal").style.display="block"; - }, - /* Fetch the image data (base64 encoded) from the IndexedDB*/ - getObservationIllustrationDataFromStore() - { - - let isDBStoreNotAvailable = null; - let dbRequest = indexedDB.open(CommonUtil.CONST_DB_NAME, CommonUtil.CONST_DB_VERSION); - this.checkDBUpgrade(dbRequest,CommonUtil.CONST_DB_ENTITY_PHOTO); - dbRequest.onsuccess = (evt) =>{ - let db = evt.target.result; - if(db.objectStoreNames.contains(CommonUtil.CONST_DB_ENTITY_PHOTO)) - { - let transaction = db.transaction([CommonUtil.CONST_DB_ENTITY_PHOTO],'readwrite'); - let objectstore = transaction.objectStore(CommonUtil.CONST_DB_ENTITY_PHOTO); - if(this.observationIllustration.observationIllustrationPK.fileName) - { - let objectstoreRequest = objectstore.get(this.observationIllustration.observationIllustrationPK.fileName); - objectstoreRequest.onsuccess = (event) => - { - let observationImage = event.target.result; - if(observationImage) - { - //console.info("Rendering image data for file " + observationImage.illustration.fileName); - this.imageBase64Data = observationImage.illustration.imageTextData; - } - else - { - console.log('Image filename mentioned in Observation, but no image data found'); - } - }; - } - else - { - // Pass?? - } - } - else - { - db.close(); - isDBStoreNotAvailable = true; + watch: { + // We need to do this because the parent component (ObservationIllustrationBoard) + // re-uses the components, and when the prop (observationIllustration) changes, + // this does not trigger the mounted() function, since the component is already + // mounted + observationIllustration: function (val, oldVal) { + this.getObservationIllustrationDataFromStore(); + + }, + imageBase64Data(newVal, oldVal) { + // Check image size and compress to max 10MB + if (newVal.length / 1024 ** 2 > this.maxImageSizeMB) { + console.info("Image is too big. Compressing"); + this.compressImage(); } } - if(isDBStoreNotAvailable) - { - this.checkDBUpgrade(dbRequest,CommonUtil.CONST_DB_ENTITY_PHOTO); - } - }, - /** Remove the image data from IndexedDB */ - deleteImageFromIndexedDB() - { - let dbRequest = indexedDB.open(CommonUtil.CONST_DB_NAME, CommonUtil.CONST_DB_VERSION); - dbRequest.onsuccess = (evt) => { - let db = evt.target.result; - let transaction = db.transaction([CommonUtil.CONST_DB_ENTITY_PHOTO],'readwrite'); - let objectstore = transaction.objectStore(CommonUtil.CONST_DB_ENTITY_PHOTO); - let objectStoreRequest = objectstore.delete(this.observationIllustration.observationIllustrationPK.fileName); - this.propagateMetaDataDelete(); - } }, - updateImageInIndexedDB() - { - let dbRequest = indexedDB.open(CommonUtil.CONST_DB_NAME, CommonUtil.CONST_DB_VERSION); - dbRequest.onsuccess = (evt) => { - let db = evt.target.result; - let transaction = db.transaction([CommonUtil.CONST_DB_ENTITY_PHOTO],'readwrite'); - let objectstore = transaction.objectStore(CommonUtil.CONST_DB_ENTITY_PHOTO); - let objectstoreRequest = objectstore.get(this.observationIllustration.observationIllustrationPK.fileName); - objectstoreRequest.onsuccess = (event) => - { - let observationImage = event.target.result; - if(observationImage) + methods: { + async compressImage() { + //https://ionicframework.com/blog/converting-a-base64-string-to-a-blob-in-javascript/ + const base64Response = await fetch(this.imageBase64Data); + const imageBlob = await base64Response.blob(); + //console.info(imageBlob); + // https://github.com/Donaldcwl/browser-image-compression + const blob = await imageCompression( + imageBlob, { - //console.info("Rendering image data for file " + observationImage.illustration.fileName); - observationImage.illustration.imageTextData = this.imageBase64Data; - transaction.objectStore(CommonUtil.CONST_DB_ENTITY_PHOTO).put(observationImage, observationImage.illustration.fileName).onsuccess = (event) => { - //console.info("Image data was successfully updated!"); - }; + maxSizeMB: this.maxImageSizeMB * 0.75 // Approximate size difference between jpg and base64 } - else - { - console.log('Image filename mentioned in Observation, but no image data found'); + ); + var convertBlobToBase64 = (blob) => new Promise((resolve, reject) => { + const reader = new FileReader; + reader.onerror = reject; + reader.onload = () => { + resolve(reader.result); + }; + reader.readAsDataURL(blob); + }); + + const base64String = await convertBlobToBase64(blob); + this.imageBase64Data = base64String; + this.updateImageInIndexedDB(); + //console.info("New image size=" + this.imageBase64Data.length / 1024**2); + }, + handleDeleteImageRequest() { + if (confirm(this.$i18n.t("photo.modal.deleteprompt"))) { + this.deleteImageFromIndexedDB(); + } + }, + // This is called after a successful file data deletion from IndexedDB + propagateMetaDataDelete() { + // We handle this in the parent component + this.$emit("deleteObservationIllustrationMetaData", this.observationIllustration.observationIllustrationPK); + }, + handleImageClick() { + this.$emit("illustrationClicked", this.imageBase64Data); + document.getElementById("observationModal").style.display = "block"; + }, + /* Fetch the image data (base64 encoded) from the IndexedDB*/ + getObservationIllustrationDataFromStore() { + + let isDBStoreNotAvailable = null; + let dbRequest = indexedDB.open(CommonUtil.CONST_DB_NAME, CommonUtil.CONST_DB_VERSION); + this.checkDBUpgrade(dbRequest, CommonUtil.CONST_DB_ENTITY_PHOTO); + dbRequest.onsuccess = (evt) => { + let db = evt.target.result; + if (db.objectStoreNames.contains(CommonUtil.CONST_DB_ENTITY_PHOTO)) { + let transaction = db.transaction([CommonUtil.CONST_DB_ENTITY_PHOTO], 'readwrite'); + let objectstore = transaction.objectStore(CommonUtil.CONST_DB_ENTITY_PHOTO); + if (this.observationIllustration.observationIllustrationPK.fileName) { + let objectstoreRequest = objectstore.get(this.observationIllustration.observationIllustrationPK.fileName); + objectstoreRequest.onsuccess = (event) => { + let observationImage = event.target.result; + if (observationImage) { + //console.info("Rendering image data for file " + observationImage.illustration.fileName); + this.imageBase64Data = observationImage.illustration.imageTextData; + } + else { + console.log('Image filename mentioned in Observation, but no image data found'); + } + }; + } + else { + // Pass?? + } } - }; - } - }, - /** Check requirement for DB upgrade */ - checkDBUpgrade(dbRequest,entityName, keyName){ - dbRequest.onupgradeneeded= function (event) - { - let db = event.target.result; - if( !db.objectStoreNames.contains(entityName)){ - let store = null; - if(keyName) - { - store = db.createObjectStore(entityName, {keypath : keyName}); + else { + db.close(); + isDBStoreNotAvailable = true; } - else - { - store = db.createObjectStore(entityName); + } + if (isDBStoreNotAvailable) { + this.checkDBUpgrade(dbRequest, CommonUtil.CONST_DB_ENTITY_PHOTO); + } + }, + /** Remove the image data from IndexedDB */ + deleteImageFromIndexedDB() { + let dbRequest = indexedDB.open(CommonUtil.CONST_DB_NAME, CommonUtil.CONST_DB_VERSION); + dbRequest.onsuccess = (evt) => { + let db = evt.target.result; + let transaction = db.transaction([CommonUtil.CONST_DB_ENTITY_PHOTO], 'readwrite'); + let objectstore = transaction.objectStore(CommonUtil.CONST_DB_ENTITY_PHOTO); + let objectStoreRequest = objectstore.delete(this.observationIllustration.observationIllustrationPK.fileName); + this.propagateMetaDataDelete(); + } + }, + updateImageInIndexedDB() { + let dbRequest = indexedDB.open(CommonUtil.CONST_DB_NAME, CommonUtil.CONST_DB_VERSION); + dbRequest.onsuccess = (evt) => { + let db = evt.target.result; + let transaction = db.transaction([CommonUtil.CONST_DB_ENTITY_PHOTO], 'readwrite'); + let objectstore = transaction.objectStore(CommonUtil.CONST_DB_ENTITY_PHOTO); + let objectstoreRequest = objectstore.get(this.observationIllustration.observationIllustrationPK.fileName); + objectstoreRequest.onsuccess = (event) => { + let observationImage = event.target.result; + if (observationImage) { + //console.info("Rendering image data for file " + observationImage.illustration.fileName); + observationImage.illustration.imageTextData = this.imageBase64Data; + transaction.objectStore(CommonUtil.CONST_DB_ENTITY_PHOTO).put(observationImage, observationImage.illustration.fileName).onsuccess = (event) => { + //console.info("Image data was successfully updated!"); + }; + } + else { + console.log('Image filename mentioned in Observation, but no image data found'); + } + }; + } + }, + /** Check requirement for DB upgrade */ + checkDBUpgrade(dbRequest, entityName, keyName) { + dbRequest.onupgradeneeded = function (event) { + let db = event.target.result; + if (!db.objectStoreNames.contains(entityName)) { + let store = null; + if (keyName) { + store = db.createObjectStore(entityName, {keypath: keyName}); + } + else { + store = db.createObjectStore(entityName); + } + store.createIndex('observationId', 'observationId', {unique: false}); + store.createIndex('organismId', 'organismId', {unique: false}); } - store.createIndex('observationId', 'observationId', { unique: false }); - store.createIndex('organismId', 'organismId', { unique: false }); } } + }, + + mounted() { + this.getObservationIllustrationDataFromStore() } - }, - - mounted() { - this.getObservationIllustrationDataFromStore() - } -}; + }; </script> <style scoped> diff --git a/src/components/ObservationIllustrationBoard.vue b/src/components/ObservationIllustrationBoard.vue index f91c8a4c99945697a49305f86020b472d88822e3..b35a16d719b02627b8bb5a66f0f66c65c19f4d39 100644 --- a/src/components/ObservationIllustrationBoard.vue +++ b/src/components/ObservationIllustrationBoard.vue @@ -1,4 +1,4 @@ - <!-- +<!-- Copyright (c) 2022 NIBIO <http://www.nibio.no/>. @@ -23,225 +23,217 @@ <template> <div class="row"> - <div class="col-6"><font-awesome-icon style="font-size: x-large; vertical-align: middle;" icon="camera" /> - <a class="vips-btn medium" id="cameraLauncher" ref='cameraLauncher' @click="launchCamera">{{ $t("photo.label.takephoto") }}</a> + <div class="col-6"> + <font-awesome-icon style="font-size: x-large; vertical-align: middle;" icon="camera" /> + <a class="vips-btn medium" id="cameraLauncher" ref='cameraLauncher' + @click="launchCamera">{{ $t("photo.label.takephoto") }}</a> </div> - <div class="col-6"><font-awesome-icon style="font-size: x-large; vertical-align: middle;" icon="file-image" /> - <a type="button" class="vips-btn medium" id="cameraLauncher" ref='cameraLauncher' @click="launchGallery">{{ $t("photo.label.selectphoto") }}</a> + <div class="col-6"> + <font-awesome-icon style="font-size: x-large; vertical-align: middle;" icon="file-image" /> + <a type="button" class="vips-btn medium" id="cameraLauncher" ref='cameraLauncher' + @click="launchGallery">{{ $t("photo.label.selectphoto") }}</a> </div> <div class="container"> <div class="row" v-for="observationIllustrationPair in observationIllustrationPairs"> - <div class="col-6" v-for="observationIllustration in observationIllustrationPair" > - <observation-illustration :observationIllustration="observationIllustration" @deleteObservationIllustrationMetaData="deleteObservationIllustrationMetaData" @illustrationClicked="setModalImage"/> + <div class="col-6" v-for="observationIllustration in observationIllustrationPair"> + <observation-illustration :observationIllustration="observationIllustration" + @deleteObservationIllustrationMetaData="deleteObservationIllustrationMetaData" + @illustrationClicked="setModalImage" /> </div> </div> </div> <div class="centeredModal" id="observationModal" onclick="this.style.display='none';"> <img :src="modalImageBase64Data" class="img-fluid" /></div> - </div> </div> - + </div> + </template> <script> -import CommonUtil from '@/components/CommonUtil' -import ObservationIllustration from "@/components/ObservationIllustration" + import CommonUtil from '@/components/CommonUtil' + import ObservationIllustration from "@/components/ObservationIllustration" -export default { - name: "ObservationIllustrationBoard", - components: {ObservationIllustration, CommonUtil}, - props: { - initialObservationIllustrations: Array, - observationId: Number - }, - data() { - return { - observationIllustrationPairs: [], - observationIllustrations: [], - modalImageBase64Data: "" - }; - }, - methods: { - // Triggered by the delete event in the ObservationIllustration component - deleteObservationIllustrationMetaData: function(observationIllustrationPK) { - // The ObservationIllustration file data has already been deleted by the component - // Mark the meta data as deleted from the current observation - // It will be removed on the next sync - var updatedList = []; - for(var i=0;i<this.observationIllustrations.length;i++) - { - if(this.observationIllustrations[i].observationIllustrationPK.fileName == observationIllustrationPK.fileName) - { - this.observationIllustrations[i].deleted = true; - } - updatedList.push(this.observationIllustrations[i]); - } - this.observationIllustrations = updatedList; - this.organizeIllustrationsForRendering(); - // Propagate change up to parent Observation Object - this.$emit("observationIllustrationSetUpdated", this.observationIllustrations); + export default { + name: "ObservationIllustrationBoard", + components: {ObservationIllustration, CommonUtil}, + props: { + initialObservationIllustrations: Array, + observationId: Number }, - setModalImage: function(imageBase64Data){ - this.modalImageBase64Data = imageBase64Data; + data() { + return { + observationIllustrationPairs: [], + observationIllustrations: [], + modalImageBase64Data: "" + }; }, - /** Camera plugin Launcher */ - launchCamera: function() { - if(navigator.camera) - { - console.info("The camera should launch now"); - navigator.camera.getPicture(this.addIllustration, this.onFail, { + methods: { + // Triggered by the delete event in the ObservationIllustration component + deleteObservationIllustrationMetaData: function (observationIllustrationPK) { + // The ObservationIllustration file data has already been deleted by the component + // Mark the meta data as deleted from the current observation + // It will be removed on the next sync + var updatedList = []; + for (var i = 0; i < this.observationIllustrations.length; i++) { + if (this.observationIllustrations[i].observationIllustrationPK.fileName == observationIllustrationPK.fileName) { + this.observationIllustrations[i].deleted = true; + } + updatedList.push(this.observationIllustrations[i]); + } + this.observationIllustrations = updatedList; + this.organizeIllustrationsForRendering(); + // Propagate change up to parent Observation Object + this.$emit("observationIllustrationSetUpdated", this.observationIllustrations); + }, + setModalImage: function (imageBase64Data) { + this.modalImageBase64Data = imageBase64Data; + }, + /** Camera plugin Launcher */ + launchCamera: function () { + if (navigator.camera) { + console.info("The camera should launch now"); + navigator.camera.getPicture(this.addIllustration, this.onFail, { quality: 50, destinationType: Camera.DestinationType.DATA_URL, //FILE_URI correctOrientation: true, - }); - } - else - { - console.log('WARNING : Functional Cordova plugin needed to launch camera'); - } - }, - /** Image gallery launcher with same camera plugin */ - launchGallery : function() { - if(navigator.camera) - { - console.info("Other photo location should launch now"); - navigator.camera.getPicture(this.addIllustration, this.onFail, { - quality: 50, - sourceType: Camera.PictureSourceType.PHOTOLIBRARY, - destinationType: Camera.DestinationType.DATA_URL, //FILE_URI - correctOrientation: true, - }); - } - else - { - console.log('WARNING : Functional Cordova camera plugin needed to launch gallery'); - } - }, - - // Should probably be commented before building - mockLaunchCamera: function(){ - var imageTextData="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"; - this.addIllustration(imageTextData); - }, - // Callback for launchCamera and launchGallery - addIllustration: function(imageTextData){ - var dbRequest = indexedDB.open(CommonUtil.CONST_DB_NAME, CommonUtil.CONST_DB_VERSION); - // This stuff is asynchronous... - dbRequest.onsuccess = (evt) => { - var db = evt.target.result; - // Get all registered fileNames, and find an available filename - var fileName = this.observationId + "_illustration.jpg"; - if(db.objectStoreNames.contains(CommonUtil.CONST_DB_ENTITY_PHOTO)) - { - var transaction = db.transaction([CommonUtil.CONST_DB_ENTITY_PHOTO],'readwrite') - var objectStore = transaction.objectStore(CommonUtil.CONST_DB_ENTITY_PHOTO); - // Finding an available filename - var request = objectStore.getAllKeys().onsuccess = (event) => { - //console.info("All keys?"); - // Array(4) [ "20799_illustration.jpg", "20801_illustration.jpg", "20801_illustration_1.jpg", "20801_illustration_2.jpg" ] - //console.info(event.target.result); //DETTE funker!!! - - var existingFileNames = event.target.result; - var counter = 1; - while(existingFileNames.indexOf(fileName) >= 0) - { - fileName = this.observationId + "_illustration_" + (counter++) + ".jpg"; - } - // Create the illustration object for storage in IndexedDB - var newIndexedDBIllustration = { - observationId: this.observationId, - deleted: false, - illustration: { - fileName: fileName, - imageTextData: "data:image/jpeg;base64," + imageTextData - } - }; - // Store the image in IndexedDB - transaction.objectStore(CommonUtil.CONST_DB_ENTITY_PHOTO).add(newIndexedDBIllustration, newIndexedDBIllustration.illustration.fileName).onsuccess = (event) => { - // Create an item for the ObservationIllustrationSet - var newObservationIllustration = { - observationIllustrationPK: { - observationId: this.observationId, - fileName: fileName - }, - uploaded: false + }); + } + else { + console.log('WARNING : Functional Cordova plugin needed to launch camera'); + } + }, + /** Image gallery launcher with same camera plugin */ + launchGallery: function () { + if (navigator.camera) { + console.info("Other photo location should launch now"); + navigator.camera.getPicture(this.addIllustration, this.onFail, { + quality: 50, + sourceType: Camera.PictureSourceType.PHOTOLIBRARY, + destinationType: Camera.DestinationType.DATA_URL, //FILE_URI + correctOrientation: true, + }); + } + else { + console.log('WARNING : Functional Cordova camera plugin needed to launch gallery'); + } + }, + + // Should probably be commented before building + mockLaunchCamera: function () { + var imageTextData = "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"; + this.addIllustration(imageTextData); + }, + // Callback for launchCamera and launchGallery + addIllustration: function (imageTextData) { + var dbRequest = indexedDB.open(CommonUtil.CONST_DB_NAME, CommonUtil.CONST_DB_VERSION); + // This stuff is asynchronous... + dbRequest.onsuccess = (evt) => { + var db = evt.target.result; + // Get all registered fileNames, and find an available filename + var fileName = this.observationId + "_illustration.jpg"; + if (db.objectStoreNames.contains(CommonUtil.CONST_DB_ENTITY_PHOTO)) { + var transaction = db.transaction([CommonUtil.CONST_DB_ENTITY_PHOTO], 'readwrite') + var objectStore = transaction.objectStore(CommonUtil.CONST_DB_ENTITY_PHOTO); + // Finding an available filename + var request = objectStore.getAllKeys().onsuccess = (event) => { + //console.info("All keys?"); + // Array(4) [ "20799_illustration.jpg", "20801_illustration.jpg", "20801_illustration_1.jpg", "20801_illustration_2.jpg" ] + //console.info(event.target.result); //DETTE funker!!! + + var existingFileNames = event.target.result; + var counter = 1; + while (existingFileNames.indexOf(fileName) >= 0) { + fileName = this.observationId + "_illustration_" + (counter++) + ".jpg"; } - // Add the component to the ObservationIllustrationBoard - this.observationIllustrations.push(newObservationIllustration); - this.$emit("observationIllustrationSetUpdated", this.observationIllustrations); + // Create the illustration object for storage in IndexedDB + var newIndexedDBIllustration = { + observationId: this.observationId, + deleted: false, + illustration: { + fileName: fileName, + imageTextData: "data:image/jpeg;base64," + imageTextData + } + }; + // Store the image in IndexedDB + transaction.objectStore(CommonUtil.CONST_DB_ENTITY_PHOTO).add(newIndexedDBIllustration, newIndexedDBIllustration.illustration.fileName).onsuccess = (event) => { + // Create an item for the ObservationIllustrationSet + var newObservationIllustration = { + observationIllustrationPK: { + observationId: this.observationId, + fileName: fileName + }, + uploaded: false + } + // Add the component to the ObservationIllustrationBoard + this.observationIllustrations.push(newObservationIllustration); + this.$emit("observationIllustrationSetUpdated", this.observationIllustrations); + }; }; - }; - } + } - - - // Add the component to the ObservationIllustrationBoard - // Mark the observation as "InNeedOfSync" or similar... - } - }, - onFail: function(message) { - alert(message); - }, - fileNameExists: function(fileName){ - - }, - organizeIllustrationsForRendering: function() { - if(this.observationIllustrations == undefined) - { - return; - } - var obsIllusPair = []; - var tempObsIllusPairs = []; - var counter = 0; - for(var i=0;i<this.observationIllustrations.length;i++) - { - if(!this.observationIllustrations[i].deleted) - { - obsIllusPair.push(this.observationIllustrations[i]); + + + // Add the component to the ObservationIllustrationBoard + // Mark the observation as "InNeedOfSync" or similar... } - if(obsIllusPair.length == 2 || i == this.observationIllustrations.length-1) - { - tempObsIllusPairs.push(obsIllusPair); - obsIllusPair = []; + }, + onFail: function (message) { + alert(message); + }, + fileNameExists: function (fileName) { + + }, + organizeIllustrationsForRendering: function () { + if (this.observationIllustrations == undefined) { + return; } - if(!this.observationIllustrations[i].deleted) - { - counter++; + var obsIllusPair = []; + var tempObsIllusPairs = []; + var counter = 0; + for (var i = 0; i < this.observationIllustrations.length; i++) { + if (!this.observationIllustrations[i].deleted) { + obsIllusPair.push(this.observationIllustrations[i]); + } + if (obsIllusPair.length == 2 || i == this.observationIllustrations.length - 1) { + tempObsIllusPairs.push(obsIllusPair); + obsIllusPair = []; + } + if (!this.observationIllustrations[i].deleted) { + counter++; + } } + this.observationIllustrationPairs = tempObsIllusPairs; } - this.observationIllustrationPairs = tempObsIllusPairs; - } - }, - mounted(){ - // Copying to manipulate on instance - this.observationIllustrations = this.initialObservationIllustrations; - this.organizeIllustrationsForRendering(); - //console.info("ObservationIllustrationBoard mounted. ObservationId=" + this.observationId); - }, - watch: { - initialObservationIllustrations: function(val) - { + }, + mounted() { + // Copying to manipulate on instance this.observationIllustrations = this.initialObservationIllustrations; this.organizeIllustrationsForRendering(); + //console.info("ObservationIllustrationBoard mounted. ObservationId=" + this.observationId); + }, + watch: { + initialObservationIllustrations: function (val) { + this.observationIllustrations = this.initialObservationIllustrations; + this.organizeIllustrationsForRendering(); + } } - } -}; + }; </script> <style scoped> -.centeredModal { - position: fixed; - display: none; - width: 95%; - padding: 10px; - border: 2px solid #3d8052; - border-radius: 5px; - background-color: white; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} -</style> + .centeredModal { + position: fixed; + display: none; + width: 95%; + padding: 10px; + border: 2px solid #3d8052; + border-radius: 5px; + background-color: white; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } +</style> \ No newline at end of file diff --git a/src/components/ObservationList.vue b/src/components/ObservationList.vue index bb62642907aded1eeac3d5e8f7edd4e7a41d420f..48f420eb2c3a8d4291e87d0800bc304da0a6e348 100644 --- a/src/components/ObservationList.vue +++ b/src/components/ObservationList.vue @@ -1,4 +1,4 @@ - <!-- +<!-- Copyright (c) 2022 NIBIO <http://www.nibio.no/>. @@ -22,136 +22,134 @@ --> <template> -<div> - <div class="addItem"> - <router-link to="/observation" > - <button type="button" class="btn btn-primary">+</button> - </router-link> - </div> - - <div v-if="observations && observations.length > 0" class="bg-light"> - <ul class="list-group bg-light" v-if="isInitialized"> - <!-- TODO: Remove the sync status classes (v-bind:class). Do it after sync has been automatized --> - <router-link :to="{name: 'Observation', params: {observationId:obs.observationId}}" class="list-group-item list-group-item-action bg-light" v-bind:class="{'text-danger':obs.isNew, 'text-primary':obs.toUpload, 'text-secondary':obs.isDeleted}" v-for="obs in sortedObservations" v-if="!obs.isDeleted" v-bind:key="obs.observationId"> - <div v-if="obs.isDeleted"> - <strike> {{ obs.timeOfObservation | dateFormat }} <b>{{obs.observationHeading}}</b> </strike> - </div> - <div v-else> - {{ obs.timeOfObservation | dateFormat }} <b>{{obs.observationHeading}}</b> - </div> - </router-link > - - </ul> - </div> - <div v-else class="alert alert-info" role="alert">{{ $t("observationlist.alert.emptylist")}}</div> - <common-util ref="CommonUtil"/> - <sync ref="Sync" /> -</div> + <div> + <div class="addItem"> + <router-link to="/observation"> + <button type="button" class="btn btn-primary">+</button> + </router-link> + </div> + + <div v-if="observations && observations.length > 0" class="bg-light"> + <ul class="list-group bg-light" v-if="isInitialized"> + <!-- TODO: Remove the sync status classes (v-bind:class). Do it after sync has been automatized --> + <router-link :to="{name: 'Observation', params: {observationId:obs.observationId}}" + class="list-group-item list-group-item-action bg-light" + v-bind:class="{'text-danger':obs.isNew, 'text-primary':obs.toUpload, 'text-secondary':obs.isDeleted}" + v-for="obs in sortedObservations" v-if="!obs.isDeleted" v-bind:key="obs.observationId"> + <div v-if="obs.isDeleted"> + <strike> {{ obs.timeOfObservation | dateFormat }} <b>{{obs.observationHeading}}</b> </strike> + </div> + <div v-else> + {{ obs.timeOfObservation | dateFormat }} <b>{{obs.observationHeading}}</b> + </div> + </router-link> + + </ul> + </div> + <div v-else class="alert alert-info" role="alert">{{ $t("observationlist.alert.emptylist")}}</div> + <common-util ref="CommonUtil" /> + <sync ref="Sync" /> + </div> </template> <script> -import CommonUtil from '@/components/CommonUtil' -import Sync from '@/components/Sync' -import { DateTime } from 'luxon' + import CommonUtil from '@/components/CommonUtil' + import Sync from '@/components/Sync' + import {DateTime} from 'luxon' -export default { - name: "ObservationList", - data() { - return { - /*msg: 'Startsiden'*/ - isInitialized : false, - CONST_URL_DOMAIN : '', - observations : undefined, - }; - }, - components : {CommonUtil,Sync}, - methods : { - /** Extract observations from localstorage */ - getObservationsFromStore() - { - this.isInitialized = true; - if(localStorage.getItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST)) - { - let strObservations = localStorage.getItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST); - let lstObservations = JSON.parse(strObservations); - lstObservations.forEach(function(observation){ - if(observation.uploaded==false) - { - if(observation.deleted) - { - observation.isDeleted = true; - } - else{ - if(observation.observationId < 0) - { - observation.isNew = true; - } - else{ - observation.toUpload = true; - } - } + export default { + name: "ObservationList", + data() { + return { + /*msg: 'Startsiden'*/ + isInitialized: false, + CONST_URL_DOMAIN: '', + observations: undefined, + }; + }, + components: {CommonUtil, Sync}, + methods: { + /** Extract observations from localstorage */ + getObservationsFromStore() { + this.isInitialized = true; + if (localStorage.getItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST)) { + let strObservations = localStorage.getItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST); + let lstObservations = JSON.parse(strObservations); + lstObservations.forEach(function (observation) { + if (observation.uploaded == false) { + if (observation.deleted) { + observation.isDeleted = true; + } + else { + if (observation.observationId < 0) { + observation.isNew = true; + } + else { + observation.toUpload = true; + } + } - } + } - }); - this.observations = lstObservations; - } + }); + this.observations = lstObservations; + } - }, + }, - }, - computed : { - sortedObservations : function() { - this.observations.sort( (a,b) => { - return new Date(b.timeOfObservation) - new Date(a.timeOfObservation); - }); + }, + computed: { + sortedObservations: function () { + this.observations.sort((a, b) => { + return new Date(b.timeOfObservation) - new Date(a.timeOfObservation); + }); - return this.observations; - } - }, - filters: { - dateFormat: function(timeStr) { - //return DateTime.fromISO(timeStr).toISODate(); -//return timeStr; - return DateTime.fromISO(timeStr).toISODate(); - } - }, - mounted() { - CommonUtil.setHeaderTitle(this.$i18n.t("observationlist.heading")); - this.CONST_URL_DOMAIN = CommonUtil.CONST_URL_DOMAIN; - let strUUID = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID); - if(strUUID) - { - this.getObservationsFromStore(); + return this.observations; + } + }, + filters: { + dateFormat: function (timeStr) { + //return DateTime.fromISO(timeStr).toISODate(); + //return timeStr; + return DateTime.fromISO(timeStr).toISODate(); + } + }, + mounted() { + CommonUtil.setHeaderTitle(this.$i18n.t("observationlist.heading")); + this.CONST_URL_DOMAIN = CommonUtil.CONST_URL_DOMAIN; + let strUUID = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID); + if (strUUID) { + this.getObservationsFromStore(); + } + // Making it globally available, so that e.g. the Sync component can update the list + // calling e.g. this.$root.sharedState.observationListComponent.getObservationsFromStore(); + this.$root.sharedState.observationListComponent = this; } - // Making it globally available, so that e.g. the Sync component can update the list - // calling e.g. this.$root.sharedState.observationListComponent.getObservationsFromStore(); - this.$root.sharedState.observationListComponent = this; } -} </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> - h1, h2 { - font-weight: normal; + font-weight: normal; } + ul { - list-style-type: none; - padding: 0; + list-style-type: none; + padding: 0; } + li { - display: inline; - margin: 0 0; + display: inline; + margin: 0 0; } + a { color: black; border-radius: 0 !important; border-width: 0px 0px 1px 0px !important; } - </style> \ No newline at end of file diff --git a/src/components/PlacesList.vue b/src/components/PlacesList.vue index 1d2b9c4b4b8251671f56d4dca50a435fe546dae6..bf59adfdc219dd6a9be721d35e7854ed35e5570a 100644 --- a/src/components/PlacesList.vue +++ b/src/components/PlacesList.vue @@ -1,4 +1,4 @@ - <!-- +<!-- Copyright (c) 2022 NIBIO <http://www.nibio.no/>. @@ -22,139 +22,137 @@ --> <template> - <div> - <div class="addItem poiList"> - <router-link to="/mapPOI" > - <button type="button" class="btn btn-primary">+</button> - </router-link> - </div> - <div> - <div class="row form-group" style="margin-left:0px;"> - <div><input class="form-control" type="text" v-model.trim="textSearch" :placeholder="$t('places.search.label')" @input="search"> </div> - </div> - <!-- TODO: Remove the sync status classes (v-bind:class). Do it after sync has been automatized --> - <router-link class="list-group-item list-group-item-action bg-light" ref='linkMapPoi' :to="{name:'MapPOI', params: {pointOfInterestId:poi.pointOfInterestId}}" v-bind:class="{'text-danger':poi.isNew, 'text-primary':poi.toUpload, 'text-secondary':poi.isDeleted}" v-for="poi in listPOI" v-bind:key="poi.pointOfInterestId"> - <div v-if="poi.isDeleted" class="row"> + <div> + <div class="addItem poiList"> + <router-link to="/mapPOI"> + <button type="button" class="btn btn-primary">+</button> + </router-link> + </div> + <div> + <div class="row form-group" style="margin-left:0px;"> + <div><input class="form-control" type="text" v-model.trim="textSearch" + :placeholder="$t('places.search.label')" @input="search"> </div> + </div> + <!-- TODO: Remove the sync status classes (v-bind:class). Do it after sync has been automatized --> + <router-link class="list-group-item list-group-item-action bg-light" ref='linkMapPoi' + :to="{name:'MapPOI', params: {pointOfInterestId:poi.pointOfInterestId}}" + v-bind:class="{'text-danger':poi.isNew, 'text-primary':poi.toUpload, 'text-secondary':poi.isDeleted}" + v-for="poi in listPOI" v-bind:key="poi.pointOfInterestId"> + <div v-if="poi.isDeleted" class="row"> <div class="col-9"> - <strike>{{poi.name}}</strike> + <strike>{{poi.name}}</strike> </div> <div class="col-3"></div> - </div> - <div v-else class="row"> + </div> + <div v-else class="row"> <div class="col-9"> - <div>{{poi.name}}</div> + <div>{{poi.name}}</div> </div> <div class="col-3"> - <router-link :to="{name: 'Observation', params: {locationPointOfInterestId:poi.pointOfInterestId, paramGeoinfo: JSON.parse(poi.geoJSON)}}" class="addobs-btn"><font-awesome-icon style="font-size: x-large; color: #3d8052; " icon="magnifying-glass-plus" /></i></router-link> - </div> - </div> - </router-link> - </div> - <common-util ref="CommonUtil"/> - </div> + <router-link + :to="{name: 'Observation', params: {locationPointOfInterestId:poi.pointOfInterestId, paramGeoinfo: JSON.parse(poi.geoJSON)}}" + class="addobs-btn"> + <font-awesome-icon style="font-size: x-large; color: #3d8052; " + icon="magnifying-glass-plus" /></i></router-link> + </div> + </div> + </router-link> + </div> + <common-util ref="CommonUtil" /> + </div> </template> <script> -import CommonUtil from '@/components/CommonUtil' + import CommonUtil from '@/components/CommonUtil' -export default { - name: 'PlacesList', - components : {CommonUtil}, - data () { - return { - CONST_URL_DOMAIN : '', - listPOI : [], - textSearch : null, - } - }, + export default { + name: 'PlacesList', + components: {CommonUtil}, + data() { + return { + CONST_URL_DOMAIN: '', + listPOI: [], + textSearch: null, + } + }, - - methods : { - fetchFromServer() - { - let strUUID = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID); - let jsonHeader = { Authorization: strUUID }; - fetch(this.CONST_URL_DOMAIN + CommonUtil.CONST_URL_USER_POI, { - method: "GET", - headers: jsonHeader, - }).then((response) => response.json()) - .then((data) => { - localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST,JSON.stringify(data)); - //this.getObservationsFromStore(); - }) + methods: { + fetchFromServer() { + let strUUID = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID); + let jsonHeader = {Authorization: strUUID}; + + fetch(this.CONST_URL_DOMAIN + CommonUtil.CONST_URL_USER_POI, { + method: "GET", + headers: jsonHeader, + }).then((response) => response.json()) + .then((data) => { + localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST, JSON.stringify(data)); + //this.getObservationsFromStore(); + }) + + }, + getPlacesList(lstPOI) { + lstPOI.forEach(function (poi) { + if (poi.uploaded === false) { + if (poi.deleted) { + poi.isDeleted = true; + } + else { + if (poi.pointOfInterestId < 0) { + poi.isNew = true; + } + else { + poi.toUpload = true; + } + } - }, - getPlacesList(lstPOI) - { - lstPOI.forEach(function(poi){ - if(poi.uploaded===false) - { - if(poi.deleted) - { - poi.isDeleted = true; - } - else{ - if(poi.pointOfInterestId < 0) - { - poi.isNew = true; - } - else{ - poi.toUpload = true; - } - } + } - } + }); + return lstPOI; + }, + /** Search POI */ + search() { + let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)); + if (this.textSearch) { + let This = this; - }); - return lstPOI; - }, - /** Search POI */ - search() - { - let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)); - if(this.textSearch) - { - let This = this; - - let lstPOISearch = lstPOI.filter(function (poi){ - let poiName = poi.name; - return poiName.toLowerCase().indexOf(This.textSearch.toLowerCase()) != -1; - }); - this.listPOI = this.getPlacesList(lstPOISearch) - } - else - { - this.listPOI = this.getPlacesList(lstPOI); - } - }, - getPOIListFromStore() - { - let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)); - this.listPOI = this.getPlacesList(lstPOI); + let lstPOISearch = lstPOI.filter(function (poi) { + let poiName = poi.name; + return poiName.toLowerCase().indexOf(This.textSearch.toLowerCase()) != -1; + }); + this.listPOI = this.getPlacesList(lstPOISearch) } - }, - - mounted() { - CommonUtil.setHeaderTitle(this.$i18n.t("placeslist.heading")); - this.CONST_URL_DOMAIN = CommonUtil.CONST_URL_DOMAIN; + else { + this.listPOI = this.getPlacesList(lstPOI); + } + }, + getPOIListFromStore() { + let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)); + this.listPOI = this.getPlacesList(lstPOI); + } + }, + + mounted() { + CommonUtil.setHeaderTitle(this.$i18n.t("placeslist.heading")); + this.CONST_URL_DOMAIN = CommonUtil.CONST_URL_DOMAIN; /* - let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)); - this.listPOI = this.getPlacesList(lstPOI); + let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)); + this.listPOI = this.getPlacesList(lstPOI); */ this.getPOIListFromStore(); - // Making it globally available, so that e.g. the Sync component can update the list - // calling e.g. this.$root.sharedState.placesListComponent.fooBar(); - this.$root.sharedState.placesListComponent = this; + // Making it globally available, so that e.g. the Sync component can update the list + // calling e.g. this.$root.sharedState.placesListComponent.fooBar(); + this.$root.sharedState.placesListComponent = this; - } -} + } + } </script> <style scoped> -a { + a { color: black; border-radius: 0 !important; border-width: 0px 0px 1px 0px !important; } - -</style> +</style> \ No newline at end of file diff --git a/src/components/Quantification.vue b/src/components/Quantification.vue index aaa0f28a840f4f0d4aa02c9705342e802596818b..e897803635e504d072b634b9316d09a0e4252cca 100644 --- a/src/components/Quantification.vue +++ b/src/components/Quantification.vue @@ -1,4 +1,4 @@ - <!-- +<!-- Copyright (c) 2022 NIBIO <http://www.nibio.no/>. @@ -22,71 +22,69 @@ --> <template> - <div> - <!-- <router-link v-show="iseditable" :to="{name:'Observation', params:{observationId:observation_Id}}">Back</router-link> --> - <div v-if="isMounted"> - <FormSchema :schema="observationDataSchema" v-model="observationData" @change="change"></FormSchema> - </div> + <div> + <!-- <router-link v-show="iseditable" :to="{name:'Observation', params:{observationId:observation_Id}}">Back</router-link> --> + <div v-if="isMounted"> + <FormSchema :schema="observationDataSchema" v-model="observationData" @change="change"></FormSchema> + </div> - </div> + </div> </template> <script> -import CommonUtil from '@/components/CommonUtil' -import FormSchema from '@formschema/native' -export default { - props: ['observationId','organismId','isEditable','schemaData'], - components : {FormSchema}, - data(){ - return { - isMounted : false, - observation_Id : '', - organism_id : '', - iseditable : true, - observationDataSchema : {}, - observationData : {} - } - }, - methods : - { - change(){ - this.$emit('updateQuntificationData',this.observationData); - }, - initQuantification() - { - + import CommonUtil from '@/components/CommonUtil' + import FormSchema from '@formschema/native' + export default { + props: ['observationId', 'organismId', 'isEditable', 'schemaData'], + components: {FormSchema}, + data() { + return { + isMounted: false, + observation_Id: '', + organism_id: '', + iseditable: true, + observationDataSchema: {}, + observationData: {} + } + }, + methods: + { + change() { + this.$emit('updateQuntificationData', this.observationData); + }, + initQuantification() { - let pestList = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_PEST_LIST)); - let pest = pestList.find(({organismId}) => organismId === this.organism_id); - - this.observationDataSchema = JSON.parse(pest.observationDataSchema); - - } - }, - errorCaptured(err, vm, info) { - console.log('err : '+err); - console.log('vm : '+vm); - console.log('info : '+info); - }, - mounted(){ - this.isMounted = true; - this.observation_Id = (this.observationId) ? this.observationId : this.$route.params.observationId; - this.organism_id = (this.organismId) ? this.organismId : this.$route.params.organismId; - this.observationData = (this.schemaData) ? this.schemaData : (this.$route.params.schemaData) ? this.$route.params.schemaData:''; - + let pestList = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_PEST_LIST)); + let pest = pestList.find(({organismId}) => organismId === this.organism_id); - if(typeof(this.isEditable)==='undefined' ) - { - this.iseditable = true; - } - else{ - this.iseditable = false; - } + this.observationDataSchema = JSON.parse(pest.observationDataSchema); - this.initQuantification(); - //console.info(this.observationDataSchema); - }, + } + }, + errorCaptured(err, vm, info) { + console.log('err : ' + err); + console.log('vm : ' + vm); + console.log('info : ' + info); + }, + mounted() { + this.isMounted = true; + this.observation_Id = (this.observationId) ? this.observationId : this.$route.params.observationId; + this.organism_id = (this.organismId) ? this.organismId : this.$route.params.organismId; + this.observationData = (this.schemaData) ? this.schemaData : (this.$route.params.schemaData) ? this.$route.params.schemaData : ''; -} + + if (typeof (this.isEditable) === 'undefined') { + this.iseditable = true; + } + else { + this.iseditable = false; + } + + this.initQuantification(); + //console.info(this.observationDataSchema); + + }, + + } </script> \ No newline at end of file diff --git a/src/components/Settings.vue b/src/components/Settings.vue index 013a5ebf086bbd391046a19729027f6b09829f9f..1e3894fb28d76e06eacbbc2f705541a5fca2afab 100644 --- a/src/components/Settings.vue +++ b/src/components/Settings.vue @@ -1,4 +1,5 @@ -> <!-- +> +<!-- Copyright (c) 2022 NIBIO <http://www.nibio.no/>. @@ -22,38 +23,38 @@ --> <template> - <div class="container hello"> -<h1>Denne komponenten skal fjernes så snart Sync fungerer automatisk</h1> - + <div class="container hello"> + <h1>Denne komponenten skal fjernes så snart Sync fungerer automatisk</h1> - <sync ref="sync"/> - <div class="row"> - <button type="button" v-on:click="runSync" class="btn btn-outline-info">{{ $t("sync")}}</button> - </div> - <router-view/> - </div> + + <sync ref="sync" /> + <div class="row"> + <button type="button" v-on:click="runSync" class="btn btn-outline-info">{{ $t("sync")}}</button> + </div> + <router-view /> + </div> </template> <script> -import Sync from '@/components/Sync' -import CommonUtil from '@/components/CommonUtil' -export default { - components: { Sync }, - name: 'Settings', - data () { - return { - msg: this.$i18n.t("settings.heading") - } - }, - methods : { - runSync(){ - this.$refs.sync.syncTwoWay(); -/* this.$router.push("/").catch(()=>{}); - this.$router.go(); */ - }, - }, - mounted() { - CommonUtil.setHeaderTitle(this.$i18n.t("settings.heading")); + import Sync from '@/components/Sync' + import CommonUtil from '@/components/CommonUtil' + export default { + components: {Sync}, + name: 'Settings', + data() { + return { + msg: this.$i18n.t("settings.heading") + } + }, + methods: { + runSync() { + this.$refs.sync.syncTwoWay(); + /* this.$router.push("/").catch(()=>{}); + this.$router.go(); */ + }, + }, + mounted() { + CommonUtil.setHeaderTitle(this.$i18n.t("settings.heading")); + } } -} </script> \ No newline at end of file diff --git a/src/components/Sync.vue b/src/components/Sync.vue index 0e0336ae9605017df635b4f48dde4ec858f62c3f..df7daed5385e5f99225e3b10a3ab1f8cb9a30ad4 100644 --- a/src/components/Sync.vue +++ b/src/components/Sync.vue @@ -1,4 +1,4 @@ - <!-- +<!-- Copyright (c) 2022 NIBIO <http://www.nibio.no/>. @@ -22,1148 +22,1034 @@ --> <template> - <div> - <div v-if="loading"> - <div class="spinner-border text-success" role="status"> - - </div> <span class="text-danger"> Data Loading...</span> - <!-- -- Sync : <div v-html="isSyncNeeded"></div> --> - </div> - <div v-show="isMounted"><common-util ref="CommonUtil"/></div> - </div> + <div> + <div v-if="loading"> + <div class="spinner-border text-success" role="status"> + + </div> <span class="text-danger"> Data Loading...</span> + <!-- -- Sync : <div v-html="isSyncNeeded"></div> --> + </div> + <div v-show="isMounted"> + <common-util ref="CommonUtil" /> + </div> + </div> </template> <script> -import CommonUtil from "@/components/CommonUtil"; - -export default { - name: "Sync", - components : {CommonUtil}, - props: { - isSyncNeeded: Boolean - }, - data() { - return { - isMounted : false, - CONST_URL_DOMAIN : '', - booIsSyncOneWayReady : false, - booIsSyncTwoWayReady : false, - arrSyncOneWay : [ - {"name":CommonUtil.CONST_STORAGE_CROP_CATEGORY,"complete":false} , - {"name":CommonUtil.CONST_STORAGE_CROP_LIST,"complete":false} , - {"name":CommonUtil.CONST_STORAGE_PEST_LIST,"complete":false} , - {"name":CommonUtil.CONST_STORAGE_CROP_PEST_LIST,"complete":false} , - {"name":CommonUtil.CONST_STORAGE_VISIBILITY_POLYGON,"complete":false} , - ], - arrSyncTwoWay : [ - {"name":CommonUtil.CONST_STORAGE_OBSERVATION_LIST,"complete":false} , - {"name":CommonUtil.CONST_STORAGE_POI_LIST,"complete":false} , - - ], - appUser : {}, - loading : false, - counterTwoWaySyncPOST : 0, - totalTwoWaySyncPOST : 0, - counterTwoWaySyncPOST : 0, - }; - }, -/* computed: { - testSync : function(){ - console.log('computed : msg 1 from parent for sync : '+this.isSyncNeeded); - return this.isSyncNeeded; - } - }, -*/ - - watch : { - booIsSyncOneWayReady : - { - immediate : true, - handler (val, oldVal) - { - if(val) - { - - /* Starting of Sync */ - console.log('----- SYNC ONE WAY STARTED -----'); - this.syncOneWay(); - } - if(typeof(booIsSyncOneWayReady)===undefined) - { - this.loading = false; - /* refresh for next time */ - this.refreshOneWaySyncItems(); - } - if(typeof(booIsSyncOneWayReady)==false) - { - this.loading = false; - } - } - }, - booIsSyncTwoWayReady : - { - immediate : true, - handler(val, oldVal) - { - if(val) - { - this.loading = true; - console.log('----- SYNC TWO WAY STARTED -----'); - this.syncTwoWayAtLogin(); - this.loading = false; - } - } - } - - /* - isSyncNeeded : - { - immediate : true, - handler (val, oldVal) - { - console.log('watch 1 - prop changed : newval : '+val+' - oldval : '+oldVal); - }, - } - */ - }, - - methods: { - testFunction(){ - console.log('test child testFuntion'); - }, - - /** One way Sync. Fetching the data from server and stored in local storage */ - syncOneWay(){ - if (this.booIsSyncOneWayReady) - { - this.loading = true; - - let appUser = this.appUser; - let funStorageSet = this.oneWaySyncStorageSet; - - if(typeof(appUser)==undefined || appUser == null || appUser === '') - { - // TODO if appUser is not available - } - else - { let strUrl = ''; - let This = this; - $.each(this.arrSyncOneWay, function(index, value){ - switch(value.name) { - case CommonUtil.CONST_STORAGE_CROP_CATEGORY : - strUrl = This.CONST_URL_DOMAIN +CommonUtil.CONST_URL_CROP_CATEGORY+appUser.organization_id; - break; - case CommonUtil.CONST_STORAGE_CROP_LIST : - strUrl = This.CONST_URL_DOMAIN +CommonUtil.CONST_URL_CROP_LIST; - break; - case CommonUtil.CONST_STORAGE_PEST_LIST : - strUrl = This.CONST_URL_DOMAIN +CommonUtil.CONST_URL_PEST_LIST+'?organizationId='+appUser.organization_id; - break; - case CommonUtil.CONST_STORAGE_CROP_PEST_LIST : - strUrl = This.CONST_URL_DOMAIN +CommonUtil.CONST_URL_CROP_PEST_LIST; - break; - case CommonUtil.CONST_STORAGE_VISIBILITY_POLYGON : - strUrl = This.CONST_URL_DOMAIN +CommonUtil.CONST_URL_POLYGON_SERVICES+appUser.organization_id; - break; - default : - } - - funStorageSet(value,strUrl); - }); - } - - this.loading = false; - } - }, - - syncTwoWayAtLogin() - { - - let appUser = this.appUser; - let funStorageSet = this.oneWaySyncStorageSet; - - if(typeof(appUser)==undefined || appUser == null || appUser === '') - { - // TODO if appUser is not available - } - else - { - - this.syncTwoWay(); - } - }, - syncTwoWay() - { - // Run this only when we are online - if(navigator.onLine) - { - let strUrl = ''; - let This = this; - $.each(this.arrSyncTwoWay, function(index, value){ - switch(value.name) { - case CommonUtil.CONST_STORAGE_OBSERVATION_LIST : - strUrl = This.CONST_URL_DOMAIN +CommonUtil.CONST_URL_USER_OBSERVATION_LIST; - break; - case CommonUtil.CONST_STORAGE_POI_LIST : - strUrl = This.CONST_URL_DOMAIN +CommonUtil.CONST_URL_USER_POI; - break; - - default : - } - - This.syncTwoWayInitiate(value,strUrl); - }); - } - }, - - /** Writing server data to localstorage using sync */ - oneWaySyncStorageSet(value,strUrl) - { - - fetch(strUrl) - .then((response) => response.json()) - .then((data) => - { - localStorage.setItem(value.name,JSON.stringify(data)); - value.complete=true; - this.setOneWaySyncStatus(value); - }); - - }, - - /** Set status whether the sync of items completed or not */ - setOneWaySyncStatus(itemValue) - { - let booFlag; - /* Set the status - array value status set by reference */ - $.each(this.arrSyncOneWay, function(index,value){ - if(value.name === itemValue.name) - { - value.complete = itemValue.complete; - } - }); - - /* Set the global flag - to mark the end of sync*/ - $.each(this.arrSyncOneWay, function(index,value){ - if(value.complete===false) - { - booFlag = false; - } - }); - this.booIsSyncOneWayReady = booFlag; - - }, - - /** Reset One way sync items */ - refreshOneWaySyncItems(){ - $.each(this.arrSyncOneWay, function(index,value){ - value.complete=false; - }) - this.booIsSyncOneWayReady=false; - }, - - /** Deciding factor whether oneway should start or not */ - isSyncOnewayNeeded(loggedUser) - { - if(!this.booIsSyncOneWayReady) - { - this.syncOneWayEmptyProduct(loggedUser); - this.syncOneWayDifferentUser(loggedUser); - this.syncOneWayDifferentTimeStamp(loggedUser); - } - - }, - - /** Oneway Sync on empty products in store -- (e.g. first time) */ - syncOneWayEmptyProduct(appUser) - { - - let booLocalIsSyncOneWayReady = false; - if(!this.booIsSyncOneWayReady) { - $.each(this.arrSyncOneWay, function(index, value){ - let strItem = localStorage.getItem(value.name); - /** Check empty local storage */ - if(typeof(strItem)==undefined || strItem == null || strItem === '') - { - booLocalIsSyncOneWayReady = true; - - return false; - } - }); - this.booIsSyncOneWayReady = booLocalIsSyncOneWayReady; - this.appUser = appUser; - } - }, - - /** Oneway Sync For Different user login */ - syncOneWayDifferentUser(loggedUser) - { - - if(!this.booIsSyncOneWayReady) { - let userStored = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_USER_DETAIL)); - if (userStored.userId != loggedUser.userId) - { - this.booIsSyncOneWayReady = true; - this.appUser=loggedUser; - } - } - - }, - - /** Oneway sync for different timestamp (e.g. changes in server) */ - syncOneWayDifferentTimeStamp(appUser) - { - if(!this.booIsSyncOneWayReady) { - let strStoreTimeStamp = localStorage.getItem(CommonUtil.CONST_STORE_TIMESTAMP); - - let jsonHeader = {Authorization:appUser.userUuid}; - - fetch( - this.CONST_URL_DOMAIN +CommonUtil.CONST_URL_LAST_TIMESTAMP, - { - method:"GET", - headers: jsonHeader, - } - ) - .then((response) => response.json()) - .then((data) => - { - - if(typeof(strStoreTimeStamp)==undefined || strStoreTimeStamp == null || strStoreTimeStamp === '') - { - localStorage.setItem(CommonUtil.CONST_STORE_TIMESTAMP,JSON.stringify(data)); - this.booIsSyncOneWayReady = true; - this.appUser=appUser; - } - else{ - let jsonServerTimeStamp = data; - let jsonStoredTimeStamp = JSON.parse(strStoreTimeStamp); - let jsTimeStamp = new Date (jsonServerTimeStamp.lastUpdated); - let dtStoreTimeStamp = new Date(jsonStoredTimeStamp.lastUpdated); - - if(jsTimeStamp.getTime() != dtStoreTimeStamp.getTime()) - { - this.booIsSyncOneWayReady = true; - this.appUser=appUser; - - localStorage.setItem(CommonUtil.CONST_STORE_TIMESTAMP,JSON.stringify(data)); - - - } - - } - - }); - - } - }, - - /** Decide which two way sync will start */ - syncTwoWayInitiate(value, strUrl) - { - - switch(value.name) - { - case CommonUtil.CONST_STORAGE_OBSERVATION_LIST : - this.syncTwoWayObservation(value,strUrl); - break; - case CommonUtil.CONST_STORAGE_POI_LIST : - this.syncTwoWayPOI(value,strUrl); - break; - - default: - } - - }, - - /** Two way Sync POI */ - syncTwoWayPOI(value,strUrl) - { - this.syncPOISendPrepare(value); - }, - - /** Findout which data at POI need to be synced */ - syncPOISendPrepare(value) - { - let This = this; - let lstPOI = JSON.parse(localStorage.getItem(value.name)); - - if(lstPOI) - { - let lstPOIUpload = lstPOI.filter(poi => poi.uploaded === false); - this.totalTwoWaySyncPOST = lstPOIUpload.length; - if(lstPOIUpload && lstPOIUpload.length != 0) - { - lstPOIUpload.forEach(function(poi){ - This.syncPOIPOST(poi,This.totalTwoWaySyncPOST); - }) - } - else{ - /** Create the list using GET */ - let totalTwoWaySyncPOST = 0; - let updatedPOI = {}; - This.getPOIFromServerTwowaySync(totalTwoWaySyncPOST,updatedPOI); - } - } - else{ - /** Create the list using GET */ - this.getPOIFromServerTwowaySync(0,undefined); - } - }, - - /** Posting required POI data for sync */ - syncPOIPOST(poi,totalTwoWaySyncPOST) - { - let This = this; - let userUUID = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID); - let jsonBody = null; - - if(poi.deleted) - { - /** prepare POI object for server Delete Operation */ - let delPOI = {}; - delPOI.pointOfInterestId = poi.pointOfInterestId; - delPOI.deleted = poi.deleted; - jsonBody = JSON.stringify(delPOI); - - this.removeLocalPOI(poi.pointOfInterestId); - } - else - { - jsonBody = JSON.stringify(poi); - } - - fetch( - This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_SYNC_UPDATE_POI, - { - method: "POST", - headers: { - "Content-Type": "application/json", - 'Authorization' : userUUID - }, - body : jsonBody - }) - .then(function(response){ - if(response.status === 200) { - - } - else{ - /** Even if the response is not success, still need to increase the counter, - * to decide for next action after all PUSH */ - This.counterTwoWaySyncPOST = This.counterTwoWaySyncPOST + 1; - if(response.status===500) - { - return false; - } - } - return response.text() - }) - .then((data) => { - if(data) { - let updatedPOI = JSON.parse(data); - if(updatedPOI.deleted) - { - This.removeLocalPOI(poupdatedPOI.pointOfInterestId); - } - else - { - if(poi.pointOfInterestId < 0) - { - let indexPosition = null; - let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)); - $.each(lstPOI, function(index, jsonPOI){ - if(poi.pointOfInterestId == jsonPOI.pointOfInterestId) - { - indexPosition = index; - return false; - } - }) - - if(indexPosition) - { - /** Remove/delete the POI with nagative number (localy created ) */ - lstPOI.splice(indexPosition,1); - localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST,JSON.stringify(lstPOI)); - this.getPOIFromServerTwowaySync(1,updatedPOI); - } - } - if(poi.pointOfInterestId === updatedPOI.pointOfInterestId) - { - - this.updatePOIPOST(updatedPOI,totalTwoWaySyncPOST); - } - } - - } - }) - }, - - /** Update POI after response from server */ - updatePOIPOST(updatedPOI,totalTwoWaySyncPOST) - { - let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)); - let counter = undefined; - $.each(lstPOI, function(index, jsonPOI){ - if(jsonPOI.pointOfInterestId === updatedPOI.pointOfInterestId) - { - counter = index; - return false; - } - }); - - if(counter) - { - lstPOI[counter]=updatedPOI; - localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST, JSON.stringify(lstPOI) ); - - this.counterTwoWaySyncPOST = this.counterTwoWaySyncPOST + 1; - console.log('total number of upload : '+totalTwoWaySyncPOST+' ---- counter value : '+this.counterTwoWaySyncPOST); - - if(this.counterTwoWaySyncPOST === totalTwoWaySyncPOST) - { - this.counterTwoWaySyncPOST = 0; - this.getPOIFromServerTwowaySync(totalTwoWaySyncPOST,updatedPOI); - } - - - } - - }, - - /** GET POIs */ - getPOIFromServerTwowaySync(totalTwoWaySyncPOST,updatedPOI) - { - let This = this; - let strUUID = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID); - let jsonHeader = { Authorization: strUUID }; - fetch(This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_USER_POI, { - method: "GET", - headers: jsonHeader, - }).then((response) => response.json()) - .then((data) => { - let serverPOIs = data; - if(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)) - { - let lstLocalPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)); - serverPOIs.forEach(function(serverPOI){ - let arrIndex = undefined; - let booNoRecordFound = false; - let booRecordFound = false; - $.each(lstLocalPOI,function(index, localPOI){ - if(serverPOI.pointOfInterestId === localPOI.pointOfInterestId) { - booRecordFound = true; - if(updatedPOI && (totalTwoWaySyncPOST === 1 && updatedPOI.pointOfInterestId === serverPOI.pointOfInterestId )) - { - } - else - { - if(serverPOI.lastEditedTime) - { - let srvDate = new Date(serverPOI.lastEditedTime); - let localDate = new Date(localPOI.lastEditedTime); - - if(srvDate >= localDate) - { - arrIndex = index; - return false; - } - - } - else{ - arrIndex = index; - return false; - } - } - - } - }); - - if(booRecordFound){} - else { - booNoRecordFound = true; - } - if(arrIndex) - { - - lstLocalPOI[arrIndex]=serverPOI; - } - if(booNoRecordFound) - { - lstLocalPOI.push(serverPOI); - return false; - } - - }); - - localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST,JSON.stringify(lstLocalPOI)); - - } - else{ - - localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST,JSON.stringify(serverPOIs)); - } - - // Update the POI list after sync - if(this.$root.sharedState.placesListComponent != undefined) - { - this.$root.sharedState.placesListComponent.getPOIListFromStore(); + import CommonUtil from "@/components/CommonUtil"; + + export default { + name: "Sync", + components: {CommonUtil}, + props: { + isSyncNeeded: Boolean + }, + data() { + return { + isMounted: false, + CONST_URL_DOMAIN: '', + booIsSyncOneWayReady: false, + booIsSyncTwoWayReady: false, + arrSyncOneWay: [ + {"name": CommonUtil.CONST_STORAGE_CROP_CATEGORY, "complete": false}, + {"name": CommonUtil.CONST_STORAGE_CROP_LIST, "complete": false}, + {"name": CommonUtil.CONST_STORAGE_PEST_LIST, "complete": false}, + {"name": CommonUtil.CONST_STORAGE_CROP_PEST_LIST, "complete": false}, + {"name": CommonUtil.CONST_STORAGE_VISIBILITY_POLYGON, "complete": false}, + ], + arrSyncTwoWay: [ + {"name": CommonUtil.CONST_STORAGE_OBSERVATION_LIST, "complete": false}, + {"name": CommonUtil.CONST_STORAGE_POI_LIST, "complete": false}, + + ], + appUser: {}, + loading: false, + counterTwoWaySyncPOST: 0, + totalTwoWaySyncPOST: 0, + counterTwoWaySyncPOST: 0, + }; + }, + /* computed: { + testSync : function(){ + console.log('computed : msg 1 from parent for sync : '+this.isSyncNeeded); + return this.isSyncNeeded; + } + }, + */ + + watch: { + booIsSyncOneWayReady: + { + immediate: true, + handler(val, oldVal) { + if (val) { + + /* Starting of Sync */ + console.log('----- SYNC ONE WAY STARTED -----'); + this.syncOneWay(); + } + if (typeof (booIsSyncOneWayReady) === undefined) { + this.loading = false; + /* refresh for next time */ + this.refreshOneWaySyncItems(); + } + if (typeof (booIsSyncOneWayReady) == false) { + this.loading = false; + } + } + }, + booIsSyncTwoWayReady: + { + immediate: true, + handler(val, oldVal) { + if (val) { + this.loading = true; + console.log('----- SYNC TWO WAY STARTED -----'); + this.syncTwoWayAtLogin(); + this.loading = false; + } + } + } + + /* + isSyncNeeded : + { + immediate : true, + handler (val, oldVal) + { + console.log('watch 1 - prop changed : newval : '+val+' - oldval : '+oldVal); + }, + } + */ + }, + + methods: { + testFunction() { + console.log('test child testFuntion'); + }, + + /** One way Sync. Fetching the data from server and stored in local storage */ + syncOneWay() { + if (this.booIsSyncOneWayReady) { + this.loading = true; + + let appUser = this.appUser; + let funStorageSet = this.oneWaySyncStorageSet; + + if (typeof (appUser) == undefined || appUser == null || appUser === '') { + // TODO if appUser is not available + } + else { + let strUrl = ''; + let This = this; + $.each(this.arrSyncOneWay, function (index, value) { + switch (value.name) { + case CommonUtil.CONST_STORAGE_CROP_CATEGORY: + strUrl = This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_CROP_CATEGORY + appUser.organization_id; + break; + case CommonUtil.CONST_STORAGE_CROP_LIST: + strUrl = This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_CROP_LIST; + break; + case CommonUtil.CONST_STORAGE_PEST_LIST: + strUrl = This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_PEST_LIST + '?organizationId=' + appUser.organization_id; + break; + case CommonUtil.CONST_STORAGE_CROP_PEST_LIST: + strUrl = This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_CROP_PEST_LIST; + break; + case CommonUtil.CONST_STORAGE_VISIBILITY_POLYGON: + strUrl = This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_POLYGON_SERVICES + appUser.organization_id; + break; + default: + } + + funStorageSet(value, strUrl); + }); + } + + this.loading = false; + } + }, + + syncTwoWayAtLogin() { + + let appUser = this.appUser; + let funStorageSet = this.oneWaySyncStorageSet; + + if (typeof (appUser) == undefined || appUser == null || appUser === '') { + // TODO if appUser is not available + } + else { + + this.syncTwoWay(); + } + }, + syncTwoWay() { + // Run this only when we are online + if (navigator.onLine) { + let strUrl = ''; + let This = this; + $.each(this.arrSyncTwoWay, function (index, value) { + switch (value.name) { + case CommonUtil.CONST_STORAGE_OBSERVATION_LIST: + strUrl = This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_USER_OBSERVATION_LIST; + break; + case CommonUtil.CONST_STORAGE_POI_LIST: + strUrl = This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_USER_POI; + break; + + default: + } + + This.syncTwoWayInitiate(value, strUrl); + }); + } + }, + + /** Writing server data to localstorage using sync */ + oneWaySyncStorageSet(value, strUrl) { + + fetch(strUrl) + .then((response) => response.json()) + .then((data) => { + localStorage.setItem(value.name, JSON.stringify(data)); + value.complete = true; + this.setOneWaySyncStatus(value); + }); + + }, + + /** Set status whether the sync of items completed or not */ + setOneWaySyncStatus(itemValue) { + let booFlag; + /* Set the status - array value status set by reference */ + $.each(this.arrSyncOneWay, function (index, value) { + if (value.name === itemValue.name) { + value.complete = itemValue.complete; + } + }); + + /* Set the global flag - to mark the end of sync*/ + $.each(this.arrSyncOneWay, function (index, value) { + if (value.complete === false) { + booFlag = false; + } + }); + this.booIsSyncOneWayReady = booFlag; + + }, + + /** Reset One way sync items */ + refreshOneWaySyncItems() { + $.each(this.arrSyncOneWay, function (index, value) { + value.complete = false; + }) + this.booIsSyncOneWayReady = false; + }, + + /** Deciding factor whether oneway should start or not */ + isSyncOnewayNeeded(loggedUser) { + if (!this.booIsSyncOneWayReady) { + this.syncOneWayEmptyProduct(loggedUser); + this.syncOneWayDifferentUser(loggedUser); + this.syncOneWayDifferentTimeStamp(loggedUser); + } + + }, + + /** Oneway Sync on empty products in store -- (e.g. first time) */ + syncOneWayEmptyProduct(appUser) { + + let booLocalIsSyncOneWayReady = false; + if (!this.booIsSyncOneWayReady) { + $.each(this.arrSyncOneWay, function (index, value) { + let strItem = localStorage.getItem(value.name); + /** Check empty local storage */ + if (typeof (strItem) == undefined || strItem == null || strItem === '') { + booLocalIsSyncOneWayReady = true; + + return false; + } + }); + this.booIsSyncOneWayReady = booLocalIsSyncOneWayReady; + this.appUser = appUser; + } + }, + + /** Oneway Sync For Different user login */ + syncOneWayDifferentUser(loggedUser) { + + if (!this.booIsSyncOneWayReady) { + let userStored = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_USER_DETAIL)); + if (userStored.userId != loggedUser.userId) { + this.booIsSyncOneWayReady = true; + this.appUser = loggedUser; + } + } + + }, + + /** Oneway sync for different timestamp (e.g. changes in server) */ + syncOneWayDifferentTimeStamp(appUser) { + if (!this.booIsSyncOneWayReady) { + let strStoreTimeStamp = localStorage.getItem(CommonUtil.CONST_STORE_TIMESTAMP); + + let jsonHeader = {Authorization: appUser.userUuid}; + + fetch( + this.CONST_URL_DOMAIN + CommonUtil.CONST_URL_LAST_TIMESTAMP, + { + method: "GET", + headers: jsonHeader, + } + ) + .then((response) => response.json()) + .then((data) => { + + if (typeof (strStoreTimeStamp) == undefined || strStoreTimeStamp == null || strStoreTimeStamp === '') { + localStorage.setItem(CommonUtil.CONST_STORE_TIMESTAMP, JSON.stringify(data)); + this.booIsSyncOneWayReady = true; + this.appUser = appUser; + } + else { + let jsonServerTimeStamp = data; + let jsonStoredTimeStamp = JSON.parse(strStoreTimeStamp); + let jsTimeStamp = new Date(jsonServerTimeStamp.lastUpdated); + let dtStoreTimeStamp = new Date(jsonStoredTimeStamp.lastUpdated); + + if (jsTimeStamp.getTime() != dtStoreTimeStamp.getTime()) { + this.booIsSyncOneWayReady = true; + this.appUser = appUser; + + localStorage.setItem(CommonUtil.CONST_STORE_TIMESTAMP, JSON.stringify(data)); + + + } + + } + + }); + + } + }, + + /** Decide which two way sync will start */ + syncTwoWayInitiate(value, strUrl) { + + switch (value.name) { + case CommonUtil.CONST_STORAGE_OBSERVATION_LIST: + this.syncTwoWayObservation(value, strUrl); + break; + case CommonUtil.CONST_STORAGE_POI_LIST: + this.syncTwoWayPOI(value, strUrl); + break; + + default: + } + + }, + + /** Two way Sync POI */ + syncTwoWayPOI(value, strUrl) { + this.syncPOISendPrepare(value); + }, + + /** Findout which data at POI need to be synced */ + syncPOISendPrepare(value) { + let This = this; + let lstPOI = JSON.parse(localStorage.getItem(value.name)); + + if (lstPOI) { + let lstPOIUpload = lstPOI.filter(poi => poi.uploaded === false); + this.totalTwoWaySyncPOST = lstPOIUpload.length; + if (lstPOIUpload && lstPOIUpload.length != 0) { + lstPOIUpload.forEach(function (poi) { + This.syncPOIPOST(poi, This.totalTwoWaySyncPOST); + }) + } + else { + /** Create the list using GET */ + let totalTwoWaySyncPOST = 0; + let updatedPOI = {}; + This.getPOIFromServerTwowaySync(totalTwoWaySyncPOST, updatedPOI); + } + } + else { + /** Create the list using GET */ + this.getPOIFromServerTwowaySync(0, undefined); } - }) - - }, - /** Remove local POI */ - removeLocalPOI(id) - { - let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)); - let indexPosition = null; - $.each(lstPOI, function(index, poi){ - if(poi.pointOfInterestId===id) - { - indexPosition = index; - return false; - } - }); - - if(indexPosition) - { - - lstPOI.splice(indexPosition,1); - localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST,JSON.stringify(lstPOI)); - } - }, - - /** Two way Sync - Observation */ - syncTwoWayObservation(value,strUrl) - { - this.syncObservationSendPrepare(value); - }, - - /** Sync - Observation - embed with image data for POST */ - syncObservationSendPrepare(value) - { - let This = this; - let lstObservations = JSON.parse(localStorage.getItem(value.name)); - if(lstObservations) - { - - let lstObservationUpload = lstObservations.filter(observation => observation.uploaded === false); - this.totalTwoWaySyncPOST = lstObservationUpload.length; - - if(lstObservationUpload && lstObservationUpload.length != 0) - { - lstObservationUpload.forEach(function(observation) { - //console.info("syncObservationSendPrepare:observation.geoinfo= " + observation.geoinfo); - - let observationForStore = {}; - observationForStore.observationId = observation.observationId; - observationForStore.cropOrganismId = observation.cropOrganismId; - observationForStore.organismId = observation.organismId - observationForStore.timeOfObservation = observation.timeOfObservation; - observationForStore.statusChangedTime = observation.statusChangedTime; - observationForStore.statusTypeId = observation.statusTypeId; - observationForStore.isQuantified = observation.isQuantified; - observationForStore.userId = observation.userId; - observationForStore.geoinfo = observation.geoinfo; - observationForStore.locationPointOfInterestId = observation.locationPointOfInterestId; - observationForStore.broadcastMessage = observation.broadcastMessage; - observationForStore.statusRemarks = observation.statusRemarks; - observationForStore.observationHeading = observation.observationHeading; - observationForStore.observationText = observation.observationText; - observationForStore.observationData = observation.observationData; - observationForStore.locationIsPrivate = observation.locationIsPrivate; - observationForStore.polygonService = observation.polygonService; - observationForStore.uploaded = observation.uploaded; - observationForStore.observationIllustrationSet = observation.observationIllustrationSet; - if(observation.deleted) - { - observationForStore.deleted = observation.deleted; - } - - This.syncObservationSendPrepareSingleObject(observationForStore,This.totalTwoWaySyncPOST); - - }); - } - else - { - let totalTwoWaySyncPOST = 0; - let updatedObservation = {}; - /** GET Observations */ - this.getObservationsFromServerTwowaySync(totalTwoWaySyncPOST,updatedObservation); - } - } - else{ - - this.getObservationsFromServerTwowaySync(0,undefined); - } - - }, - - /** Prepare a single object includes image data */ - syncObservationSendPrepareSingleObject(observation,totalTwoWaySyncPOST,syncObservationPOST) - { - - let This = this; - let entityName = CommonUtil.CONST_DB_ENTITY_PHOTO; - if(observation.uploaded === false) - { - let observationUpload = observation; - let observationIllustrationSet = observationUpload.observationIllustrationSet; - - // talk to Innodb, embed image data to JSON POST and exit - let dbRequest = indexedDB.open(CommonUtil.CONST_DB_NAME, CommonUtil.CONST_DB_VERSION); - dbRequest.onsuccess = function(evt) { - let db = evt.target.result; - - if(db.objectStoreNames.contains(entityName)) - { - let transaction = db.transaction([entityName],'readwrite'); - let objectstore = transaction.objectStore(entityName); - if(observationIllustrationSet) - { - observationIllustrationSet.forEach(function(illustration) - { - let fileName = illustration.observationIllustrationPK.fileName; - - let objectstoreRequest = objectstore.get(fileName); - - - objectstoreRequest.onsuccess = function(event) - { - - let observationImage = event.target.result; - if(observationImage) - { - let imageTextData = observationImage.illustration.imageTextData; - illustration.imageTextData=imageTextData; - } - } - }); - } - - transaction.oncomplete = function() { - This.syncObservationPOST(observation, totalTwoWaySyncPOST); - } - } - else - { - This.syncObservationPOST(observation, totalTwoWaySyncPOST); - } - - } - } - }, - - /** Posting Observation data to VIPS server for sync */ - syncObservationPOST(observation,totalTwoWaySyncPOST) - { - //console.info("syncObservationPOST: observation.geoinfo=" + observation.geoinfo); - let This = this; - //if(this.isSyncNeeded) - { - let userUUID = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID); - let jsonBody = null; - - if(observation.deleted) - { - let delObservation = {}; - delObservation.observationId = observation.observationId; - delObservation.deleted = observation.deleted; - jsonBody = JSON.stringify(delObservation); - } - else - { - jsonBody = JSON.stringify(observation); - } - - - - if(observation.deleted) - { - This.removeLocalObservation(observation.observationId); - } -/*console.info("syncObservationPOST: jsonbody=" + jsonBody); - console.info("Sending observation");*/ - fetch( - This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_SYNC_UPDATE_OBSERVATION, - { - method: "POST", - headers: { - "Content-Type": "application/json", - 'Authorization' : userUUID - }, - body : jsonBody - } - ) - .then(function(response){ - - if(response.status === 200) { - //console.info("Done POSTING") - } - else{ - /** Even if the response is not success, still need to increase the counter, - * to decide for next action after all PUSH */ - This.counterTwoWaySyncPOST = This.counterTwoWaySyncPOST + 1; - } - return response.text() - }) - .then((data)=>{ - if(data) { - - - let updatedObservation = JSON.parse(data); - /*console.info("updatedObservation=(next line)") - console.info( updatedObservation); - console.info("observation=(next line)") - console.info( observation); - */ - - if(observation.observationId < 0) - { - let indexPosition = null; - let lstObservations = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST)); - $.each(lstObservations, function(index, jsonObservation){ - if(observation.observationId == jsonObservation.observationId) - { - indexPosition = index; - return false; - - } - }) - if(indexPosition || indexPosition === 0) - { - /** Remove/delete the Observation with nagative number (localy created ) */ - lstObservations.splice(indexPosition,1); - localStorage.setItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST,JSON.stringify(lstObservations)); - this.getObservationsFromServerTwowaySync(1,updatedObservation) - } - - } - - if(updatedObservation.observationId === observation.observationId) - { - this.updateObservationPOST(updatedObservation,totalTwoWaySyncPOST); - - } - - } - }); - } - - }, - - /** After App's POST opecation, update local observation when response received from VIPS Server */ - updateObservationPOST(updatedObservation,totalTwoWaySyncPOST) - { - - let lstObservations = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST)); - - let observationOld = {}; - let counter = undefined; - $.each(lstObservations, function(index, jsonObservation){ - if(jsonObservation.observationId === updatedObservation.observationId) - { - observationOld = Object.assign({},jsonObservation); // Deep cloning - counter = index; - return false; - } - - }); - - if(counter || counter === 0) - { - - let illustrationsUpdated = updatedObservation.observationIllustrationSet; - let illustrationsOld = observationOld.observationIllustrationSet; - - illustrationsOld.forEach(function(illOld){ - let recFound = false; - if(illustrationsUpdated) - { - illustrationsUpdated.forEach(function(illUpdated){ - if(illOld.observationIllustrationPK.fileName === illUpdated.observationIllustrationPK.fileName) - { - recFound = true; - return false; - } - }); - } - if(recFound) - { - delete illOld.uploaded; - if(illOld.deleted) - { - console.log('record to delete --- found'); - } - } - }) - updatedObservation.observationIllustrationSet = observationOld.observationIllustrationSet; - - - lstObservations[counter]=updatedObservation; - localStorage.setItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST, JSON.stringify(lstObservations) ); - - this.counterTwoWaySyncPOST = this.counterTwoWaySyncPOST + 1; - console.log('total number of upload : '+totalTwoWaySyncPOST+' ---- counter value : '+this.counterTwoWaySyncPOST); - if(this.counterTwoWaySyncPOST === totalTwoWaySyncPOST) - { - this.counterTwoWaySyncPOST = 0; - this.getObservationsFromServerTwowaySync(totalTwoWaySyncPOST,updatedObservation); - } - } - - }, - - /** GET Observations from server to sync local data */ - getObservationsFromServerTwowaySync(totalTwoWaySyncPOST,updatedObservation) - { - - let This = this; - let strUUID = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID); - let jsonHeader = { Authorization: strUUID }; - - fetch(This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_USER_OBSERVATION_LIST, { - method: "GET", - headers: jsonHeader, - }).then((response) => response.json()) - .then((data) => { - let serverObservations = data; - if(localStorage.getItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST)) - { - let lstLocalObservations = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST)); - - serverObservations.forEach(function(srvObservation){ - let arrIndex = undefined; - let booNoRecordFound = false; - let booRecordFound = false; - - $.each(lstLocalObservations,function(index, localObservation){ - if(srvObservation.observationId === localObservation.observationId) { - booRecordFound = true; - if(updatedObservation && (totalTwoWaySyncPOST === 1 && updatedObservation.observationId === srvObservation.observationId)) - { - - } - else - { - if(srvObservation.lastEditedTime) - { - if(localObservation.lastEditedTime) - { - - let srvDate = new Date(srvObservation.lastEditedTime); - let localDate = new Date(localObservation.lastEditedTime); - - if(srvDate >= localDate) - { - arrIndex = index; - return false; - } - } - else - { - arrIndex = index; - return false; - } - } - else - { - arrIndex = index; - return false; - } - } - - } - else - { - //booNoRecordFound = true; - } - - }) - if(booRecordFound){} - else { - booNoRecordFound = true; - } - if(arrIndex || arrIndex===0) - { - lstLocalObservations[arrIndex]=srvObservation; - } - if(booNoRecordFound) - { - lstLocalObservations.push(srvObservation); - return false; - } - - - }); - //console.info(lstLocalObservations); - localStorage.setItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST,JSON.stringify(lstLocalObservations)); - } - else - { - localStorage.setItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST,JSON.stringify(serverObservations)); - //this.$router.replace({path:'/'}); -/* this.$router.push("/").catch(()=>{}); - this.$router.go(); */ - } - - serverObservations.forEach(function(srvObservation){ - let observationId = srvObservation.observationId; - let organismId = srvObservation.organismId; - let illustrations = srvObservation.observationIllustrationSet; - - if(illustrations && illustrations.length != 0) - { - illustrations.forEach(function(illustration){ - let imageFileName = illustration.observationIllustrationPK.fileName; - if(imageFileName) - { - This.fetchImageFromServer(observationId, organismId,imageFileName); - } - }) - } - }); - // Update the data model for the observation list. - if(this.$root.sharedState.observationListComponent != undefined) + }, + + /** Posting required POI data for sync */ + syncPOIPOST(poi, totalTwoWaySyncPOST) { + let This = this; + let userUUID = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID); + let jsonBody = null; + + if (poi.deleted) { + /** prepare POI object for server Delete Operation */ + let delPOI = {}; + delPOI.pointOfInterestId = poi.pointOfInterestId; + delPOI.deleted = poi.deleted; + jsonBody = JSON.stringify(delPOI); + + this.removeLocalPOI(poi.pointOfInterestId); + } + else { + jsonBody = JSON.stringify(poi); + } + + fetch( + This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_SYNC_UPDATE_POI, + { + method: "POST", + headers: { + "Content-Type": "application/json", + 'Authorization': userUUID + }, + body: jsonBody + }) + .then(function (response) { + if (response.status === 200) { + + } + else { + /** Even if the response is not success, still need to increase the counter, + * to decide for next action after all PUSH */ + This.counterTwoWaySyncPOST = This.counterTwoWaySyncPOST + 1; + if (response.status === 500) { + return false; + } + } + return response.text() + }) + .then((data) => { + if (data) { + let updatedPOI = JSON.parse(data); + if (updatedPOI.deleted) { + This.removeLocalPOI(poupdatedPOI.pointOfInterestId); + } + else { + if (poi.pointOfInterestId < 0) { + let indexPosition = null; + let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)); + $.each(lstPOI, function (index, jsonPOI) { + if (poi.pointOfInterestId == jsonPOI.pointOfInterestId) { + indexPosition = index; + return false; + } + }) + + if (indexPosition) { + /** Remove/delete the POI with nagative number (localy created ) */ + lstPOI.splice(indexPosition, 1); + localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST, JSON.stringify(lstPOI)); + this.getPOIFromServerTwowaySync(1, updatedPOI); + } + } + if (poi.pointOfInterestId === updatedPOI.pointOfInterestId) { + + this.updatePOIPOST(updatedPOI, totalTwoWaySyncPOST); + } + } + + } + }) + }, + + /** Update POI after response from server */ + updatePOIPOST(updatedPOI, totalTwoWaySyncPOST) { + let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)); + let counter = undefined; + $.each(lstPOI, function (index, jsonPOI) { + if (jsonPOI.pointOfInterestId === updatedPOI.pointOfInterestId) { + counter = index; + return false; + } + }); + + if (counter) { + lstPOI[counter] = updatedPOI; + localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST, JSON.stringify(lstPOI)); + + this.counterTwoWaySyncPOST = this.counterTwoWaySyncPOST + 1; + console.log('total number of upload : ' + totalTwoWaySyncPOST + ' ---- counter value : ' + this.counterTwoWaySyncPOST); + + if (this.counterTwoWaySyncPOST === totalTwoWaySyncPOST) { + this.counterTwoWaySyncPOST = 0; + this.getPOIFromServerTwowaySync(totalTwoWaySyncPOST, updatedPOI); + } + + + } + + }, + + /** GET POIs */ + getPOIFromServerTwowaySync(totalTwoWaySyncPOST, updatedPOI) { + let This = this; + let strUUID = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID); + let jsonHeader = {Authorization: strUUID}; + fetch(This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_USER_POI, { + method: "GET", + headers: jsonHeader, + }).then((response) => response.json()) + .then((data) => { + let serverPOIs = data; + if (localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)) { + let lstLocalPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)); + serverPOIs.forEach(function (serverPOI) { + let arrIndex = undefined; + let booNoRecordFound = false; + let booRecordFound = false; + $.each(lstLocalPOI, function (index, localPOI) { + if (serverPOI.pointOfInterestId === localPOI.pointOfInterestId) { + booRecordFound = true; + if (updatedPOI && (totalTwoWaySyncPOST === 1 && updatedPOI.pointOfInterestId === serverPOI.pointOfInterestId)) { + } + else { + if (serverPOI.lastEditedTime) { + let srvDate = new Date(serverPOI.lastEditedTime); + let localDate = new Date(localPOI.lastEditedTime); + + if (srvDate >= localDate) { + arrIndex = index; + return false; + } + + } + else { + arrIndex = index; + return false; + } + } + + } + }); + + if (booRecordFound) {} + else { + booNoRecordFound = true; + } + if (arrIndex) { + + lstLocalPOI[arrIndex] = serverPOI; + } + if (booNoRecordFound) { + lstLocalPOI.push(serverPOI); + return false; + } + + }); + + localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST, JSON.stringify(lstLocalPOI)); + + } + else { + + localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST, JSON.stringify(serverPOIs)); + } + + // Update the POI list after sync + if (this.$root.sharedState.placesListComponent != undefined) { + this.$root.sharedState.placesListComponent.getPOIListFromStore(); + } + }) + + }, + /** Remove local POI */ + removeLocalPOI(id) { + let lstPOI = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_POI_LIST)); + let indexPosition = null; + $.each(lstPOI, function (index, poi) { + if (poi.pointOfInterestId === id) { + indexPosition = index; + return false; + } + }); + + if (indexPosition) { + + lstPOI.splice(indexPosition, 1); + localStorage.setItem(CommonUtil.CONST_STORAGE_POI_LIST, JSON.stringify(lstPOI)); + } + }, + + /** Two way Sync - Observation */ + syncTwoWayObservation(value, strUrl) { + this.syncObservationSendPrepare(value); + }, + + /** Sync - Observation - embed with image data for POST */ + syncObservationSendPrepare(value) { + let This = this; + let lstObservations = JSON.parse(localStorage.getItem(value.name)); + if (lstObservations) { + + let lstObservationUpload = lstObservations.filter(observation => observation.uploaded === false); + this.totalTwoWaySyncPOST = lstObservationUpload.length; + + if (lstObservationUpload && lstObservationUpload.length != 0) { + lstObservationUpload.forEach(function (observation) { + //console.info("syncObservationSendPrepare:observation.geoinfo= " + observation.geoinfo); + + let observationForStore = {}; + observationForStore.observationId = observation.observationId; + observationForStore.cropOrganismId = observation.cropOrganismId; + observationForStore.organismId = observation.organismId + observationForStore.timeOfObservation = observation.timeOfObservation; + observationForStore.statusChangedTime = observation.statusChangedTime; + observationForStore.statusTypeId = observation.statusTypeId; + observationForStore.isQuantified = observation.isQuantified; + observationForStore.userId = observation.userId; + observationForStore.geoinfo = observation.geoinfo; + observationForStore.locationPointOfInterestId = observation.locationPointOfInterestId; + observationForStore.broadcastMessage = observation.broadcastMessage; + observationForStore.statusRemarks = observation.statusRemarks; + observationForStore.observationHeading = observation.observationHeading; + observationForStore.observationText = observation.observationText; + observationForStore.observationData = observation.observationData; + observationForStore.locationIsPrivate = observation.locationIsPrivate; + observationForStore.polygonService = observation.polygonService; + observationForStore.uploaded = observation.uploaded; + observationForStore.observationIllustrationSet = observation.observationIllustrationSet; + if (observation.deleted) { + observationForStore.deleted = observation.deleted; + } + + This.syncObservationSendPrepareSingleObject(observationForStore, This.totalTwoWaySyncPOST); + + }); + } + else { + let totalTwoWaySyncPOST = 0; + let updatedObservation = {}; + /** GET Observations */ + this.getObservationsFromServerTwowaySync(totalTwoWaySyncPOST, updatedObservation); + } + } + else { + + this.getObservationsFromServerTwowaySync(0, undefined); + } + + }, + + /** Prepare a single object includes image data */ + syncObservationSendPrepareSingleObject(observation, totalTwoWaySyncPOST, syncObservationPOST) { + + let This = this; + let entityName = CommonUtil.CONST_DB_ENTITY_PHOTO; + if (observation.uploaded === false) { + let observationUpload = observation; + let observationIllustrationSet = observationUpload.observationIllustrationSet; + + // talk to Innodb, embed image data to JSON POST and exit + let dbRequest = indexedDB.open(CommonUtil.CONST_DB_NAME, CommonUtil.CONST_DB_VERSION); + dbRequest.onsuccess = function (evt) { + let db = evt.target.result; + + if (db.objectStoreNames.contains(entityName)) { + let transaction = db.transaction([entityName], 'readwrite'); + let objectstore = transaction.objectStore(entityName); + if (observationIllustrationSet) { + observationIllustrationSet.forEach(function (illustration) { + let fileName = illustration.observationIllustrationPK.fileName; + + let objectstoreRequest = objectstore.get(fileName); + + + objectstoreRequest.onsuccess = function (event) { + + let observationImage = event.target.result; + if (observationImage) { + let imageTextData = observationImage.illustration.imageTextData; + illustration.imageTextData = imageTextData; + } + } + }); + } + + transaction.oncomplete = function () { + This.syncObservationPOST(observation, totalTwoWaySyncPOST); + } + } + else { + This.syncObservationPOST(observation, totalTwoWaySyncPOST); + } + + } + } + }, + + /** Posting Observation data to VIPS server for sync */ + syncObservationPOST(observation, totalTwoWaySyncPOST) { + //console.info("syncObservationPOST: observation.geoinfo=" + observation.geoinfo); + let This = this; + //if(this.isSyncNeeded) { - this.$root.sharedState.observationListComponent.getObservationsFromStore(); + let userUUID = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID); + let jsonBody = null; + + if (observation.deleted) { + let delObservation = {}; + delObservation.observationId = observation.observationId; + delObservation.deleted = observation.deleted; + jsonBody = JSON.stringify(delObservation); + } + else { + jsonBody = JSON.stringify(observation); + } + + + + if (observation.deleted) { + This.removeLocalObservation(observation.observationId); + } + /*console.info("syncObservationPOST: jsonbody=" + jsonBody); + console.info("Sending observation");*/ + fetch( + This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_SYNC_UPDATE_OBSERVATION, + { + method: "POST", + headers: { + "Content-Type": "application/json", + 'Authorization': userUUID + }, + body: jsonBody + } + ) + .then(function (response) { + + if (response.status === 200) { + //console.info("Done POSTING") + } + else { + /** Even if the response is not success, still need to increase the counter, + * to decide for next action after all PUSH */ + This.counterTwoWaySyncPOST = This.counterTwoWaySyncPOST + 1; + } + return response.text() + }) + .then((data) => { + if (data) { + + + let updatedObservation = JSON.parse(data); + /*console.info("updatedObservation=(next line)") + console.info( updatedObservation); + console.info("observation=(next line)") + console.info( observation); + */ + + if (observation.observationId < 0) { + let indexPosition = null; + let lstObservations = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST)); + $.each(lstObservations, function (index, jsonObservation) { + if (observation.observationId == jsonObservation.observationId) { + indexPosition = index; + return false; + + } + }) + if (indexPosition || indexPosition === 0) { + /** Remove/delete the Observation with nagative number (localy created ) */ + lstObservations.splice(indexPosition, 1); + localStorage.setItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST, JSON.stringify(lstObservations)); + this.getObservationsFromServerTwowaySync(1, updatedObservation) + } + + } + + if (updatedObservation.observationId === observation.observationId) { + this.updateObservationPOST(updatedObservation, totalTwoWaySyncPOST); + + } + + } + }); } - }) - }, - - /** GET image from server */ - fetchImageFromServer(observationId,organismId,imageFileName) - { - - let photoURL=this.CONST_URL_DOMAIN+CommonUtil.CONST_URL_STATIC_IMAGE_PATH+organismId+'/'+imageFileName; - let imgTest; - let This = this; - let observationImage = { - observationId : '', - organismId : '', - illustration : { - fileName : '', - imageTextData : '', - deleted : false - } - }; - observationImage.observationId = observationId; - observationImage.organismId = organismId; - observationImage.illustration.fileName = imageFileName; - - - - if(organismId) - { - const toDataURL = url => fetch(url) - .then(response => response.blob()) - .then(blob => new Promise((resolve, reject) => { - const reader = new FileReader() - reader.onloadend = () => resolve(reader.result) - reader.onerror = reject - reader.readAsDataURL(blob) - })) - - - toDataURL(photoURL) - .then(imageTextData => { - observationImage.illustration.imageTextData = imageTextData; - This.storeImageData(observationImage); - }) - } - - }, - /** Store image from server - */ - storeImageData(observationImage) - { let This = this; - let entityName = CommonUtil.CONST_DB_ENTITY_PHOTO; - - let dbRequest = indexedDB.open(CommonUtil.CONST_DB_NAME, CommonUtil.CONST_DB_VERSION); - dbRequest.onsuccess = function(evt) { - let db = evt.target.result; - if(db.objectStoreNames.contains(entityName)){ - let transaction = db.transaction([entityName],'readwrite'); - let objectstore = transaction.objectStore(entityName).add(observationImage,observationImage.illustration.fileName); - } - else - { - let store = db.createObjectStore(entityName, {keypath : observationImage.illustration.fileName}); - store.createIndex('observationId', 'observationId', { unique: false }); - store.createIndex('organismId', 'organismId', { unique: false }); - } - } - - - dbRequest.onupgradeneeded= function (event) - { - let db = event.target.result; - if( !db.objectStoreNames.contains(entityName)){ - let store = db.createObjectStore(entityName, {keypath : observationImage.illustration.fileName}); - store.createIndex('observationId', 'observationId', { unique: false }); - store.createIndex('organismId', 'organismId', { unique: false }); - } - } - - }, - - /** Remove local Observation */ - removeLocalObservation(id) - { - let lstObservations = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST)); - let indexPosition = null; - $.each(lstObservations, function(index, observation){ - if(observation.observationId===id) - { - indexPosition = index; - return false; - } - }); - if(indexPosition || indexPosition === 0) - { - this.removeImageRecord(id); - - lstObservations.splice(indexPosition,1); - localStorage.setItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST,JSON.stringify(lstObservations)); - } - }, - /** - * Remove image data from indexed DB - */ - removeImageRecord(observationId){ - let entityName = CommonUtil.CONST_DB_ENTITY_PHOTO; - let dbRequest = indexedDB.open(CommonUtil.CONST_DB_NAME, CommonUtil.CONST_DB_VERSION); - let indexName = CommonUtil.CONST_DB_INDEX_NAME_OBSERVATION_ID; - dbRequest.onsuccess = function(evt) { - let db = evt.target.result; - if(db.objectStoreNames.contains(entityName)) - { - let transaction = db.transaction([entityName],'readwrite'); - let objectstore = transaction.objectStore(entityName); - let indexStore = objectstore.index(indexName); - let keyRange = IDBKeyRange.only(observationId); - let cursorRequest = indexStore.openCursor(keyRange); - cursorRequest.onsuccess = function(event){ - let cursor = event.target.result; - if(cursor) - { - cursor.delete(); - cursor.continue(); - } - } - } - - } - - }, - - - - }, - mounted () { - this.isMounted = true; - this.CONST_URL_DOMAIN = CommonUtil.CONST_URL_DOMAIN; - } - -}; -</script> + }, + + /** After App's POST opecation, update local observation when response received from VIPS Server */ + updateObservationPOST(updatedObservation, totalTwoWaySyncPOST) { + + let lstObservations = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST)); + + let observationOld = {}; + let counter = undefined; + $.each(lstObservations, function (index, jsonObservation) { + if (jsonObservation.observationId === updatedObservation.observationId) { + observationOld = Object.assign({}, jsonObservation); // Deep cloning + counter = index; + return false; + } + + }); + + if (counter || counter === 0) { + + let illustrationsUpdated = updatedObservation.observationIllustrationSet; + let illustrationsOld = observationOld.observationIllustrationSet; + + illustrationsOld.forEach(function (illOld) { + let recFound = false; + if (illustrationsUpdated) { + illustrationsUpdated.forEach(function (illUpdated) { + if (illOld.observationIllustrationPK.fileName === illUpdated.observationIllustrationPK.fileName) { + recFound = true; + return false; + } + }); + } + if (recFound) { + delete illOld.uploaded; + if (illOld.deleted) { + console.log('record to delete --- found'); + } + } + }) + updatedObservation.observationIllustrationSet = observationOld.observationIllustrationSet; + + + lstObservations[counter] = updatedObservation; + localStorage.setItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST, JSON.stringify(lstObservations)); + + this.counterTwoWaySyncPOST = this.counterTwoWaySyncPOST + 1; + console.log('total number of upload : ' + totalTwoWaySyncPOST + ' ---- counter value : ' + this.counterTwoWaySyncPOST); + if (this.counterTwoWaySyncPOST === totalTwoWaySyncPOST) { + this.counterTwoWaySyncPOST = 0; + this.getObservationsFromServerTwowaySync(totalTwoWaySyncPOST, updatedObservation); + } + } + + }, + + /** GET Observations from server to sync local data */ + getObservationsFromServerTwowaySync(totalTwoWaySyncPOST, updatedObservation) { + + let This = this; + let strUUID = localStorage.getItem(CommonUtil.CONST_STORAGE_UUID); + let jsonHeader = {Authorization: strUUID}; + + fetch(This.CONST_URL_DOMAIN + CommonUtil.CONST_URL_USER_OBSERVATION_LIST, { + method: "GET", + headers: jsonHeader, + }).then((response) => response.json()) + .then((data) => { + let serverObservations = data; + if (localStorage.getItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST)) { + let lstLocalObservations = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST)); + + serverObservations.forEach(function (srvObservation) { + let arrIndex = undefined; + let booNoRecordFound = false; + let booRecordFound = false; + + $.each(lstLocalObservations, function (index, localObservation) { + if (srvObservation.observationId === localObservation.observationId) { + booRecordFound = true; + if (updatedObservation && (totalTwoWaySyncPOST === 1 && updatedObservation.observationId === srvObservation.observationId)) { + + } + else { + if (srvObservation.lastEditedTime) { + if (localObservation.lastEditedTime) { + + let srvDate = new Date(srvObservation.lastEditedTime); + let localDate = new Date(localObservation.lastEditedTime); + + if (srvDate >= localDate) { + arrIndex = index; + return false; + } + } + else { + arrIndex = index; + return false; + } + } + else { + arrIndex = index; + return false; + } + } + + } + else { + //booNoRecordFound = true; + } + + }) + if (booRecordFound) {} + else { + booNoRecordFound = true; + } + if (arrIndex || arrIndex === 0) { + lstLocalObservations[arrIndex] = srvObservation; + } + if (booNoRecordFound) { + lstLocalObservations.push(srvObservation); + return false; + } + + + }); + //console.info(lstLocalObservations); + localStorage.setItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST, JSON.stringify(lstLocalObservations)); + } + else { + localStorage.setItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST, JSON.stringify(serverObservations)); + //this.$router.replace({path:'/'}); + /* this.$router.push("/").catch(()=>{}); + this.$router.go(); */ + } + + serverObservations.forEach(function (srvObservation) { + let observationId = srvObservation.observationId; + let organismId = srvObservation.organismId; + let illustrations = srvObservation.observationIllustrationSet; + + if (illustrations && illustrations.length != 0) { + illustrations.forEach(function (illustration) { + let imageFileName = illustration.observationIllustrationPK.fileName; + if (imageFileName) { + This.fetchImageFromServer(observationId, organismId, imageFileName); + } + }) + } + }); + // Update the data model for the observation list. + if (this.$root.sharedState.observationListComponent != undefined) { + this.$root.sharedState.observationListComponent.getObservationsFromStore(); + } + }) + }, + + /** GET image from server */ + fetchImageFromServer(observationId, organismId, imageFileName) { + + let photoURL = this.CONST_URL_DOMAIN + CommonUtil.CONST_URL_STATIC_IMAGE_PATH + organismId + '/' + imageFileName; + let imgTest; + let This = this; + let observationImage = { + observationId: '', + organismId: '', + illustration: { + fileName: '', + imageTextData: '', + deleted: false + } + }; + observationImage.observationId = observationId; + observationImage.organismId = organismId; + observationImage.illustration.fileName = imageFileName; + + + + if (organismId) { + const toDataURL = url => fetch(url) + .then(response => response.blob()) + .then(blob => new Promise((resolve, reject) => { + const reader = new FileReader() + reader.onloadend = () => resolve(reader.result) + reader.onerror = reject + reader.readAsDataURL(blob) + })) + + + toDataURL(photoURL) + .then(imageTextData => { + observationImage.illustration.imageTextData = imageTextData; + This.storeImageData(observationImage); + }) + } + + }, + /** Store image from server + */ + storeImageData(observationImage) { + let This = this; + let entityName = CommonUtil.CONST_DB_ENTITY_PHOTO; + + let dbRequest = indexedDB.open(CommonUtil.CONST_DB_NAME, CommonUtil.CONST_DB_VERSION); + dbRequest.onsuccess = function (evt) { + let db = evt.target.result; + if (db.objectStoreNames.contains(entityName)) { + let transaction = db.transaction([entityName], 'readwrite'); + let objectstore = transaction.objectStore(entityName).add(observationImage, observationImage.illustration.fileName); + } + else { + let store = db.createObjectStore(entityName, {keypath: observationImage.illustration.fileName}); + store.createIndex('observationId', 'observationId', {unique: false}); + store.createIndex('organismId', 'organismId', {unique: false}); + } + } + + + dbRequest.onupgradeneeded = function (event) { + let db = event.target.result; + if (!db.objectStoreNames.contains(entityName)) { + let store = db.createObjectStore(entityName, {keypath: observationImage.illustration.fileName}); + store.createIndex('observationId', 'observationId', {unique: false}); + store.createIndex('organismId', 'organismId', {unique: false}); + } + } + + }, + + /** Remove local Observation */ + removeLocalObservation(id) { + let lstObservations = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST)); + let indexPosition = null; + $.each(lstObservations, function (index, observation) { + if (observation.observationId === id) { + indexPosition = index; + return false; + } + }); + if (indexPosition || indexPosition === 0) { + this.removeImageRecord(id); + + lstObservations.splice(indexPosition, 1); + localStorage.setItem(CommonUtil.CONST_STORAGE_OBSERVATION_LIST, JSON.stringify(lstObservations)); + } + }, + /** + * Remove image data from indexed DB + */ + removeImageRecord(observationId) { + let entityName = CommonUtil.CONST_DB_ENTITY_PHOTO; + let dbRequest = indexedDB.open(CommonUtil.CONST_DB_NAME, CommonUtil.CONST_DB_VERSION); + let indexName = CommonUtil.CONST_DB_INDEX_NAME_OBSERVATION_ID; + dbRequest.onsuccess = function (evt) { + let db = evt.target.result; + if (db.objectStoreNames.contains(entityName)) { + let transaction = db.transaction([entityName], 'readwrite'); + let objectstore = transaction.objectStore(entityName); + let indexStore = objectstore.index(indexName); + let keyRange = IDBKeyRange.only(observationId); + let cursorRequest = indexStore.openCursor(keyRange); + cursorRequest.onsuccess = function (event) { + let cursor = event.target.result; + if (cursor) { + cursor.delete(); + cursor.continue(); + } + } + } + + } + + }, + + + + }, + mounted() { + this.isMounted = true; + this.CONST_URL_DOMAIN = CommonUtil.CONST_URL_DOMAIN; + } + + }; +</script> \ No newline at end of file diff --git a/src/components/Visibility.vue b/src/components/Visibility.vue index c9625d7bbd9072f2c63fce3334418bd8fcd536f0..0e0a00968759ee00985dde53389ced6ce68e0577 100644 --- a/src/components/Visibility.vue +++ b/src/components/Visibility.vue @@ -1,4 +1,4 @@ - <!-- +<!-- Copyright (c) 2022 NIBIO <http://www.nibio.no/>. @@ -22,93 +22,89 @@ --> <template> - <select class="form-control" v-model=visibilityId ref='visibility' v-show="isMounted" v-on:change="selectVisibility($event)"> - <option v-for="privacy in visibilities" v-bind:value="privacy.value" v-bind:key="privacy.value">{{privacy.name}}</option> - </select> + <select class="form-control" v-model=visibilityId ref='visibility' v-show="isMounted" + v-on:change="selectVisibility($event)"> + <option v-for="privacy in visibilities" v-bind:value="privacy.value" v-bind:key="privacy.value">{{privacy.name}} + </option> + </select> </template> <script> -import CommonUtil from '@/components/CommonUtil' -export default { - props : ['locationIsPrivate','polygonService'], - data() { - return { - isMounted : false, - visibilities : [ - {name: this.$i18n.t("visibility.label.undefined"), value:'undefined'}, - {name: this.$i18n.t("visibility.label.private"), value:3}, - {name: this.$i18n.t("visibility.label.public"), value:4}, - ], - visibilityId : 'undefined' - } - - }, - methods : { - buildVisibilities() { - let This = this; - let polygons = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_VISIBILITY_POLYGON)); - $.each(polygons, function(index, data){ - - let polygon = {}; - polygon.name = data.polygonServiceName; - polygon.value = data.polygonServiceId; + import CommonUtil from '@/components/CommonUtil' + export default { + props: ['locationIsPrivate', 'polygonService'], + data() { + return { + isMounted: false, + visibilities: [ + {name: this.$i18n.t("visibility.label.undefined"), value: 'undefined'}, + {name: this.$i18n.t("visibility.label.private"), value: 3}, + {name: this.$i18n.t("visibility.label.public"), value: 4}, + ], + visibilityId: 'undefined' + } - This.visibilities.push(polygon) + }, + methods: { + buildVisibilities() { + let This = this; + let polygons = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_VISIBILITY_POLYGON)); + $.each(polygons, function (index, data) { - }) + let polygon = {}; + polygon.name = data.polygonServiceName; + polygon.value = data.polygonServiceId; - if(this.locationIsPrivate) - { - this.visibilityId = 3; // Private = 3 check data() - } - else - { - if(this.polygonService) - { - this.visibilityId = this.polygonService.polygonServiceId; - } - else - { - this.visibilityId = 4; // Public = 4 check data() - } - - - } + This.visibilities.push(polygon) - this.isMounted = true; - - }, - selectVisibility(event){ - let paramPrivate = false; - let paramPolygonService = ''; - let polygons = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_VISIBILITY_POLYGON)); - let polygon = polygons.find(({polygonServiceId})=> polygonServiceId === parseInt(event.target.value)); - - if(event.target.value) - { - switch(parseInt(event.target.value)) - { - case 1 : - paramPolygonService=polygon; - break; - case 2 : - paramPolygonService=polygon; - break; - case 3 : - paramPrivate=true; - break; - case 4 : - break; - default : - console.log('Selected option is beyond the range'); - } + }) - } - this.$emit('visibilityMapAction', paramPrivate, paramPolygonService); - }, - }, - mounted () { - this.buildVisibilities(); - } - -} + if (this.locationIsPrivate) { + this.visibilityId = 3; // Private = 3 check data() + } + else { + if (this.polygonService) { + this.visibilityId = this.polygonService.polygonServiceId; + } + else { + this.visibilityId = 4; // Public = 4 check data() + } + + + } + + this.isMounted = true; + + }, + selectVisibility(event) { + let paramPrivate = false; + let paramPolygonService = ''; + let polygons = JSON.parse(localStorage.getItem(CommonUtil.CONST_STORAGE_VISIBILITY_POLYGON)); + let polygon = polygons.find(({polygonServiceId}) => polygonServiceId === parseInt(event.target.value)); + + if (event.target.value) { + switch (parseInt(event.target.value)) { + case 1: + paramPolygonService = polygon; + break; + case 2: + paramPolygonService = polygon; + break; + case 3: + paramPrivate = true; + break; + case 4: + break; + default: + console.log('Selected option is beyond the range'); + } + + } + this.$emit('visibilityMapAction', paramPrivate, paramPolygonService); + }, + }, + mounted() { + this.buildVisibilities(); + } + + } </script> \ No newline at end of file diff --git a/src/components/Welcome.vue b/src/components/Welcome.vue index 898c6483597d3dd0ec2cd03ec30c1613a8830ae6..c8d9e846d28569317958d1087e9f31f5b0a3cc94 100644 --- a/src/components/Welcome.vue +++ b/src/components/Welcome.vue @@ -1,4 +1,4 @@ - <!-- +<!-- Copyright (c) 2022 NIBIO <http://www.nibio.no/>. @@ -22,14 +22,14 @@ --> <template> - <div class="main"> - <center> - <p><img src="../assets/welcome_illus.png" style="width: 70%; margin-bottom: 50px;"/></p> + <div class="main"> + <center> + <p><img src="../assets/welcome_illus.png" style="width: 70%; margin-bottom: 50px;" /></p> <h2>{{$t("welcome.header.text")}}</h2> <p v-html="$t('welcome.body.text')"></p> - <footer v-html="$t('welcome.footer.text')"/> - </center> - </div> + <footer v-html="$t('welcome.footer.text')" /> + </center> + </div> </template> <style scoped> @@ -37,6 +37,7 @@ padding-right: 15px; padding-left: 15px; } + footer { margin-top: 55px; font-size: small; @@ -48,25 +49,24 @@ </style> <script> -import CommonUtil from '@/components/CommonUtil' + import CommonUtil from '@/components/CommonUtil' + + export default ({ + data() { + return { + + } + }, + methods: { -export default ({ - data() - { - return { - - } - }, - methods: { + }, + mounted() { + //this.isMounted = true; + //this.uuid = localStorage.getItem(localStorage.removeItem(CommonUtil.CONST_STORAGE_UUID)); + //console.log('uuid : '+this.uuid); - }, - mounted() { - //this.isMounted = true; - //this.uuid = localStorage.getItem(localStorage.removeItem(CommonUtil.CONST_STORAGE_UUID)); - //console.log('uuid : '+this.uuid); + } - } - -}) -</script> + }) +</script> \ No newline at end of file diff --git a/src/locales/en.json b/src/locales/en.json index e4dbcdc90fc465ca59688e7c2e2f731e7b7d65f0..26c5647ba66ae78a09e14d36d0b7464d4754c7e4 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1,93 +1,71 @@ { - "observationlist.heading" : "My observations", - "observationlist.alert.emptylist" : "No registered observations", - - "welcome.header.text" : "Observation App", - "welcome.body.text" : "Lorem Ipsum", - "welcome.footer.text" : "VIPS is an automatic forecasting system for agricultural pests and diseases, developed by NIBIO (the Norwegian Institute of Bioeconomy Research) and Norsk landbruksrådgivning (The Norwegian agricultural extension service). ", - - "login.button.label" : "Log in", - "logout.button.label" : "Log out", - "login.username.field.placeholder" : "Username", - "login.pwd.field.placeholder" : "Password", - "login.systems.wrong.credential" : "Wrong username and/or password. Please try again.", - - "logout.header.text" : "Logout", - "logout.success.text" : "You successfully log out ", - - "observation.date.label" : "Observation Date", - "observation.quantification.label" : "Quantify", - "observation.quantification.form.label" : "Observasjon quantification", - "observation.detail.heading.placeholder" : "Heading (What was found and where?)", - "observation.detail.body.placeholder" : "Text body (more details, please)", - "observation.map.label" : "Observation Map", - "observation.msg" : "Observation", - "observation.selectcrop" : "Select crop", - "observation.selectpest" : "Select pest", - "observation.unregisteredpest" : "Unknown to our database", - - "photo.label.takephoto" : "Take photo", - "photo.label.selectphoto" : "Select photo", - "photo.modal.deleteprompt" : "This image will be deleted", - "photo.modal.label.pleasechoose" : "Please choose", - "photo.modal.observationphoto.label" : "Observation photo", - - - "back.label" : "Back", - "save.label" : "Save", - "delete.label" : "Delete", - "close.label" : "Close", - "action.label" : "Action", - - "map.link.back.label" : "Back", - "map.link.OK.label" : "OK", - "map.link.cancel.label" : "Cancel", - - "mapobservation.label.selectPOI" : "Select a Point Of Interest", - "mapobservation.label.noPOIselected" : "No Point Of Interest selected", - "mapPOI.poiName.label" : "Point Of Interest name", - "mapPOI.modal.label.saveprompt" : "Save information for", - "mapPOI.modal.label.pleasechoose" : "Please choose the option below", - "mapPOI.modal.label.deleteprompt" : "Delete information for", - "mapPOI.modal.alert.missingGeoInfo" : "Please set the location for this Point Of Interest", - "mapPOI.modal.alert.missingPOIType" : "Please select the type of POI this is", - "mapPOI.modal.alert.missingPOIName" : "Please enter a name for the Point Of Interest", - - "General" : "General", - "Farm" : "Farm", - "Field" : "Field", - "Weather station" : "Weather station", - - "places.search.label" : "Search", - "placeslist.heading" : "My places", - - - - - "crop.category.empty.alert" : "Please select Crop category", - "cropcategory" : "Crop category", - - "alert.header.label" : "!! ALERT !!", - "alert.header.error.label" : "!! ERROR !!", - "alert.footer.label" : "Please chose the option below", - - - - "err.observation.date.empty" : "Observation Date cannot be empty", - "err.observation.geoinfo" : "Geolocation cannot be empty !!", - "err.observation.header.empty" : "Observation details cannot be empty", - "err.observation.remove.msg" : "This Observation will be removed. Are you sure ?", - "err.observation.remove.warn" : "This Observation will be removed", - - "sync" : "Sync to server", - "settings.heading" : "Settings", - - "visibility.label.undefined" : "Select visibility", - "visibility.label.private" : "Private", - "visibility.label.public" : "Public", - - "index.header" : "Field observations", - "about.header" : "About", - "about.version" : "Version", - "about.text" : "<p>The app is developed by <a href='https://nibio.no/em' target='new'>NIBIO</a>'s <a href='https://www.vips-landbruk.no/'>VIPS team</a>.</p><p>VIPS project leader: <a href='https://www.nibio.no/en/employees/berit-nordskog' target='new'>Berit Nordskog</a></p><p>© 2022 <a href='https://nibio.no/en' target='new'>NIBIO</a></p>" + "observationlist.heading": "My observations", + "observationlist.alert.emptylist": "No registered observations", + "welcome.header.text": "Observation App", + "welcome.body.text": "Lorem Ipsum", + "welcome.footer.text": "VIPS is an automatic forecasting system for agricultural pests and diseases, developed by NIBIO (the Norwegian Institute of Bioeconomy Research) and Norsk landbruksrådgivning (The Norwegian agricultural extension service). ", + "login.button.label": "Log in", + "logout.button.label": "Log out", + "login.username.field.placeholder": "Username", + "login.pwd.field.placeholder": "Password", + "login.systems.wrong.credential": "Wrong username and/or password. Please try again.", + "logout.header.text": "Logout", + "logout.success.text": "You successfully log out ", + "observation.date.label": "Observation Date", + "observation.quantification.label": "Quantify", + "observation.quantification.form.label": "Observasjon quantification", + "observation.detail.heading.placeholder": "Heading (What was found and where?)", + "observation.detail.body.placeholder": "Text body (more details, please)", + "observation.map.label": "Observation Map", + "observation.msg": "Observation", + "observation.selectcrop": "Select crop", + "observation.selectpest": "Select pest", + "observation.unregisteredpest": "Unknown to our database", + "photo.label.takephoto": "Take photo", + "photo.label.selectphoto": "Select photo", + "photo.modal.deleteprompt": "This image will be deleted", + "photo.modal.label.pleasechoose": "Please choose", + "photo.modal.observationphoto.label": "Observation photo", + "back.label": "Back", + "save.label": "Save", + "delete.label": "Delete", + "close.label": "Close", + "action.label": "Action", + "map.link.back.label": "Back", + "map.link.OK.label": "OK", + "map.link.cancel.label": "Cancel", + "mapobservation.label.selectPOI": "Select a Point Of Interest", + "mapobservation.label.noPOIselected": "No Point Of Interest selected", + "mapPOI.poiName.label": "Point Of Interest name", + "mapPOI.modal.label.saveprompt": "Save information for", + "mapPOI.modal.label.pleasechoose": "Please choose the option below", + "mapPOI.modal.label.deleteprompt": "Delete information for", + "mapPOI.modal.alert.missingGeoInfo": "Please set the location for this Point Of Interest", + "mapPOI.modal.alert.missingPOIType": "Please select the type of POI this is", + "mapPOI.modal.alert.missingPOIName": "Please enter a name for the Point Of Interest", + "General": "General", + "Farm": "Farm", + "Field": "Field", + "Weather station": "Weather station", + "places.search.label": "Search", + "placeslist.heading": "My places", + "crop.category.empty.alert": "Please select Crop category", + "cropcategory": "Crop category", + "alert.header.label": "!! ALERT !!", + "alert.header.error.label": "!! ERROR !!", + "alert.footer.label": "Please chose the option below", + "err.observation.date.empty": "Observation Date cannot be empty", + "err.observation.geoinfo": "Geolocation cannot be empty !!", + "err.observation.header.empty": "Observation details cannot be empty", + "err.observation.remove.msg": "This Observation will be removed. Are you sure ?", + "err.observation.remove.warn": "This Observation will be removed", + "sync": "Sync to server", + "settings.heading": "Settings", + "visibility.label.undefined": "Select visibility", + "visibility.label.private": "Private", + "visibility.label.public": "Public", + "index.header": "Field observations", + "about.header": "About", + "about.version": "Version", + "about.text": "<p>The app is developed by <a href='https://nibio.no/em' target='new'>NIBIO</a>'s <a href='https://www.vips-landbruk.no/'>VIPS team</a>.</p><p>VIPS project leader: <a href='https://www.nibio.no/en/employees/berit-nordskog' target='new'>Berit Nordskog</a></p><p>© 2022 <a href='https://nibio.no/en' target='new'>NIBIO</a></p>" } \ No newline at end of file diff --git a/src/locales/nb.json b/src/locales/nb.json index 0006a1cb83cdb55af2a8de8e6152b68d941ff352..49e990ae4ae285e1483540902d26c5c60dced96c 100644 --- a/src/locales/nb.json +++ b/src/locales/nb.json @@ -1,90 +1,71 @@ { - "observationlist.heading" : "Mine observasjoner", - "observationlist.alert.emptylist" : "Du har ingen registrerte observasjoner. For å registrere en ny, klikk på pluss-ikonet nede til høyre.", - - "welcome.header.text" : "VIPS feltobservasjonsapp", - "welcome.body.text" : "I denne appen kan du registrere, stedfeste og ta bilder av skadegjørere i landbruket. Registreringene kan brukes som førstefunn, inngå i modellberegninger og brukes til rapportering og forskning.<br/>For å bruke appen må du ha en brukerkonto på VIPS. Hvis du ikke har det, <a href='https://logic.vips.nibio.no/' target='new'>klikk her for å opprette</a>.<br/>Har du brukerkonto allerede? Klikk på menyikonet oppe til høyre for å logge inn.", - "welcome.footer.text" : "VIPS (Varsling Innen PlanteSkadegjørere) er utviklet av NIBIO og Norsk Landbruksrådgiving. Rådgivningstjenesten registrerer angrep av skadegjørere i felt, mens NIBIO leverer klimadata og tar seg av utvikling, drift og vedlikehold av systemet. Les mer på <a style='color: white; text-decoration:underline;' href='https://www.vips-landbruk.no/'>VIPS</a>", - - "login.button.label" : "Logg inn", - "logout.button.label" : "Logg ut", - "login.username.field.placeholder" : "Brukernavn", - "login.pwd.field.placeholder" : "Passord", - "login.systems.wrong.credential" : "Feil brukernavn og/eller passord. Vennligst prøv igjen.", - - "logout.header.text" : "Logg ut", - "logout.success.text" : "Du er nå utlogget", - - "observation.date.label" : "Observasjonsdato", - "observation.quantification.label" : "Tell/kvantifiser", - "observation.quantification.form.label" : "Observasjonsspesifikke detaljer", - "observation.detail.heading.placeholder" : "Overskrift (Hva er funnet hvor?)", - "observation.detail.body.placeholder" : "Brødtekst (Flere detaljer)", - "observation.map.label" : "Velg plassering", - "observation.msg" : "Registrer observasjon", - "observation.selectcrop" : "Velg kultur", - "observation.selectpest" : "Velg skadegjører", - "observation.unregisteredpest" : "Ukjent for VIPS-databasen", - - - "photo.label.takephoto" : "Ta bilde", - "photo.label.selectphoto" : "Velg bilde", - "photo.modal.deleteprompt" : "Bildet vil bli slettet", - "photo.modal.label.pleasechoose" : "Vennligst velg", - "photo.modal.observationphoto.label" : "Observasjonsbilde", - - "back.label" : "Tilbake", - "save.label" : "Lagre", - "delete.label" : "Slett", - "close.label" : "Lukk", - "action.label" : "Handling", - "map.link.back.label" : "Tilbake", - "map.link.OK.label" : "OK", - "map.link.cancel.label" : "Avbryt", - - "mapobservation.label.selectPOI" : "Velg et sted", - "mapobservation.label.noPOIselected" : "Sted ikke valgt", - "mapPOI.poiName.label" : "Stedsnavn", - "mapPOI.modal.label.saveprompt" : "Lagre informasjon for", - "mapPOI.modal.label.pleasechoose" : "Vennligst velg", - "mapPOI.modal.label.deleteprompt" : "Slette informasjon for", - "mapPOI.modal.alert.missingGeoInfo" : "Stedet mangler en plassering", - "mapPOI.modal.alert.missingPOIType" : "Vennligst angi hva slags type sted dette er", - "mapPOI.modal.alert.missingPOIName" : "Vennligst angi et navn på stedet", - - "General" : "Generelt", - "Farm" : "Gård", - "Field" : "Felt", - "Weather station" : "Målestasjon", - - "places.search.label" : "Søk", - "placeslist.heading" : "Mine steder", - - - "crop.category.empty.alert" : "For å kunne registrere observasjoner, må du først velge de kulturgruppene du ønsker å jobbe med", - "cropcategory" : "Kulturgrupper", - - "alert.header.label" : "!! ADVARSEL !!", - "alert.header.error.label" : "!! FEIL !!", - "alert.footer.label" : "Vennligst velg et av alternativene", - - "err.observation.date.empty" : "Observasjonsdato må være utfylt", - "err.observation.geoinfo" : "Lokalitet må være utfylt", - "err.observation.header.empty" : "Observasjonsdetaljer må være utfylt", - "err.observation.remove.msg" : "Observasjonen vil bli slettet. Er du helt sikker?", - "err.observation.remove.warn" : "Observasjonen vil bli slettet", - - "sync" : "Synkroniser med server", - "settings.heading" : "Innstillinger", - - "visibility.label.undefined" : "Velg synlighet", - "visibility.label.private" : "Privat", - "visibility.label.public" : "Offentlig", - - "index.header" : "Feltobservasjoner", - "about.header" : "Om appen", - "about.version" : "Versjon", - "about.text" : "<p>Appen er utviklet av <a href='https://nibio.no' target='new'>NIBIO</a>s <a href='https://www.vips-landbruk.no/'>VIPS-team</a>.</p><p>Prosjektleder for VIPS: <a href='https://www.nibio.no/ansatte/berit-nordskog' target='new'>Berit Nordskog</a></p><p>© 2022 <a href='https://nibio.no' target='new'>NIBIO</a></p>" - - + "observationlist.heading": "Mine observasjoner", + "observationlist.alert.emptylist": "Du har ingen registrerte observasjoner. For å registrere en ny, klikk på pluss-ikonet nede til høyre.", + "welcome.header.text": "VIPS feltobservasjonsapp", + "welcome.body.text": "I denne appen kan du registrere, stedfeste og ta bilder av skadegjørere i landbruket. Registreringene kan brukes som førstefunn, inngå i modellberegninger og brukes til rapportering og forskning.<br/>For å bruke appen må du ha en brukerkonto på VIPS. Hvis du ikke har det, <a href='https://logic.vips.nibio.no/' target='new'>klikk her for å opprette</a>.<br/>Har du brukerkonto allerede? Klikk på menyikonet oppe til høyre for å logge inn.", + "welcome.footer.text": "VIPS (Varsling Innen PlanteSkadegjørere) er utviklet av NIBIO og Norsk Landbruksrådgiving. Rådgivningstjenesten registrerer angrep av skadegjørere i felt, mens NIBIO leverer klimadata og tar seg av utvikling, drift og vedlikehold av systemet. Les mer på <a style='color: white; text-decoration:underline;' href='https://www.vips-landbruk.no/'>VIPS</a>", + "login.button.label": "Logg inn", + "logout.button.label": "Logg ut", + "login.username.field.placeholder": "Brukernavn", + "login.pwd.field.placeholder": "Passord", + "login.systems.wrong.credential": "Feil brukernavn og/eller passord. Vennligst prøv igjen.", + "logout.header.text": "Logg ut", + "logout.success.text": "Du er nå utlogget", + "observation.date.label": "Observasjonsdato", + "observation.quantification.label": "Tell/kvantifiser", + "observation.quantification.form.label": "Observasjonsspesifikke detaljer", + "observation.detail.heading.placeholder": "Overskrift (Hva er funnet hvor?)", + "observation.detail.body.placeholder": "Brødtekst (Flere detaljer)", + "observation.map.label": "Velg plassering", + "observation.msg": "Registrer observasjon", + "observation.selectcrop": "Velg kultur", + "observation.selectpest": "Velg skadegjører", + "observation.unregisteredpest": "Ukjent for VIPS-databasen", + "photo.label.takephoto": "Ta bilde", + "photo.label.selectphoto": "Velg bilde", + "photo.modal.deleteprompt": "Bildet vil bli slettet", + "photo.modal.label.pleasechoose": "Vennligst velg", + "photo.modal.observationphoto.label": "Observasjonsbilde", + "back.label": "Tilbake", + "save.label": "Lagre", + "delete.label": "Slett", + "close.label": "Lukk", + "action.label": "Handling", + "map.link.back.label": "Tilbake", + "map.link.OK.label": "OK", + "map.link.cancel.label": "Avbryt", + "mapobservation.label.selectPOI": "Velg et sted", + "mapobservation.label.noPOIselected": "Sted ikke valgt", + "mapPOI.poiName.label": "Stedsnavn", + "mapPOI.modal.label.saveprompt": "Lagre informasjon for", + "mapPOI.modal.label.pleasechoose": "Vennligst velg", + "mapPOI.modal.label.deleteprompt": "Slette informasjon for", + "mapPOI.modal.alert.missingGeoInfo": "Stedet mangler en plassering", + "mapPOI.modal.alert.missingPOIType": "Vennligst angi hva slags type sted dette er", + "mapPOI.modal.alert.missingPOIName": "Vennligst angi et navn på stedet", + "General": "Generelt", + "Farm": "Gård", + "Field": "Felt", + "Weather station": "Målestasjon", + "places.search.label": "Søk", + "placeslist.heading": "Mine steder", + "crop.category.empty.alert": "For å kunne registrere observasjoner, må du først velge de kulturgruppene du ønsker å jobbe med", + "cropcategory": "Kulturgrupper", + "alert.header.label": "!! ADVARSEL !!", + "alert.header.error.label": "!! FEIL !!", + "alert.footer.label": "Vennligst velg et av alternativene", + "err.observation.date.empty": "Observasjonsdato må være utfylt", + "err.observation.geoinfo": "Lokalitet må være utfylt", + "err.observation.header.empty": "Observasjonsdetaljer må være utfylt", + "err.observation.remove.msg": "Observasjonen vil bli slettet. Er du helt sikker?", + "err.observation.remove.warn": "Observasjonen vil bli slettet", + "sync": "Synkroniser med server", + "settings.heading": "Innstillinger", + "visibility.label.undefined": "Velg synlighet", + "visibility.label.private": "Privat", + "visibility.label.public": "Offentlig", + "index.header": "Feltobservasjoner", + "about.header": "Om appen", + "about.version": "Versjon", + "about.text": "<p>Appen er utviklet av <a href='https://nibio.no' target='new'>NIBIO</a>s <a href='https://www.vips-landbruk.no/'>VIPS-team</a>.</p><p>Prosjektleder for VIPS: <a href='https://www.nibio.no/ansatte/berit-nordskog' target='new'>Berit Nordskog</a></p><p>© 2022 <a href='https://nibio.no' target='new'>NIBIO</a></p>" } \ No newline at end of file