@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,103 @@
1
+ <template>
2
+ <b-dropdown class="dataset-status-chooser">
3
+ <template #trigger>
4
+ <div class="dataset-status-drop-down">
5
+ <div class="icon">
6
+ <StatusIcon />
7
+ </div>
8
+ <div class="dataset-status-info">
9
+ <p class="dataset-status-title">
10
+ {{ $t("status") }}
11
+ </p>
12
+ <div class="dataset-status-name">
13
+ {{ datasetStatus === 0 ? $t("set_status") : handleStatus() }}
14
+ </div>
15
+ </div>
16
+ <div class="caret">
17
+ <b-icon icon="angle-down" size="is-small" class="caret" />
18
+ </div>
19
+ </div>
20
+ </template>
21
+
22
+ <p class="dropdown-menu-title">
23
+ {{ $t("status") }}
24
+ </p>
25
+
26
+ <b-dropdown-item
27
+ v-for="(status, index) in statusList"
28
+ :key="index"
29
+ class="dropdown-item"
30
+ aria-role="listitem"
31
+ :disabled="disable(status)"
32
+ @click="handleChangeStatus(index)"
33
+ >
34
+ {{ status }}
35
+ </b-dropdown-item>
36
+ </b-dropdown>
37
+ </template>
38
+
39
+ <script>
40
+ import StatusIcon from "../../assets/images/StatusImg";
41
+
42
+ export default {
43
+ name: "DatasetStatus",
44
+ components: {
45
+ StatusIcon,
46
+ },
47
+ props: {
48
+ datasetStatus: {
49
+ type: Number,
50
+ },
51
+ },
52
+ data() {
53
+ return {
54
+ statusList: [
55
+ this.$t("preparation"),
56
+ this.$t("training"),
57
+ this.$t("test"),
58
+ this.$t("excluded"),
59
+ ],
60
+ currentStatus: null,
61
+ };
62
+ },
63
+ methods: {
64
+ handleChangeStatus(index) {
65
+ const updatedDatasetStatus = {
66
+ dataset_status: index + 1,
67
+ };
68
+
69
+ this.$store
70
+ .dispatch("document/updateDocument", updatedDatasetStatus)
71
+ .then((response) => {
72
+ if (response) {
73
+ this.currentStatus = this.statusList[index];
74
+ }
75
+ })
76
+ .catch((error) => {
77
+ this.$store.dispatch("document/createErrorMessage", {
78
+ error,
79
+ serverErrorMessage: this.$t("server_error"),
80
+ defaultErrorMessage: this.$t("edit_error"),
81
+ });
82
+ });
83
+ },
84
+ handleStatus() {
85
+ if (!this.currentStatus) {
86
+ return this.statusList[this.datasetStatus - 1];
87
+ }
88
+ return this.currentStatus;
89
+ },
90
+ disable(status) {
91
+ if (this.datasetStatus === this.statusList.indexOf(status) + 1) {
92
+ return true;
93
+ }
94
+ },
95
+ },
96
+ };
97
+ </script>
98
+
99
+ <style
100
+ scoped
101
+ lang="scss"
102
+ src="../../assets/scss/document_dataset_status.scss"
103
+ ></style>
@@ -0,0 +1,202 @@
1
+ <template>
2
+ <div class="handover-container">
3
+ <b-button
4
+ :label="$t('handover')"
5
+ type="is-primary"
6
+ aria-controls="handover"
7
+ class="handover-btn"
8
+ @click="open = true"
9
+ />
10
+
11
+ <div class="handover-collapse">
12
+ <b-collapse
13
+ :open="open"
14
+ aria-id="handover"
15
+ class="is-bottom"
16
+ >
17
+ <div class="notification">
18
+ <div class="content">
19
+ <div class="header container">
20
+ <p class="title">
21
+ {{ $t("handover_document") }}
22
+ </p>
23
+ <div
24
+ type="button"
25
+ class="close-icon-container modal-btn"
26
+ @click="open = false"
27
+ >
28
+ <b-icon
29
+ icon="xmark"
30
+ class="close-btn"
31
+ size="is-small"
32
+ />
33
+ </div>
34
+ </div>
35
+ <div class="input-container">
36
+ <section
37
+ :class="['input-section container', invalidEmail && 'invalid']"
38
+ >
39
+ <b-field type="input">
40
+ <b-input
41
+ id="input"
42
+ v-model="selected"
43
+ :placeholder="$t('type_email')"
44
+ autocomplete="off"
45
+ @input="invalidEmail = false"
46
+ />
47
+ </b-field>
48
+ <b-loading
49
+ v-if="isLoading"
50
+ v-model="isLoading"
51
+ :is-full-page="isFullPage"
52
+ >
53
+ <b-icon
54
+ icon="spinner"
55
+ class="fa-spin loading-icon-size spinner"
56
+ />
57
+ </b-loading>
58
+ <b-button
59
+ v-else
60
+ :label="$t('handover')"
61
+ class="handover-btn-menu"
62
+ @click.prevent="handleHandover"
63
+ />
64
+ </section>
65
+ <div
66
+ v-if="invalidEmail"
67
+ class="invalid-email-msg"
68
+ >
69
+ <p>{{ $t("invalid_email") }}</p>
70
+ </div>
71
+ </div>
72
+ <div class="members-section">
73
+ <div class="members-title">
74
+ <h3 class="title">
75
+ {{ $t("members") }}
76
+ </h3>
77
+ </div>
78
+ <div :class="['member-list', scroll && 'scroll']">
79
+ <div
80
+ v-for="member in members"
81
+ :key="member.id"
82
+ >
83
+ <div class="member container">
84
+ <p class="email">
85
+ {{ member.email }}
86
+ </p>
87
+ <b-button
88
+ :label="$t('select')"
89
+ class="select-btn"
90
+ @click="handleSelect(member.id, member.email)"
91
+ />
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </div>
98
+ </b-collapse>
99
+ </div>
100
+ </div>
101
+ </template>
102
+
103
+ <script>
104
+ export default {
105
+ name: "DocumentHandover",
106
+ data() {
107
+ return {
108
+ isComponentModalActive: false,
109
+ open: false,
110
+ // TODO: bring list of members from the backend when endpoint is ready
111
+ members: [
112
+ { id: 1, email: "ch@konfuzio.com" },
113
+ { id: 2, email: "fz@helm-nagel.com" },
114
+ { id: 3, email: "ana@helm-nagel.com" },
115
+ { id: 4, email: "ch@konfuzio.com" },
116
+ { id: 5, email: "fz@helm-nagel.com" },
117
+ { id: 6, email: "ana@helm-nagel.com" }
118
+ ],
119
+ isFullPage: false,
120
+ isLoading: false,
121
+ selected: null,
122
+ scroll: false,
123
+ invalidEmail: false,
124
+ regex:
125
+ /^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i
126
+ };
127
+ },
128
+ computed: {
129
+ // TODO: map members from store
130
+ },
131
+
132
+ updated() {
133
+ if (this.members.length > 3) {
134
+ this.scroll = true;
135
+ }
136
+ },
137
+ methods: {
138
+ emailValidation(email) {
139
+ return email.toLowerCase().match(this.regex);
140
+ },
141
+ isMember() {
142
+ const found = this.members.find(member => member.email === this.selected);
143
+
144
+ if (found) {
145
+ return true;
146
+ }
147
+
148
+ return false;
149
+ },
150
+ handleSelect(id, email) {
151
+ const inputField = document.getElementById("input");
152
+
153
+ if (id) {
154
+ inputField.value = email;
155
+ this.selected = email;
156
+ }
157
+ },
158
+ handleHandover() {
159
+ if (!this.selected) {
160
+ return;
161
+ }
162
+
163
+ if (!this.emailValidation(this.selected)) {
164
+ this.invalidEmail = true;
165
+ return;
166
+ }
167
+
168
+ this.invalidEmail = false;
169
+ this.isLoading = true;
170
+
171
+ // TODO: dispatch to store to set the new owner of the doc
172
+ let assignee;
173
+
174
+ // Check if the user is already a member of the project or new member
175
+ if (this.isMember()) {
176
+ assignee = { assignee: `${this.selected}` };
177
+ } else {
178
+ assignee = { email: `${this.selected}`, role: "annotator" };
179
+ }
180
+
181
+ setTimeout(() => {
182
+ this.isLoading = false;
183
+ // this.snackbar();
184
+ this.selected = null;
185
+ }, 2000);
186
+ },
187
+ snackbar() {
188
+ this.$buefy.snackbar.open({
189
+ message: `
190
+ ${this.$t("document_successfully_handed")} ${this.selected}`,
191
+ actionText: null
192
+ });
193
+ }
194
+ }
195
+ };
196
+ </script>
197
+
198
+ <style
199
+ scoped
200
+ lang="scss"
201
+ src="../../assets/scss/document_handover.scss"
202
+ ></style>
@@ -0,0 +1,224 @@
1
+ <template>
2
+ <div class="document-name-component">
3
+ <div class="document-icon">
4
+ <ServerImage
5
+ :height="'22px'"
6
+ :image-url="`${selectedDocument.thumbnail_url}?${selectedDocument.downloaded_at}`"
7
+ >
8
+ <b-skeleton width="15px" height="22px" :rounded="false" />
9
+ </ServerImage>
10
+ </div>
11
+ <span class="file-name-section">
12
+ <span
13
+ :class="['document-name', isEditable ? 'is-editable' : '']"
14
+ :contenteditable="isEditable"
15
+ @input="handleInput"
16
+ @paste="handlePaste"
17
+ @keydown.enter="handleSave"
18
+ @blur="handleSave"
19
+ >{{ textContent }}</span
20
+ >
21
+ </span>
22
+ <div
23
+ v-if="
24
+ !publicView && showEditBtn && !editMode && !recalculatingAnnotations
25
+ "
26
+ class="edit-btn btn"
27
+ @click="handleEdit"
28
+ >
29
+ {{ $t("edit") }}
30
+ </div>
31
+ <div
32
+ v-if="showSaveBtn && !editMode"
33
+ class="save-btn btn"
34
+ @click="handleSave"
35
+ >
36
+ {{ $t("save") }}
37
+ </div>
38
+ <div v-if="saving" class="message-container">
39
+ <span class="loading-container">
40
+ <b-notification :closable="false" class="loading-background">
41
+ <b-loading v-model="saving" :is-full-page="isFullPage">
42
+ <b-icon icon="spinner" class="fa-spin loading-icon-size spinner" />
43
+ </b-loading>
44
+ </b-notification>
45
+ </span>
46
+ <span>{{ $t("autosaving") }}</span>
47
+ </div>
48
+ <div v-if="changed" class="message-container">
49
+ <span v-if="saved" class="cloud-icon"><FileNameSaved /></span>
50
+ <span v-else class="cloud-icon"><FileNameNotSaved /></span>
51
+ <span>{{ saved ? $t("saved") : $t("not_saved") }}</span>
52
+ </div>
53
+ </div>
54
+ </template>
55
+
56
+ <script>
57
+ import ServerImage from "../../assets/images/ServerImage";
58
+ import FileNameSaved from "../../assets/images/FileNameSavedImage";
59
+ import FileNameNotSaved from "../../assets/images/FileNameNotSavedImage";
60
+ import { mapState } from "vuex";
61
+
62
+ export default {
63
+ name: "DocumentName",
64
+ components: {
65
+ ServerImage,
66
+ FileNameSaved,
67
+ FileNameNotSaved,
68
+ },
69
+ props: {
70
+ dataFileName: {
71
+ type: String,
72
+ },
73
+ },
74
+ data() {
75
+ return {
76
+ isEditable: false,
77
+ showEditBtn: true,
78
+ showSaveBtn: false,
79
+ fileExtension: null,
80
+ fileName: null,
81
+ oldFileName: null,
82
+ saving: false,
83
+ isFullPage: false,
84
+ changed: false,
85
+ saved: false,
86
+ };
87
+ },
88
+ computed: {
89
+ ...mapState("document", [
90
+ "selectedDocument",
91
+ "publicView",
92
+ "recalculatingAnnotations",
93
+ ]),
94
+ ...mapState("display", ["optimalResolution"]),
95
+ ...mapState("edit", ["editMode"]),
96
+ textContent() {
97
+ if (this.isEditable) {
98
+ return this.oldFileName;
99
+ } else if (this.fileName) {
100
+ return this.shortFilenameIfNeeded(
101
+ `${this.fileName}.${this.fileExtension}`
102
+ );
103
+ } else {
104
+ return this.shortFilenameIfNeeded(this.dataFileName);
105
+ }
106
+ },
107
+ },
108
+ updated() {
109
+ const contentEditable = document.querySelector(".document-name");
110
+ if (this.isEditable && contentEditable) {
111
+ contentEditable.focus();
112
+ this.handleCaretPlacing(contentEditable);
113
+ }
114
+
115
+ if (this.changed) {
116
+ const that = this;
117
+ setTimeout(function () {
118
+ that.saved = false;
119
+ that.changed = false;
120
+ that.showEditBtn = true;
121
+ this.showSaveBtn = false;
122
+ }, 4000);
123
+ }
124
+ },
125
+ methods: {
126
+ shortFilenameIfNeeded(filename) {
127
+ if (filename && (filename.length >= 70 || !this.optimalResolution)) {
128
+ return (
129
+ filename.substr(0, 20) +
130
+ "..." +
131
+ filename.substr(filename.length - 10, filename.length)
132
+ );
133
+ }
134
+ return filename;
135
+ },
136
+ handleFileName() {
137
+ // Save the file name and the extension in different variables
138
+ this.fileName = this.dataFileName.split(".").slice(0, -1).join(".");
139
+
140
+ this.fileExtension = this.dataFileName.split(".").at(-1);
141
+ },
142
+ handleEdit() {
143
+ // Split file name and extension to only edit name
144
+ this.handleFileName();
145
+
146
+ this.isEditable = true;
147
+ this.showEditBtn = false;
148
+ this.showSaveBtn = true;
149
+ this.oldFileName = this.fileName;
150
+ },
151
+ handleCaretPlacing(contentEditable) {
152
+ let range, selection;
153
+ if (document.createRange) {
154
+ range = document.createRange(); //Create a range (a range is a like the selection but invisible)
155
+ range.selectNodeContents(contentEditable); //Select the entire contents of the element with the range
156
+ range.collapse(false); //collapse the range to the end point. false means collapse to end rather than the start
157
+ selection = window.getSelection(); //get the selection object (allows you to change selection)
158
+ selection.removeAllRanges(); //remove any selections already made
159
+ selection.addRange(range); //make the range you have just created the visible selection
160
+ } else if (document.selection) {
161
+ range = document.body.createTextRange(); //Create a range (a range is a like the selection but invisible)
162
+ range.moveToElementText(contentEditable); //Select the entire contents of the element with the range
163
+ range.collapse(false); //collapse the range to the end point. false means collapse to end rather than the start
164
+ range.select(); //Select the range (make it the visible selection
165
+ }
166
+ },
167
+ handlePaste(event) {
168
+ // TODO: modify to only paste plain text
169
+ event.preventDefault();
170
+ },
171
+ handleInput(event) {
172
+ const input = event.target.textContent;
173
+
174
+ // Set the new file name to the new input value
175
+ if (input === "") {
176
+ // If the user deletes the name:
177
+ this.fileName = "untitled";
178
+ } else {
179
+ this.fileName = input.trim();
180
+ }
181
+ },
182
+ handleSave(event) {
183
+ this.isEditable = false;
184
+
185
+ event.target.textContent = this.fileName;
186
+
187
+ const updatedFileName = {
188
+ data_file_name: `${this.fileName}.${this.fileExtension}`,
189
+ };
190
+
191
+ this.showSaveBtn = false;
192
+ this.saving = true;
193
+
194
+ this.$store
195
+ .dispatch("document/updateDocument", updatedFileName)
196
+ .then((response) => {
197
+ // Check if the response is successful or not
198
+ if (response) {
199
+ // if successful, set the old name to be the new name
200
+ this.changed = true;
201
+ this.oldFileName = this.fileName;
202
+ this.saved = true;
203
+ }
204
+ })
205
+ .catch((error) => {
206
+ this.changed = true;
207
+ event.target.textContent = this.oldFileName;
208
+ this.fileName = this.oldFileName;
209
+ })
210
+ .finally(() => {
211
+ this.saving = false;
212
+ });
213
+
214
+ // Remove focus from contenteditable
215
+ const contentNotEditable = document.querySelector(".document-name");
216
+ if (contentNotEditable) {
217
+ contentNotEditable.blur();
218
+ }
219
+ },
220
+ },
221
+ };
222
+ </script>
223
+
224
+ <style scoped lang="scss" src="../../assets/scss/document_name.scss"></style>
@@ -0,0 +1,144 @@
1
+ <template>
2
+ <div ref="documentTopBar" class="document-top-bar-component">
3
+ <div
4
+ v-if="selectedDocument && selectedDocument.pages.length > 0 && !loading"
5
+ class="document-top-bar"
6
+ >
7
+ <div class="left-bar-components">
8
+ <DocumentCategory
9
+ v-if="categories && !editMode && !recalculatingAnnotations"
10
+ />
11
+ <DocumentDatasetStatus
12
+ v-if="showDatasetDropdown && !editMode && !recalculatingAnnotations"
13
+ :dataset-status="selectedDocument.dataset_status"
14
+ />
15
+ </div>
16
+
17
+ <DocumentName :data-file-name="selectedDocument.data_file_name" />
18
+
19
+ <div class="right-bar-components">
20
+ <div
21
+ v-if="!editMode && (!publicView || !selectedDocument.is_reviewed)"
22
+ class="keyboard-actions-info"
23
+ >
24
+ <KeyboardActionsDescription />
25
+ </div>
26
+
27
+ <div
28
+ v-if="!editMode && (publicView || selectedDocument.is_reviewed)"
29
+ class="read-only-info"
30
+ >
31
+ <b-tooltip
32
+ type="is-dark"
33
+ :animated="false"
34
+ position="is-bottom"
35
+ class="right-aligned width-184"
36
+ >
37
+ <span v-if="publicView && !selectedDocument.is_reviewed">
38
+ {{ $t("lite_mode") }}
39
+ </span>
40
+ <span v-else class="doc-reviewed">
41
+ {{ $t("reviewed_mode") }}
42
+ </span>
43
+ <b-icon
44
+ :class="[
45
+ 'info-icon is-small',
46
+ selectedDocument.is_reviewed && 'info-reviewed',
47
+ ]"
48
+ icon="circle-info"
49
+ />
50
+ <template #content>
51
+ <div
52
+ v-if="!selectedDocument.is_reviewed"
53
+ class="read-only-details"
54
+ >
55
+ {{ $t("limited_functionalities") }}
56
+ </div>
57
+ <div v-else class="read-only-details">
58
+ {{ $t("document_reviewed") }}
59
+ </div>
60
+ </template>
61
+ </b-tooltip>
62
+ </div>
63
+
64
+ <div class="top-bar-buttons">
65
+ <DocumentTopBarButtons />
66
+ </div>
67
+
68
+ <div v-if="showHandoverButton && !editMode" class="handover">
69
+ <DocumentHandover />
70
+ </div>
71
+ </div>
72
+ </div>
73
+ <div v-else class="loading-top-bar">
74
+ <b-skeleton position="is-centered" width="25%" height="60%" />
75
+ </div>
76
+ </div>
77
+ </template>
78
+
79
+ <script>
80
+ import { mapState } from "vuex";
81
+ import DocumentDatasetStatus from "./DocumentDatasetStatus";
82
+ import DocumentCategory from "../../components/DocumentCategory";
83
+ import DocumentName from "./DocumentName";
84
+ import DocumentHandover from "./DocumentHandover";
85
+ import DocumentTopBarButtons from "./DocumentTopBarButtons";
86
+ import KeyboardActionsDescription from "./KeyboardActionsDescription";
87
+
88
+ /**
89
+ * This component has different functionalities
90
+ * based on Dashboard View or Edit Mode
91
+ */
92
+
93
+ export default {
94
+ name: "DocumentTopBar",
95
+ components: {
96
+ DocumentCategory,
97
+ DocumentDatasetStatus,
98
+ DocumentName,
99
+ DocumentHandover,
100
+ DocumentTopBarButtons,
101
+ KeyboardActionsDescription,
102
+ },
103
+ data() {
104
+ return {
105
+ categoryError: false,
106
+ showDatasetDropdown: false,
107
+ showHandoverButton: false,
108
+ };
109
+ },
110
+ computed: {
111
+ ...mapState("document", [
112
+ "selectedDocument",
113
+ "publicView",
114
+ "loading",
115
+ "recalculatingAnnotations",
116
+ ]),
117
+ ...mapState("category", ["categories"]),
118
+ ...mapState("edit", ["editMode"]),
119
+ },
120
+ created() {
121
+ window.addEventListener("resize", this.handleResize);
122
+ },
123
+ destroyed() {
124
+ window.removeEventListener("resize", this.handleResize);
125
+ },
126
+ mounted() {
127
+ if (this.$refs.documentTopBar) {
128
+ this.setComponentWidth(this.$refs.documentTopBar.offsetWidth);
129
+ }
130
+ },
131
+ methods: {
132
+ setComponentWidth(width) {
133
+ // set width for the error messages to match the width
134
+ // and avoid going over the Dashboard
135
+ this.$store.dispatch("document/setErrorMessageWidth", width);
136
+ },
137
+ handleResize() {
138
+ this.setComponentWidth(this.$refs.documentTopBar.offsetWidth);
139
+ },
140
+ },
141
+ };
142
+ </script>
143
+
144
+ <style scoped lang="scss" src="../../assets/scss/document_top_bar.scss"></style>