@konfuzio/document-validation-ui 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/.eslintrc.js +11 -0
  2. package/.prettierrc.json +1 -0
  3. package/LICENSE +21 -0
  4. package/README.md +13 -0
  5. package/dist/css/app.0c8973f8.css +1 -0
  6. package/dist/css/chunk-vendors.053b6b6e.css +5 -0
  7. package/dist/favicon.ico +0 -0
  8. package/dist/index.html +1 -0
  9. package/dist/js/app.17fe48c4.js +2 -0
  10. package/dist/js/app.17fe48c4.js.map +1 -0
  11. package/dist/js/chunk-vendors.a48fca3f.js +47 -0
  12. package/dist/js/chunk-vendors.a48fca3f.js.map +1 -0
  13. package/jest.config.js +4 -0
  14. package/package.json +60 -0
  15. package/src/.DS_Store +0 -0
  16. package/src/api.js +49 -0
  17. package/src/assets/images/AcceptedCheckMark.vue +8 -0
  18. package/src/assets/images/AcceptedUser.vue +8 -0
  19. package/src/assets/images/ActionIcon.vue +60 -0
  20. package/src/assets/images/ArrowDownKey.vue +11 -0
  21. package/src/assets/images/ArrowUpKey.vue +11 -0
  22. package/src/assets/images/CategoryIconImg.vue +13 -0
  23. package/src/assets/images/CheckMark.vue +8 -0
  24. package/src/assets/images/EditDocIcon.vue +12 -0
  25. package/src/assets/images/EmptyStateImg.vue +129 -0
  26. package/src/assets/images/ErrorIcon.vue +28 -0
  27. package/src/assets/images/EyeIcon.vue +11 -0
  28. package/src/assets/images/FileNameNotSavedImage.vue +26 -0
  29. package/src/assets/images/FileNameSavedImage.vue +14 -0
  30. package/src/assets/images/FitZoomIcon.vue +16 -0
  31. package/src/assets/images/KeyboardIcon.vue +16 -0
  32. package/src/assets/images/MinusIcon.vue +13 -0
  33. package/src/assets/images/NotOptimizedIllustration.vue +651 -0
  34. package/src/assets/images/PlusIcon.vue +13 -0
  35. package/src/assets/images/QuestionMark.vue +12 -0
  36. package/src/assets/images/ServerImage.vue +63 -0
  37. package/src/assets/images/SplitLines.vue +18 -0
  38. package/src/assets/images/SplitZigZag.vue +16 -0
  39. package/src/assets/images/StatusImg.vue +14 -0
  40. package/src/assets/images/UserIcon.vue +8 -0
  41. package/src/assets/scss/annotation_details.scss +126 -0
  42. package/src/assets/scss/categorize_modal.scss +42 -0
  43. package/src/assets/scss/choose_label_set_modal.scss +62 -0
  44. package/src/assets/scss/document_action_bar.scss +37 -0
  45. package/src/assets/scss/document_annotations.scss +472 -0
  46. package/src/assets/scss/document_category.scss +80 -0
  47. package/src/assets/scss/document_dashboard.scss +47 -0
  48. package/src/assets/scss/document_dataset_status.scss +46 -0
  49. package/src/assets/scss/document_edit.scss +431 -0
  50. package/src/assets/scss/document_error.scss +81 -0
  51. package/src/assets/scss/document_handover.scss +200 -0
  52. package/src/assets/scss/document_name.scss +62 -0
  53. package/src/assets/scss/document_page.scss +8 -0
  54. package/src/assets/scss/document_thumbnails.scss +41 -0
  55. package/src/assets/scss/document_toolbar.scss +89 -0
  56. package/src/assets/scss/document_top_bar.scss +139 -0
  57. package/src/assets/scss/document_viewport_modal.scss +25 -0
  58. package/src/assets/scss/documents_list.scss +130 -0
  59. package/src/assets/scss/empty_state.scss +34 -0
  60. package/src/assets/scss/extracting_data.scss +35 -0
  61. package/src/assets/scss/imports.scss +1 -0
  62. package/src/assets/scss/main.scss +24 -0
  63. package/src/assets/scss/multi_ann_table_popup.scss +12 -0
  64. package/src/assets/scss/new_annotation.scss +86 -0
  65. package/src/assets/scss/scrolling_document.scss +19 -0
  66. package/src/assets/scss/variables.scss +696 -0
  67. package/src/components/App.vue +112 -0
  68. package/src/components/DocumentAnnotations/ActionButtons.vue +237 -0
  69. package/src/components/DocumentAnnotations/AnnotationContent.vue +249 -0
  70. package/src/components/DocumentAnnotations/AnnotationDetails.vue +292 -0
  71. package/src/components/DocumentAnnotations/AnnotationRow.vue +616 -0
  72. package/src/components/DocumentAnnotations/CategorizeModal.vue +159 -0
  73. package/src/components/DocumentAnnotations/ChooseLabelSetModal.vue +155 -0
  74. package/src/components/DocumentAnnotations/DocumentAnnotations.vue +502 -0
  75. package/src/components/DocumentAnnotations/DocumentLabel.vue +148 -0
  76. package/src/components/DocumentAnnotations/EmptyAnnotation.vue +222 -0
  77. package/src/components/DocumentAnnotations/EmptyState.vue +21 -0
  78. package/src/components/DocumentAnnotations/ExtractingData.vue +29 -0
  79. package/src/components/DocumentAnnotations/LoadingAnnotations.vue +43 -0
  80. package/src/components/DocumentAnnotations/LoadingLabels.vue +43 -0
  81. package/src/components/DocumentAnnotations/RejectedLabels.vue +96 -0
  82. package/src/components/DocumentAnnotations/index.js +8 -0
  83. package/src/components/DocumentCategory.vue +156 -0
  84. package/src/components/DocumentDashboard.vue +159 -0
  85. package/src/components/DocumentEdit/DocumentEdit.vue +279 -0
  86. package/src/components/DocumentEdit/EditPages.vue +213 -0
  87. package/src/components/DocumentEdit/EditSidebar.vue +118 -0
  88. package/src/components/DocumentEdit/SplitOverview.vue +182 -0
  89. package/src/components/DocumentEdit/index.js +4 -0
  90. package/src/components/DocumentError.vue +53 -0
  91. package/src/components/DocumentPage/ActionBar.vue +48 -0
  92. package/src/components/DocumentPage/BoxSelection.vue +149 -0
  93. package/src/components/DocumentPage/DocumentPage.vue +517 -0
  94. package/src/components/DocumentPage/DocumentToolbar.vue +145 -0
  95. package/src/components/DocumentPage/DummyPage.vue +53 -0
  96. package/src/components/DocumentPage/MultiAnnSelection.vue +302 -0
  97. package/src/components/DocumentPage/MultiAnnotationTablePopup.vue +253 -0
  98. package/src/components/DocumentPage/NewAnnotation.vue +283 -0
  99. package/src/components/DocumentPage/ScrollingDocument.vue +108 -0
  100. package/src/components/DocumentPage/ScrollingPage.vue +184 -0
  101. package/src/components/DocumentPage/index.js +5 -0
  102. package/src/components/DocumentThumbnails/DocumentThumbnails.vue +92 -0
  103. package/src/components/DocumentThumbnails/LoadingThumbnail.vue +25 -0
  104. package/src/components/DocumentThumbnails/index.js +1 -0
  105. package/src/components/DocumentTopBar/DocumentDatasetStatus.vue +103 -0
  106. package/src/components/DocumentTopBar/DocumentHandover.vue +202 -0
  107. package/src/components/DocumentTopBar/DocumentName.vue +224 -0
  108. package/src/components/DocumentTopBar/DocumentTopBar.vue +144 -0
  109. package/src/components/DocumentTopBar/DocumentTopBarButtons.vue +148 -0
  110. package/src/components/DocumentTopBar/KeyboardActionsDescription.vue +71 -0
  111. package/src/components/DocumentTopBar/index.js +5 -0
  112. package/src/components/DocumentsList/DocumentsList.vue +126 -0
  113. package/src/components/DocumentsList/index.js +1 -0
  114. package/src/components/ErrorMessage.vue +40 -0
  115. package/src/components/NotOptimizedViewportModal.vue +54 -0
  116. package/src/constants.js +4 -0
  117. package/src/directives/scroll.js +28 -0
  118. package/src/i18n.js +23 -0
  119. package/src/locales/de.json +114 -0
  120. package/src/locales/en.json +114 -0
  121. package/src/locales/es.json +113 -0
  122. package/src/main.js +87 -0
  123. package/src/store/category.js +193 -0
  124. package/src/store/display.js +238 -0
  125. package/src/store/document.js +1057 -0
  126. package/src/store/edit.js +210 -0
  127. package/src/store/index.js +22 -0
  128. package/src/store/project.js +95 -0
  129. package/src/store/selection.js +179 -0
  130. package/src/utils/utils.js +3 -0
  131. package/vue.config.js +13 -0
