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
+      }
     }
   }
 }