You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

297 lines
7.7 KiB

4 years ago
  1. <template>
  2. <v-app>
  3. <the-header>
  4. <template #leftDrawerIcon>
  5. <v-app-bar-nav-icon @click="drawerLeft = !drawerLeft" />
  6. </template>
  7. </the-header>
  8. <v-navigation-drawer
  9. v-model="drawerLeft"
  10. app
  11. clipped
  12. color=""
  13. >
  14. <the-side-bar
  15. :link="getLink"
  16. :role="getCurrentUserRole"
  17. />
  18. </v-navigation-drawer>
  19. <v-main>
  20. <v-overlay :value="loading">
  21. <v-progress-circular indeterminate size="64" />
  22. </v-overlay>
  23. <v-snackbar
  24. v-model="snackbar"
  25. >
  26. {{ text }}
  27. <template v-slot:action="{ attrs }">
  28. <v-btn
  29. color="pink"
  30. text
  31. v-bind="attrs"
  32. @click="snackbar = false"
  33. >
  34. Close
  35. </v-btn>
  36. </template>
  37. </v-snackbar>
  38. <v-container
  39. v-if="currentDoc && !loading"
  40. fluid
  41. >
  42. <v-row
  43. no-gutters
  44. class="d-none d-sm-flex"
  45. >
  46. <v-col>
  47. <approve-button
  48. v-if="canViewApproveButton"
  49. v-model="page"
  50. :length="total"
  51. :approved="approved"
  52. :disabled="currentDoc ? false : true"
  53. />
  54. <filter-button
  55. v-model="filterOption"
  56. />
  57. <guideline-button />
  58. <clear-annotations-button />
  59. <v-tooltip bottom>
  60. <template v-slot:activator="{ on }">
  61. <v-btn
  62. class="text-capitalize ps-1 pe-1"
  63. min-width="36"
  64. icon
  65. v-on="on"
  66. @click="dialogComment=true"
  67. >
  68. <v-icon>
  69. mdi-chat
  70. </v-icon>
  71. </v-btn>
  72. </template>
  73. <span>{{ $t('annotation.commentTooltip') }}</span>
  74. </v-tooltip>
  75. <settings
  76. v-model="options"
  77. :errors="errors"
  78. />
  79. <v-dialog
  80. v-model="dialogComment"
  81. width="800"
  82. >
  83. <form-create-comment
  84. @cancel="dialogComment=false"
  85. />
  86. </v-dialog>
  87. </v-col>
  88. <v-spacer />
  89. <v-col>
  90. <pagination
  91. v-model="page"
  92. :length="total"
  93. />
  94. </v-col>
  95. </v-row>
  96. <v-row justify="center">
  97. <v-col cols="12" md="9">
  98. <nuxt />
  99. </v-col>
  100. <v-col cols="12" md="3">
  101. <metadata-box
  102. :metadata="JSON.parse(currentDoc.meta)"
  103. />
  104. </v-col>
  105. </v-row>
  106. </v-container>
  107. <v-container
  108. v-else
  109. fill-height
  110. >
  111. <v-layout align-center>
  112. <v-flex text-center>
  113. <h1 class="display-2 primary--text">
  114. Whoops, data is empty.
  115. </h1>
  116. <p>Please upload your dataset.</p>
  117. </v-flex>
  118. </v-layout>
  119. </v-container>
  120. </v-main>
  121. <bottom-navigator
  122. v-model="page"
  123. :length="total"
  124. class="d-flex d-sm-none"
  125. />
  126. </v-app>
  127. </template>
  128. <script>
  129. import { mapActions, mapGetters, mapState, mapMutations } from 'vuex'
  130. import BottomNavigator from '@/components/containers/annotation/BottomNavigator'
  131. import ClearAnnotationsButton from '@/components/containers/annotation/ClearAnnotationsButton.vue'
  132. import GuidelineButton from '@/components/containers/annotation/GuidelineButton'
  133. import MetadataBox from '@/components/organisms/annotation/MetadataBox'
  134. import FilterButton from '@/components/containers/annotation/FilterButton'
  135. import ApproveButton from '@/components/containers/annotation/ApproveButton'
  136. import FormCreateComment from '../components/comment/FormCRUD.vue'
  137. import Pagination from '~/components/containers/annotation/Pagination'
  138. import TheHeader from '~/components/organisms/layout/TheHeader'
  139. import TheSideBar from '~/components/organisms/layout/TheSideBar'
  140. import Settings from '~/components/containers/annotation/Settings.vue'
  141. export default {
  142. middleware: ['check-auth', 'auth', 'set-project'],
  143. components: {
  144. TheSideBar,
  145. TheHeader,
  146. BottomNavigator,
  147. Pagination,
  148. GuidelineButton,
  149. FilterButton,
  150. ApproveButton,
  151. MetadataBox,
  152. ClearAnnotationsButton,
  153. FormCreateComment,
  154. Settings
  155. },
  156. fetch() {
  157. this.getDocumentList({
  158. projectId: this.$route.params.id,
  159. limit: this.limit,
  160. offset: this.offset,
  161. q: this.$route.query.q,
  162. isChecked: this.filterOption,
  163. filterName: this.getFilterOption
  164. })
  165. },
  166. data() {
  167. return {
  168. dialogComment: false,
  169. drawerLeft: null,
  170. limit: 10,
  171. options: {
  172. onAutoLabeling: false
  173. },
  174. errors: {
  175. 'autoLabelingConfig': ''
  176. },
  177. snackbar: false,
  178. text: ''
  179. }
  180. },
  181. computed: {
  182. ...mapGetters('projects', ['getLink', 'getCurrentUserRole', 'getFilterOption', 'canViewApproveButton']),
  183. ...mapState('documents', ['loading', 'total']),
  184. ...mapGetters('documents', ['currentDoc', 'approved']),
  185. page: {
  186. get() {
  187. return parseInt(this.$route.query.page, 10)
  188. },
  189. set(value) {
  190. this.$router.push({
  191. query: {
  192. isChecked: this.$route.query.isChecked,
  193. page: parseInt(value, 10),
  194. q: this.$route.query.q
  195. }
  196. })
  197. }
  198. },
  199. filterOption: {
  200. get() {
  201. return this.$route.query.isChecked
  202. },
  203. set(value) {
  204. this.$router.push({
  205. query: {
  206. isChecked: value,
  207. page: 1,
  208. q: this.$route.query.q
  209. }
  210. })
  211. }
  212. },
  213. offset() {
  214. return Math.floor((this.page - 1) / this.limit) * this.limit
  215. },
  216. current() {
  217. return (this.page - 1) % this.limit
  218. },
  219. searchOptions() {
  220. // a bit tricky technique to capture variables change simultaneously.
  221. // see https://github.com/vuejs/vue/issues/844#issuecomment-265315349
  222. return JSON.stringify({
  223. page: this.page,
  224. q: this.$route.query.q,
  225. isChecked: this.filterOption
  226. })
  227. }
  228. },
  229. watch: {
  230. total() {
  231. // To validate the range of page variable on reloading the annotation page.
  232. if (this.total !== 0 && this.page > this.total) {
  233. this.$router.push({
  234. path: this.localePath(`/projects/${this.$route.params.id}/`)
  235. })
  236. }
  237. },
  238. offset() {
  239. this.$fetch()
  240. },
  241. filterOption() {
  242. this.page = 1
  243. this.$fetch()
  244. },
  245. current: {
  246. async handler() {
  247. this.setCurrent(this.current)
  248. if (this.options.onAutoLabeling) {
  249. try {
  250. this.setLoading(true)
  251. await this.autoLabeling({ projectId: this.$route.params.id })
  252. } catch (e) {
  253. this.snackbar = true
  254. this.text = e.response.data.detail
  255. } finally {
  256. this.setLoading(false)
  257. }
  258. }
  259. },
  260. immediate: true
  261. },
  262. searchOptions() {
  263. this.saveSearchOptions(JSON.parse(this.searchOptions))
  264. },
  265. async "options.onAutoLabeling"(val) {
  266. if (val) {
  267. try {
  268. this.setLoading(true)
  269. await this.autoLabeling({ projectId: this.$route.params.id })
  270. this.errors.autoLabelingConfig = ''
  271. } catch (e) {
  272. this.errors.autoLabelingConfig = e.response.data.detail
  273. this.options.onAutoLabeling = false
  274. } finally {
  275. this.setLoading(false)
  276. }
  277. }
  278. }
  279. },
  280. methods: {
  281. ...mapActions('documents', ['getDocumentList', 'autoLabeling']),
  282. ...mapMutations('documents', ['setCurrent', 'setLoading']),
  283. ...mapMutations('projects', ['saveSearchOptions'])
  284. }
  285. }
  286. </script>