@@ -0,0 +1,502 @@
1
+ <template>
2
+ <div class="labels-sidebar">
3
+ <!-- When extracting annotations after editing -->
4
+ <div v-if="recalculatingAnnotations">
5
+ <ExtractingData />
6
+ </div>
7
+
8
+ <!-- When document data is still loading -->
9
+ <div v-else-if="!annotationSets || loading">
10
+ <div v-for="n in numberOfLoadingAnnotations" :key="n">
11
+ <LoadingAnnotations />
12
+ </div>
13
+ </div>
14
+
15
+ <!-- When there's no annotations in the label -->
16
+ <div v-else-if="annotationSets.length === 0">
17
+ <CategorizeModal v-if="!publicView" />
18
+ <EmptyState />
19
+ </div>
20
+
21
+ <div
22
+ v-else
23
+ :class="[
24
+ 'annotation-set-list',
25
+ missingAnnotations.length && !publicView && 'showing-rejected',
26
+ ]"
27
+ >
28
+ <CategorizeModal v-if="!publicView" />
29
+ <div
30
+ v-for="(annotationSet, indexGroup) in annotationSets"
31
+ :key="indexGroup"
32
+ class="annotation-set-group"
33
+ >
34
+ <div class="label-set-header">
35
+ <div class="label-set-name">
36
+ {{
37
+ `${annotationSet.label_set.name} ${numberOfAnnotationSetGroup(
38
+ annotationSet
39
+ )}`
40
+ }}
41
+ </div>
42
+ <div class="labelset-action-buttons">
43
+ <ActionButtons
44
+ :reject-all-empty-btn="true"
45
+ :annotation-set="annotationSet"
46
+ :accept-all-btn="true"
47
+ @reject-all-empty="
48
+ rejectMissingAnnotations(null, null, annotationSet, true)
49
+ "
50
+ @hover-annotation-set-to-reject="
51
+ handleHoverAnnotationSet(annotationSet, 'reject')
52
+ "
53
+ @leave-annotation-set-to-reject="handleHoverAnnotationSet(null)"
54
+ @accept-group="acceptGroup(annotationSet)"
55
+ @hover-annotation-set-to-accept="
56
+ handleHoverAnnotationSet(annotationSet, 'accept')
57
+ "
58
+ @leave-annotation-set-to-accept="handleHoverAnnotationSet(null)"
59
+ />
60
+ </div>
61
+ </div>
62
+
63
+ <div v-for="label in annotationSet.labels" :key="label.id">
64
+ <div v-if="labelNotRejected(annotationSet, label)" class="labels">
65
+ <DocumentLabel
66
+ :label="label"
67
+ :annotation-set="annotationSet"
68
+ :index-group="indexGroup"
69
+ @handle-reject="rejectMissingAnnotations"
70
+ />
71
+ </div>
72
+ </div>
73
+ </div>
74
+ </div>
75
+
76
+ <div
77
+ v-if="!publicView && missingAnnotations.length"
78
+ class="rejected-labels-list"
79
+ >
80
+ <RejectedLabels :missing-annotations="missingAnnotations" />
81
+ </div>
82
+ </div>
83
+ </template>
84
+ <script>
85
+ import { mapGetters, mapState } from "vuex";
86
+ import EmptyState from "./EmptyState";
87
+ import ExtractingData from "./ExtractingData";
88
+ import ActionButtons from "./ActionButtons";
89
+ import DocumentLabel from "./DocumentLabel";
90
+ import RejectedLabels from "./RejectedLabels";
91
+ import LoadingAnnotations from "./LoadingAnnotations";
92
+ import CategorizeModal from "./CategorizeModal";
93
+
94
+ /**
95
+ * This component loads all annotations for one document
96
+ */
97
+ export default {
98
+ components: {
99
+ EmptyState,
100
+ ExtractingData,
101
+ ActionButtons,
102
+ DocumentLabel,
103
+ RejectedLabels,
104
+ LoadingAnnotations,
105
+ CategorizeModal,
106
+ },
107
+ data() {
108
+ return {
109
+ count: 0,
110
+ jumpToNextAnnotation: false,
111
+ numberOfLoadingAnnotations: 3,
112
+ };
113
+ },
114
+ computed: {
115
+ ...mapState("document", [
116
+ "documentId",
117
+ "recalculatingAnnotations",
118
+ "missingAnnotations",
119
+ "publicView",
120
+ "annotations",
121
+ "editAnnotation",
122
+ "annotationSets",
123
+ "loading",
124
+ "labels",
125
+ "selectedDocument",
126
+ ]),
127
+ ...mapGetters("category", ["category"]),
128
+ ...mapGetters("document", ["numberOfAnnotationSetGroup"]),
129
+ isAnnotationBeingEdited() {
130
+ return this.editAnnotation && this.editAnnotation.id;
131
+ },
132
+ },
133
+ watch: {
134
+ editAnnotation(newValue) {
135
+ if (!newValue && !this.jumpToNextAnnotation) {
136
+ this.count = 0;
137
+ }
138
+ },
139
+ acceptAnnotation(newValue, oldValue) {
140
+ // TODO: rework this to be more generic
141
+ if (!newValue && oldValue) {
142
+ this.focusOnNextAnnotation();
143
+ this.jumpToNextAnnotation = false;
144
+ }
145
+ },
146
+ },
147
+ watch: {
148
+ editAnnotation(newValue) {
149
+ if (!newValue && !this.jumpToNextAnnotation) {
150
+ this.count = 0;
151
+ }
152
+ },
153
+ acceptAnnotation(newValue, oldValue) {
154
+ // TODO: rework this to be more generic
155
+ if (!newValue && oldValue) {
156
+ this.focusOnNextAnnotation();
157
+ this.jumpToNextAnnotation = false;
158
+ }
159
+ },
160
+ },
161
+ created() {
162
+ window.addEventListener("keydown", this.keyDownHandler);
163
+ },
164
+ destroyed() {
165
+ window.removeEventListener("keydown", this.keyDownHandler);
166
+ },
167
+ methods: {
168
+ showMissingAnnotations() {
169
+ if (
170
+ (this.publicView &&
171
+ this.selectedDocument &&
172
+ this.selectedDocument.is_reviewed) ||
173
+ !this.publicView
174
+ ) {
175
+ return true;
176
+ } else {
177
+ return false;
178
+ }
179
+ },
180
+
181
+ focusOnNextAnnotation() {
182
+ const annotations = Array.from(
183
+ document.getElementsByClassName("annotation-value")
184
+ );
185
+ if (annotations[this.count]) {
186
+ annotations[this.count].click();
187
+ } else {
188
+ this.count = 0;
189
+ return;
190
+ }
191
+ },
192
+
193
+ scrollToFocusedAnnotationFromKeyHandler() {
194
+ if (!this.editAnnotation) return;
195
+
196
+ // Get label name for the selected annotation
197
+ let labelForAnnotation;
198
+
199
+ this.labels.map((label) => {
200
+ const found = label.annotations.find(
201
+ (ann) => ann.id === this.editAnnotation.id
202
+ );
203
+
204
+ if (found) {
205
+ labelForAnnotation = label;
206
+ return;
207
+ }
208
+ });
209
+
210
+ const currentAnnotation = this.annotations.find(
211
+ (ann) => ann.id === this.editAnnotation.id
212
+ );
213
+
214
+ if (currentAnnotation) {
215
+ this.$store.dispatch("document/setDocumentAnnotationSelected", {
216
+ annotation: currentAnnotation,
217
+ label: labelForAnnotation,
218
+ span: currentAnnotation.span[0],
219
+ scrollTo: false,
220
+ });
221
+
222
+ this.$store.dispatch("document/scrollToDocumentAnnotationSelected");
223
+ }
224
+ },
225
+
226
+ keyDownHandler(event) {
227
+ // only allow keyboard navigation if we are not in public view mode
228
+ if (this.publicView) return;
229
+
230
+ // get out of edit mode and navigation
231
+ if (event.key === "Escape") {
232
+ this.count = 0;
233
+ this.$store.dispatch("document/resetEditAnnotation");
234
+ this.$store.dispatch("selection/disableSelection");
235
+ this.$store.dispatch("document/endLoading");
236
+ return;
237
+ }
238
+
239
+ // Not allow starting edit mode with ArrowUp key
240
+ if (event.key === "ArrowUp" && !this.isAnnotationBeingEdited) return;
241
+
242
+ // Create an array from the elements selected
243
+ // for easier management of data
244
+ const annotations = Array.from(
245
+ document.getElementsByClassName("annotation-value")
246
+ );
247
+ // Get clicked element to get the index
248
+ const clickedAnnotations = Array.from(
249
+ document.getElementsByClassName("clicked")
250
+ );
251
+
252
+ // get index of currently active element
253
+ const currentAnnIndex = annotations.findIndex(
254
+ (el) => el === clickedAnnotations[0]
255
+ );
256
+
257
+ // navigate with the arrow up or down keys
258
+ if (event.key === "ArrowDown") {
259
+ if (this.count >= annotations.length) {
260
+ const finishBtn = Array.from(
261
+ document.getElementsByClassName("finish-review-btn")
262
+ );
263
+ finishBtn[0].focus();
264
+ this.$store.dispatch("document/resetEditAnnotation");
265
+ this.count = 0;
266
+ if (event.key === "Enter" && !finishBtn.disabled) {
267
+ finishBtn.click();
268
+ }
269
+ return;
270
+ }
271
+
272
+ /**
273
+ * Enable navigation when some annotation is already focused
274
+ */
275
+ // Update count to avoid restarting the navigation
276
+ if (clickedAnnotations[0]) {
277
+ this.count = currentAnnIndex + 1;
278
+ }
279
+
280
+ annotations[this.count].click();
281
+
282
+ // scroll to current annotation if not empty
283
+ this.scrollToFocusedAnnotationFromKeyHandler();
284
+
285
+ this.count++;
286
+ } else if (event.key === "ArrowUp") {
287
+ // Check if the event happened on the first element from the array
288
+ // If so, reset count to 0
289
+ if (clickedAnnotations[0] === annotations[0]) {
290
+ this.count = 0;
291
+ return;
292
+ }
293
+
294
+ /**
295
+ * Enable navigation when some annotation is already focused
296
+ */
297
+ // Update count to avoid restarting the navigation
298
+ if (clickedAnnotations[0]) {
299
+ this.count = currentAnnIndex - 1;
300
+ }
301
+
302
+ annotations[this.count].click();
303
+
304
+ // scroll to current annotation if not empty
305
+ this.scrollToFocusedAnnotationFromKeyHandler();
306
+
307
+ this.count--;
308
+ } else {
309
+ // Check for ENTER or DELETE
310
+ // Accept annotation
311
+ if (event.key === "Enter") {
312
+ if (!this.annotations || !this.editAnnotation) return;
313
+
314
+ const currentAnn = this.annotations.find(
315
+ (a) => a.id === this.editAnnotation.id
316
+ );
317
+
318
+ if (!this.editAnnotation && this.editAnnotation.id !== currentAnn.id)
319
+ return;
320
+ // set focus on next annotation
321
+ this.count = currentAnnIndex + 1;
322
+ this.jumpToNextAnnotation = true;
323
+ } else if (
324
+ event.key === "Delete" &&
325
+ annotations[currentAnnIndex].className.includes("label-empty") &&
326
+ annotations[currentAnnIndex].className.includes("clicked")
327
+ ) {
328
+ // Reject annotation
329
+ if (this.editAnnotation.id === annotations[currentAnnIndex].id) {
330
+ this.rejectMissingAnnotations();
331
+ }
332
+ this.jumpToNextAnnotation = true;
333
+ } else {
334
+ return;
335
+ }
336
+ }
337
+ },
338
+
339
+ labelNotRejected(annotationSet, label) {
340
+ // Check if the combined label and label set have been rejected
341
+ // or if the document is in public mode
342
+ if (
343
+ this.missingAnnotations.length === 0 ||
344
+ !this.showMissingAnnotations()
345
+ ) {
346
+ return true;
347
+ } else {
348
+ let found;
349
+
350
+ if (annotationSet && annotationSet.id) {
351
+ found = this.missingAnnotations.filter(
352
+ (el) =>
353
+ el.label === label.id && el.annotation_set === annotationSet.id
354
+ );
355
+ } else {
356
+ found = this.missingAnnotations.filter(
357
+ (el) =>
358
+ el.label === label.id &&
359
+ el.label_set === annotationSet.label_set.id
360
+ );
361
+ }
362
+
363
+ if (found.length !== 0) {
364
+ return false;
365
+ } else {
366
+ return true;
367
+ }
368
+ }
369
+ },
370
+
371
+ rejectMissingAnnotations(label, labelSet, annotationSet, rejectAll) {
372
+ let rejected;
373
+
374
+ if (label && labelSet && !rejectAll) {
375
+ // if single rejection is triggered by clicking the button
376
+
377
+ rejected = [
378
+ {
379
+ document: parseInt(this.documentId),
380
+ label: label,
381
+ label_set: labelSet,
382
+ annotation_set: annotationSet,
383
+ },
384
+ ];
385
+ } else if (this.editAnnotation && this.editAnnotation.id !== null) {
386
+ // if single rejection is triggered from "delete" key
387
+
388
+ rejected = {
389
+ document: parseInt(this.documentId),
390
+ label: this.editAnnotation.label,
391
+ label_set: this.editAnnotation.labelSet,
392
+ annotation_set: this.editAnnotation.annotationSet,
393
+ };
394
+ } else if (annotationSet && rejectAll) {
395
+ // reject all labels in annotation set
396
+
397
+ const allEmptyLabels = annotationSet.labels.filter(
398
+ (label) => label.annotations.length === 0
399
+ );
400
+
401
+ // Check if any of the empty annotations was already rejected individually
402
+ // and remove them
403
+ const toReject = [];
404
+
405
+ allEmptyLabels.map((label) => {
406
+ const found = this.missingAnnotations.find(
407
+ (l) => l.label === label.id && l.annotation_set === annotationSet.id
408
+ );
409
+
410
+ if (!found) {
411
+ toReject.push(label);
412
+ }
413
+ });
414
+
415
+ rejected = toReject.map((label) => {
416
+ return {
417
+ document: parseInt(this.documentId),
418
+ label: label.id,
419
+ label_set: annotationSet.label_set.id,
420
+ annotation_set: annotationSet.id,
421
+ };
422
+ });
423
+ }
424
+
425
+ this.$store.dispatch("document/setRejectedMissingAnnotations", rejected);
426
+
427
+ this.$store
428
+ .dispatch("document/addMissingAnnotations", rejected)
429
+ .then((response) => {
430
+ if (response) {
431
+ this.jumpToNextAnnotation = true;
432
+ }
433
+
434
+ this.jumpToNextAnnotation = false;
435
+ })
436
+ .catch((error) => {
437
+ this.$store.dispatch("document/createErrorMessage", {
438
+ error,
439
+ serverErrorMessage: this.$t("server_error"),
440
+ defaultErrorMessage: this.$t("edit_error"),
441
+ });
442
+ })
443
+ .finally(() => {
444
+ this.$store.dispatch("document/setRejectedMissingAnnotations", null);
445
+ });
446
+ },
447
+
448
+ handleHoverAnnotationSet(annotationSet, type) {
449
+ let hovered;
450
+
451
+ if (!type && !annotationSet) {
452
+ hovered = null;
453
+ } else {
454
+ hovered = {
455
+ annotationSet: annotationSet,
456
+ type: type,
457
+ };
458
+ }
459
+
460
+ this.$store.dispatch("document/setHoveredAnnotationSet", hovered);
461
+ },
462
+
463
+ acceptGroup(annotationSet) {
464
+ const annotationsToAccept = [];
465
+
466
+ annotationSet.labels.map((label) => {
467
+ if (label.annotations.length !== 0) {
468
+ label.annotations.map((ann) => {
469
+ if (!ann.revised) {
470
+ annotationsToAccept.push(ann.id);
471
+ }
472
+ });
473
+ }
474
+ });
475
+
476
+ if (annotationsToAccept.length !== 0) {
477
+ const acceptedAnnotations = {
478
+ ids: annotationsToAccept,
479
+ is_correct: true,
480
+ revised: true,
481
+ };
482
+
483
+ this.$store
484
+ .dispatch("document/updateMultipleAnnotations", acceptedAnnotations)
485
+ .catch((error) => {
486
+ this.$store.dispatch("document/createErrorMessage", {
487
+ error,
488
+ serverErrorMessage: this.$t("server_error"),
489
+ defaultErrorMessage: this.$t("edit_error"),
490
+ });
491
+ });
492
+ }
493
+ },
494
+ },
495
+ };
496
+ </script>
497
+
498
+ <style
499
+ scoped
500
+ lang="scss"
501
+ src="../../assets/scss/document_annotations.scss"
502
+ ></style>
@@ -0,0 +1,148 @@
1
+ <template>
2
+ <div class="label">
3
+ <div v-if="enableGroupingFeature && isMultipleAnnotations">
4
+ <div class="label-group" @click.stop="toggleGroup">
5
+ <div class="label-group-left">
6
+ <b-icon
7
+ :icon="showAnnotationsGroup ? 'angle-up' : 'angle-down'"
8
+ class="is-small caret"
9
+ />
10
+ <div class="label-name">
11
+ <span>{{ `${label.name} (${label.annotations.length})` }}</span>
12
+ </div>
13
+ </div>
14
+ <div class="label-group-right">
15
+ <div class="label-annotations-pending">
16
+ {{
17
+ `${
18
+ label.annotations.length - acceptedAnnotationsGroupCounter
19
+ } ${$t("annotations_pending")}`
20
+ }}
21
+ </div>
22
+ <div class="label-annotations-accepted">
23
+ {{
24
+ `${acceptedAnnotationsGroupCounter} ${$t("annotations_accepted")}`
25
+ }}
26
+ </div>
27
+ </div>
28
+ </div>
29
+ <div v-if="showAnnotationsGroup" class="label-group-annotation-list">
30
+ <AnnotationRow
31
+ v-for="annotation in label.annotations"
32
+ :key="annotation.id"
33
+ :annotation="annotation"
34
+ :label="label"
35
+ :annotation-set="annotationSet"
36
+ />
37
+ </div>
38
+ </div>
39
+ <div v-else-if="!enableGroupingFeature && hasAnnotations">
40
+ <AnnotationRow
41
+ v-for="annotation in label.annotations"
42
+ :key="annotation.id"
43
+ :annotation="annotation"
44
+ :label="label"
45
+ :annotation-set="annotationSet"
46
+ />
47
+ </div>
48
+ <div v-else>
49
+ <AnnotationRow
50
+ :annotation="singleAnnotation"
51
+ :label="label"
52
+ :annotation-set="annotationSet"
53
+ />
54
+ </div>
55
+ </div>
56
+ </template>
57
+ <script>
58
+ import { mapGetters, mapState } from "vuex";
59
+ import AnnotationRow from "./AnnotationRow";
60
+
61
+ /**
62
+ * This component shows each label and its annotations
63
+ */
64
+ export default {
65
+ name: "DocumentLabel",
66
+ components: { AnnotationRow },
67
+ props: {
68
+ label: {
69
+ required: true,
70
+ },
71
+ annotationSet: {
72
+ required: true,
73
+ },
74
+ },
75
+ data() {
76
+ return {
77
+ isMultipleAnnotations: false,
78
+ acceptedAnnotationsGroupCounter: 0,
79
+ showAnnotationsGroup: false,
80
+ };
81
+ },
82
+ computed: {
83
+ ...mapState("document", [
84
+ "sidebarAnnotationSelected",
85
+ "enableGroupingFeature",
86
+ ]),
87
+ ...mapGetters("document", ["numberOfAcceptedAnnotationsInLabel"]),
88
+ singleAnnotation() {
89
+ if (this.label.annotations.length > 0) {
90
+ return this.label.annotations[0];
91
+ }
92
+ return null;
93
+ },
94
+ hasAnnotations() {
95
+ return this.label.annotations.length > 0;
96
+ },
97
+ },
98
+ watch: {
99
+ sidebarAnnotationSelected(newSidebarAnnotationSelected) {
100
+ // check if annotation is inside a label group and open it
101
+ if (
102
+ this.enableGroupingFeature &&
103
+ !this.showAnnotationsGroup &&
104
+ newSidebarAnnotationSelected
105
+ ) {
106
+ const annotation = this.label.annotations.find(
107
+ (ann) => ann.id === newSidebarAnnotationSelected.id
108
+ );
109
+ if (annotation) {
110
+ this.showAnnotationsGroup = true;
111
+ this.$store.dispatch("document/setSidebarAnnotationSelected", null);
112
+ // run in next render because we need to open the group first
113
+ this.$nextTick(() => {
114
+ this.$store.dispatch(
115
+ "document/setSidebarAnnotationSelected",
116
+ annotation
117
+ );
118
+ });
119
+ }
120
+ }
121
+ },
122
+ },
123
+ mounted() {
124
+ this.updateValues();
125
+ },
126
+ updated() {
127
+ this.updateValues();
128
+ },
129
+ methods: {
130
+ toggleGroup() {
131
+ this.showAnnotationsGroup = !this.showAnnotationsGroup;
132
+ },
133
+ updateValues() {
134
+ this.isMultipleAnnotations = this.label.annotations.length > 1;
135
+ if (this.isMultipleAnnotations) {
136
+ this.acceptedAnnotationsGroupCounter =
137
+ this.numberOfAcceptedAnnotationsInLabel(this.label);
138
+ }
139
+ },
140
+ },
141
+ };
142
+ </script>
143
+
144
+ <style
145
+ scoped
146
+ lang="scss"
147
+ src="../../assets/scss/document_annotations.scss"
148
+ ></style>