diff --git a/frontend/components/view/Images.vue b/frontend/components/view/Images.vue index 92a5ea9d2635dabc4b9cfd3106991592175e543e..ea03627e7f507404b3fee10898638f8f46be2930 100644 --- a/frontend/components/view/Images.vue +++ b/frontend/components/view/Images.vue @@ -1,8 +1,8 @@ <template> - <article class="blog-card"> - <img class="post-image" :src="imageURL"> + <article class="blog-card" :class="{'disable': isCurrentlyLabeled}"> + <img class="post-image" :class="{'post-image-disabled': isCurrentlyLabeled}" :src="imageURL"> <div class="article-details"> - <h3 class="post-title"> + <h3 class="post-title" :class="{'post-title-disabled': isCurrentlyLabeled}"> {{ dataImageName }} </h3> </div> @@ -23,6 +23,10 @@ export default { imageName: { type: String, default: '' + }, + isCurrentlyLabeled: { + type: Boolean, + default: false } }, data () { @@ -62,6 +66,17 @@ export default { cursor: pointer; } +.disable { + background-color: #F0F0F0; + -webkit-box-shadow: 0 0 0 0px rgba(0,0,0,0.0); + -moz-box-shadow: 0 0 0 0px rgba(0,0,0,0.0); + box-shadow: 0 0 0 0px rgba(0,0,0,0.0); +} + +.blog-card:hover { + background: #F0F0F0; +} + .post-image { display: block; width: 100%; @@ -69,6 +84,10 @@ export default { height: 100%; } +.post-image-disabled { + opacity: 0.5; +} + .article-details { padding: 1.7rem; } @@ -81,4 +100,8 @@ export default { margin: 0 0 0 0; } +.post-title-disabled { + color: #D6D6D6; +} + </style> \ No newline at end of file diff --git a/frontend/components/view/Suggest.vue b/frontend/components/view/Suggest.vue index 9af9ba3b3a02ebee331c0dacffa42b414e82f1ed..feb63a1d4ccf68bb79cb9d303080e3f8b2637947 100644 --- a/frontend/components/view/Suggest.vue +++ b/frontend/components/view/Suggest.vue @@ -34,9 +34,11 @@ export default { }, async mounted () { const response = await this.$axios.get('/api/content/?suggestion=').catch((error) => console.error(error)) - response.data.data.forEach((content) => { - this.simpleSuggestionList.push(content['content_name']) - }) + if (response.data.data) { + response.data.data.forEach((content) => { + this.simpleSuggestionList.push(content['content_name']) + }) + } this.enableForm() this.$watch(() => { if (this.$refs["form"].isInFocus) { diff --git a/frontend/pages/main/label.vue b/frontend/pages/main/label.vue index 64e725c5c8ab0644dd69c71e34792b0909d29a45..d4aff8c4218bd51aff5f72ff18927798c41f1e7e 100644 --- a/frontend/pages/main/label.vue +++ b/frontend/pages/main/label.vue @@ -22,18 +22,18 @@ </div> </div> <br> - <b-row> - <b-col v-for="image in images" :key="image.id"> + <b-row v-if="images" :key="updateUI"> + <b-col v-for="id in Object.keys(images)" :key="id"> <div id="container"> - <nuxt-link :to="{ path: '/viewer', query: { url: image.url, id: image.id }}"> - <Images - class="animated fast fadeIn" - :src="image.url" - :image-i-d="image.id" - :image-u-r-l="image.url" - :image-name="image.name" - /> - </nuxt-link> + <Images + class="animated fadeIn" + :src="images[id].url" + :image-i-d="images[id].id" + :image-u-r-l="images[id].url" + :image-name="images[id].name" + :is-currently-labeled="images[id].isCurrentlyLabeled" + @click.native="openViewer(images[id])" + /> <br> </div> </b-col> @@ -41,7 +41,7 @@ <b-pagination v-model="page" class="mt-3" - :total-rows="100" + :total-rows="totalRows" pills :per-page="perPage" /> @@ -59,20 +59,29 @@ export default { }, data () { return { - images: [], + images: {}, keyword: '', isViewerActive: false, - perPage: 12, - page: 1 + perPage: 2, + totalRows: 0, + page: 1, + timer: '', + updateUI: false } }, watch: { async page () { - await this.getAllImages(this.perPage, this.page, this.keyword) + this.images = {} + await this.getAllImagesWithLabelStatus() } }, - async mounted () { - await this.getAllImages(this.perPage, this.page, this.keyword) + async created () { + this.images = {} + await this.getAllImagesWithLabelStatus() + this.timer = setInterval(this.getAllImagesWithLabelStatus, 5000) + }, + beforeDestroy () { + clearInterval(this.timer) }, methods: { async getAllImages(perPage, page, keyword) { @@ -84,19 +93,67 @@ export default { search: keyword } }).catch((error) => console.error(error)) - this.images = [] - if (response.data.data) { - response.data.data.forEach((image) => { + console.log("Images: ", response.data.data.images) + if (response.data.data.images) { + this.totalRows = (response.data.data["total_page"] + 1) * this.perPage + response.data.data.images.forEach((image) => { if (!image.Labeled) { var imageObj = { id: image.ImageID, name: image.Filename, - url: backendURL + '/api/' + image.ImagePath + url: backendURL + '/api/' + image.ImagePath, + isCurrentlyLabeled: false + } + this.images[image.ImageID] = imageObj + console.log("Images: ", this.images) + } + }) + } + }, + async getImageCurrentlyBeingLabelled() { + var url = '/api/accesscontrol' + const response = await this.$axios.get(url).catch((error) => console.error(error)) + console.log("Response image labelled: ", response.data.data) + if (response.data.data) { + response.data.data.forEach(async (imageStatus) => { + // Delete access control + if (imageStatus["timeout"] < this.getCurrentTime()) { + await this.deleteImageAccessControlByImageID(imageStatus["image_id"]).catch((error) => console.error(error)) + } else { + if (this.images[imageStatus["image_id"]]) { + this.images[imageStatus["image_id"]].isCurrentlyLabeled = true } - this.images.push(imageObj) } }) } + this.updateUI = !this.updateUI + }, + async getAllImagesWithLabelStatus() { + await this.getAllImages(this.perPage, this.page, this.keyword) + await this.getImageCurrentlyBeingLabelled() + }, + getCurrentTime () { + var date = new Date() + return date.toISOString() + }, + async deleteImageAccessControlByImageID (imageID) { + var url = '/api/accesscontrol/' + imageID + try { + var response = await this.$axios.delete(url) + if (this.images[imageID]) { + this.images[imageID].isCurrentlyLabeled = false + } + return response.data.status + } catch (error) { + console.log("Label" , error) + throw error + } + }, + openViewer (image) { + console.log(image) + if (!image.isCurrentlyLabeled) { + this.$router.push({ path: '/viewer', query: { url: image.url, id: image.id }}) + } }, debounceWrapper (e) { this.page = 1 diff --git a/frontend/pages/viewer/index.vue b/frontend/pages/viewer/index.vue index 8ebb67d9604264058fcd76e2492fec2c95bf461d..54ef8832ac6129fcf863a8b1f744c5a0288fccf2 100644 --- a/frontend/pages/viewer/index.vue +++ b/frontend/pages/viewer/index.vue @@ -83,15 +83,40 @@ export default { url: '' }, canDelete: true, - labelCount: 0 + labelCount: 0, + timer: '' } }, + async created () { + await this.startHeartBeat() + this.timer = setInterval(this.startHeartBeat, 5000) + }, mounted () { this.image.url = this.$route.query.url this.image.id = this.$route.query.id }, + async beforeDestroy () { + try { + await this.deleteImageAccessControlByImageID(this.image.id) + } catch(error) { + console.log(error) + } + + clearInterval(this.timer) + }, methods: { - closeViewer () { + async startHeartBeat() { + var url = '/api/accesscontrol/requestaccess/' + parseInt(this.$route.query.id) + // alert(url) + try { + await this.$axios.get(url).catch((error) => console.error(error)) + } catch (error) { + this.showFailedAlert("An error occured", error) + await this.closeViewer() + } + }, + async closeViewer () { + await this.deleteImageAccessControlByImageID(this.image.id) this.$router.push('/main/label') }, onMouseDownHandler (e) { @@ -197,8 +222,8 @@ export default { try { console.log("LABEL PAYLOAD: ", labelPayload) await this.createAllLabelsInImage(labelPayload) - this.showSuccessAlert("Success!", "Image has been saved!").then(() => { - this.closeViewer() + this.showSuccessAlert("Success!", "Image has been saved!").then(async () => { + await this.closeViewer() }) } catch (error) { console.log(error) @@ -293,6 +318,16 @@ export default { console.log("Label" , error) throw error } + }, + async deleteImageAccessControlByImageID (imageID) { + var url = '/api/accesscontrol/' + imageID + try { + var response = await this.$axios.delete(url) + return response.data.status + } catch (error) { + console.log("Label" , error) + throw error + } } } }