@konfuzio/document-validation-ui 0.1.13-dev.2 → 0.1.13-dev.4

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@konfuzio/document-validation-ui",
3
- "version": "0.1.13-dev.2",
3
+ "version": "0.1.13-dev.4",
4
4
  "repository": "git://github.com:konfuzio-ai/document-validation-ui.git",
5
5
  "main": "dist/app.js",
6
6
  "scripts": {
package/src/api.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import axios from "axios";
2
2
  import { cacheAdapterEnhancer } from "axios-extensions";
3
3
 
4
- let HTTP, IMG_REQUEST, authToken, appLocale;
4
+ let HTTP, FILE_REQUEST, authToken, appLocale;
5
5
  const DEFAULT_URL = "https://app.konfuzio.com";
6
6
 
7
7
  axios.defaults.xsrfCookieName = "csrftoken";
@@ -11,7 +11,7 @@ HTTP = axios.create({
11
11
  baseURL: process.env.VUE_APP_API_URL || `${DEFAULT_URL}/api/v3/`,
12
12
  });
13
13
 
14
- IMG_REQUEST = axios.create({
14
+ FILE_REQUEST = axios.create({
15
15
  baseURL: process.env.VUE_APP_DOCUMENT_IMAGES_URL || `${DEFAULT_URL}`,
16
16
  responseType: "blob",
17
17
  adapter: cacheAdapterEnhancer(axios.defaults.adapter),
@@ -25,8 +25,8 @@ const setApiUrl = (url) => {
25
25
  HTTP.defaults.baseURL = url;
26
26
  };
27
27
 
28
- const setImageUrl = (url) => {
29
- IMG_REQUEST.defaults.baseURL = url;
28
+ const setFileUrl = (url) => {
29
+ FILE_REQUEST.defaults.baseURL = url;
30
30
  };
31
31
 
32
32
  const setLocale = (locale) => {
@@ -45,22 +45,25 @@ HTTP.interceptors.request.use(getInterceptorConfig, (error) => {
45
45
  return Promise.reject(error);
46
46
  });
47
47
 
48
- IMG_REQUEST.interceptors.request.use(getInterceptorConfig, (error) => {
48
+ FILE_REQUEST.interceptors.request.use(getInterceptorConfig, (error) => {
49
49
  return Promise.reject(error);
50
50
  });
51
51
 
52
- const makeImageRequest = (imageURL) => {
52
+ const makeFileRequest = (fileUrl) => {
53
53
  return new Promise((resolve, reject) => {
54
54
  if (process.env.NODE_ENV === "test") {
55
55
  reject("Running unit tests!");
56
56
  return;
57
57
  }
58
- IMG_REQUEST.get(imageURL)
58
+ FILE_REQUEST.get(fileUrl)
59
59
  .then((response) => {
60
60
  return response.data;
61
61
  })
62
62
  .then((myBlob) => {
63
63
  resolve(myBlob);
64
+ })
65
+ .catch((error) => {
66
+ reject(error);
64
67
  });
65
68
  });
66
69
  };
@@ -68,8 +71,9 @@ const makeImageRequest = (imageURL) => {
68
71
  export default {
69
72
  HTTP,
70
73
  setApiUrl,
71
- setImageUrl,
72
- makeImageRequest,
74
+ setFileUrl,
75
+ makeFileRequest,
73
76
  setAuthToken,
74
77
  setLocale,
78
+ FILE_REQUEST,
75
79
  };
@@ -41,7 +41,7 @@ export default {
41
41
  loadImage() {
42
42
  if (!this.imageUrl) return;
43
43
  return api
44
- .makeImageRequest(this.imageUrl)
44
+ .makeFileRequest(this.imageUrl)
45
45
  .then((myBlob) => {
46
46
  this.$refs.imgTag.src = URL.createObjectURL(myBlob);
47
47
  if (this.height) {
@@ -66,8 +66,11 @@
66
66
  display: flex;
67
67
  flex-direction: row;
68
68
  justify-content: space-between;
69
- border-bottom: 1px solid $low-opacity-white;
70
- padding-bottom: 8px;
69
+
70
+ &:not(.tooltip-in-public-view) {
71
+ border-bottom: 1px solid $low-opacity-white;
72
+ padding-bottom: 8px;
73
+ }
71
74
 
72
75
  .value {
73
76
  color: $green;
@@ -1,6 +1,6 @@
1
1
  @import "./imports.scss";
2
2
 
3
- .document-pages {
3
+ #document-pages {
4
4
  background: $background;
5
5
  min-width: 80px;
6
6
  overflow-y: auto;
@@ -24,6 +24,16 @@
24
24
  display: none;
25
25
  }
26
26
 
27
+ .edit-mode-disabled,
28
+ .zoom-disabled {
29
+ opacity: 30%;
30
+
31
+ &:hover {
32
+ cursor: not-allowed !important;
33
+ background: inherit;
34
+ }
35
+ }
36
+
27
37
  .toolbar-divider {
28
38
  width: 1px;
29
39
  background-color: $grey-detail;
@@ -72,7 +82,6 @@
72
82
  padding: 7px;
73
83
  margin-right: 20px;
74
84
  }
75
-
76
85
  .icon {
77
86
  width: 32px;
78
87
  height: 32px;
@@ -83,13 +92,18 @@
83
92
  background: $low-opacity-white;
84
93
  border-radius: 4px;
85
94
  }
86
- &.zoom-disabled {
87
- opacity: 30%;
95
+ }
96
+ }
88
97
 
89
- &:hover {
90
- cursor: not-allowed !important;
91
- background: inherit;
92
- }
98
+ .download-file {
99
+ color: $toolbar-elements;
100
+
101
+ .is-active {
102
+ background: $low-opacity-white;
103
+ border-radius: 4px;
104
+
105
+ .icon:hover {
106
+ background: none;
93
107
  }
94
108
  }
95
109
  }
@@ -251,6 +251,15 @@
251
251
  .dropdown-trigger {
252
252
  justify-content: space-between;
253
253
  width: 100%;
254
+
255
+ .icon {
256
+ &.download-file {
257
+ svg {
258
+ height: 100% !important;
259
+ width: 100% !important;
260
+ }
261
+ }
262
+ }
254
263
  }
255
264
 
256
265
  .dropdown-menu {
@@ -1,6 +1,6 @@
1
1
  import App from "./App.vue";
2
2
 
3
- describe("<App />", () => {
3
+ describe("App", () => {
4
4
  it("renders the app", () => {
5
5
  cy.mount(App);
6
6
  });
@@ -165,7 +165,7 @@ export default {
165
165
  API.setApiUrl(this.api_url);
166
166
  }
167
167
  if (this.image_url !== "") {
168
- API.setImageUrl(this.image_url);
168
+ API.setFileUrl(this.image_url);
169
169
  }
170
170
 
171
171
  // document and project config
@@ -3,7 +3,6 @@
3
3
  :animated="false"
4
4
  :position="fromTable ? 'is-top' : 'is-bottom'"
5
5
  :class="[!fromTable && 'left-aligned', 'annotation-details']"
6
- :active="!publicView"
7
6
  >
8
7
  <div :class="['label-icon', fromTable && 'is-small']">
9
8
  <div v-if="(created(annotation) || edited(annotation)) && !publicView">
@@ -68,7 +67,10 @@
68
67
  <div v-if="description" class="label-description">
69
68
  <span>{{ description }}</span>
70
69
  </div>
71
- <div v-if="confidence(annotation)" class="confidence">
70
+ <div
71
+ v-if="confidence(annotation)"
72
+ :class="['confidence', publicView && 'tooltip-in-public-view']"
73
+ >
72
74
  <span>{{ $t("confidence") }}</span
73
75
  ><span
74
76
  :class="[
@@ -82,7 +84,7 @@
82
84
  >{{ Math.floor(confidence(annotation) * 100) / 100 }}</span
83
85
  >
84
86
  </div>
85
- <div class="revision">
87
+ <div v-if="!publicView" class="revision">
86
88
  <div class="detail-icons">
87
89
  <div v-if="created(annotation) || edited(annotation)">
88
90
  <div
@@ -459,7 +459,7 @@ export default {
459
459
  return;
460
460
  }
461
461
  api
462
- .makeImageRequest(
462
+ .makeFileRequest(
463
463
  `${this.page.image_url}?${this.selectedDocument.downloaded_at}`
464
464
  )
465
465
  .then((myBlob) => {
@@ -26,6 +26,26 @@
26
26
  v-if="!editMode && !publicView && !isDocumentReviewed"
27
27
  class="toolbar-divider"
28
28
  />
29
+
30
+ <div v-if="!publicView" class="download-file icons">
31
+ <b-dropdown aria-role="list" position="is-top-right">
32
+ <template #trigger>
33
+ <b-icon icon="download" size="small" class="download-file" />
34
+ </template>
35
+
36
+ <b-dropdown-item aria-role="listitem" @click="handleDownloadFile()">{{
37
+ $t("original_file")
38
+ }}</b-dropdown-item>
39
+ <b-dropdown-item
40
+ aria-role="listitem"
41
+ @click="handleDownloadFile('ocr')"
42
+ >{{ $t("pdf_file") }}</b-dropdown-item
43
+ >
44
+ </b-dropdown>
45
+ </div>
46
+
47
+ <div v-if="!publicView" class="toolbar-divider" />
48
+
29
49
  <div class="icons icons-right">
30
50
  <div
31
51
  :class="[
@@ -63,6 +83,7 @@ import FitZoomIcon from "../../assets/images/FitZoomIcon";
63
83
  import PlusIcon from "../../assets/images/PlusIcon";
64
84
  import MinusIcon from "../../assets/images/MinusIcon";
65
85
  import EditDocIcon from "../../assets/images/EditDocIcon";
86
+ import api from "../../api";
66
87
 
67
88
  export default {
68
89
  name: "DocumentToolbar",
@@ -158,6 +179,41 @@ export default {
158
179
  this.$store.dispatch("display/updateScale", { scale });
159
180
  });
160
181
  },
182
+ handleDownloadFile(fileType) {
183
+ let fileUrl;
184
+ // get the file name without the extension
185
+ let fileName = this.getFileName(this.selectedDocument.data_file_name);
186
+
187
+ if (fileType === "ocr") {
188
+ fileUrl = this.selectedDocument.file_url;
189
+ fileName = `${fileName}_${fileType}`;
190
+ } else {
191
+ fileUrl = `/doc/show-original/${this.selectedDocument.id}/`;
192
+ }
193
+
194
+ // Automatically download original or ocr files
195
+ return api
196
+ .makeFileRequest(fileUrl)
197
+ .then((myBlob) => {
198
+ const url = URL.createObjectURL(myBlob);
199
+ const link = document.createElement("a");
200
+ link.href = url;
201
+ link.setAttribute("download", fileName);
202
+ link.click();
203
+ URL.revokeObjectURL(link.href);
204
+ })
205
+ .catch((error) => {
206
+ this.$store.dispatch("document/createErrorMessage", {
207
+ error,
208
+ serverErrorMessage: this.$t("server_error"),
209
+ defaultErrorMessage: this.$t("error_downloading_file"),
210
+ });
211
+ console.log(error);
212
+ });
213
+ },
214
+ getFileName(fileName) {
215
+ return fileName.split(".").slice(0, -1).join(".");
216
+ },
161
217
  cancelAnnotationEditMode() {
162
218
  this.$store.dispatch("document/resetEditAnnotation");
163
219
  this.$store.dispatch("selection/disableSelection");
@@ -0,0 +1,64 @@
1
+ import DocumentThumbnails from "./DocumentThumbnails.vue";
2
+
3
+ describe("Document Thumbnails", () => {
4
+ beforeEach(() => {
5
+ cy.fetchDocument();
6
+ });
7
+
8
+ it("shows thumbnails for all document pages", () => {
9
+ cy.mount(DocumentThumbnails);
10
+ cy.get("#document-pages")
11
+ .find(".document-thumbnail")
12
+ .then((elements) => {
13
+ cy.storeState("document", "selectedDocument")
14
+ .its("pages")
15
+ .its("length")
16
+ .should("equal", elements.length);
17
+ });
18
+ });
19
+
20
+ it("loads thumbnail pictures that are shown on screen", () => {
21
+ cy.intercept("GET", "**/page/show-thumbnail/**").as("getThumbnail");
22
+ cy.mount(DocumentThumbnails);
23
+
24
+ cy.get("#document-pages")
25
+ .find(".document-thumbnail")
26
+ .then((elements) => {
27
+ cy.get("@getThumbnail.all")
28
+ .its("length")
29
+ .should("equal", elements.length);
30
+ });
31
+ });
32
+
33
+ it("displays page number correctly", () => {
34
+ cy.mount(DocumentThumbnails);
35
+ cy.get("#document-pages")
36
+ .find(".document-thumbnail")
37
+ .each(($row, index) => {
38
+ cy.wrap($row)
39
+ .find(".number-thumbnail")
40
+ .contains(index + 1);
41
+ cy.wait(1000);
42
+ });
43
+ });
44
+
45
+ it("navigates to every document thumbnail", () => {
46
+ cy.mount(DocumentThumbnails);
47
+ cy.get("#document-pages")
48
+ .find(".document-thumbnail")
49
+ .each(($row, index) => {
50
+ cy.wrap($row).click();
51
+ cy.storeState("display", "currentPage").should("equal", index + 1);
52
+ cy.wait(1000);
53
+ });
54
+ });
55
+
56
+ it("show loading when a document is not set", () => {
57
+ cy.mount(DocumentThumbnails);
58
+ cy.dispatchAction("document", "setSelectedDocument", null);
59
+ cy.get("#document-pages")
60
+ .find(".document-thumbnail-loading")
61
+ .its("length")
62
+ .should("equal", 1);
63
+ });
64
+ });
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div ref="documentThumbnails" class="document-pages">
2
+ <div id="document-pages" ref="documentThumbnails">
3
3
  <div v-if="selectedDocument">
4
4
  <div
5
5
  v-for="page in selectedDocument.pages"
@@ -32,7 +32,7 @@
32
32
  </div>
33
33
  </div>
34
34
  </div>
35
- <div v-else>
35
+ <div v-else class="document-thumbnail-loading">
36
36
  <div class="document-thumbnail">
37
37
  <div class="image-section">
38
38
  <div class="image-container">
package/src/icons.js CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  faRepeat,
18
18
  faArrowLeft,
19
19
  faArrowRight,
20
+ faDownload,
20
21
  } from "@fortawesome/free-solid-svg-icons";
21
22
  import { FontAwesomeIcon as Icons } from "@fortawesome/vue-fontawesome";
22
23
 
@@ -37,7 +38,8 @@ library.add(
37
38
  faScissors,
38
39
  faRepeat,
39
40
  faArrowLeft,
40
- faArrowRight
41
+ faArrowRight,
42
+ faDownload
41
43
  );
42
44
 
43
45
  export default Icons;
@@ -132,5 +132,8 @@
132
132
  "delete_label": "Label löschen",
133
133
  "table": "Tabelle",
134
134
  "prepare_document": "Dokument vorbereiten",
135
- "delete_table": "Tabelle löschen"
135
+ "delete_table": "Tabelle löschen",
136
+ "error_downloading_file": "Die Datei konnte nicht heruntergeladen werden.",
137
+ "original_file": "Originaldatei",
138
+ "pdf_file": "PDF Datei"
136
139
  }
@@ -132,5 +132,8 @@
132
132
  "edit_label": "Edit label",
133
133
  "delete_label": "Delete label",
134
134
  "table": "Table",
135
- "delete_table": "Delete table"
135
+ "delete_table": "Delete table",
136
+ "error_downloading_file": "The file could not be downloaded.",
137
+ "original_file": "Original file",
138
+ "pdf_file": "PDF file"
136
139
  }
@@ -132,5 +132,8 @@
132
132
  "new_multi_ann_description": "Seleccione un modelo de datos de los existentes, luego deseleccione las etiquetas que no existen en este documento.",
133
133
  "new_multi_ann_title": "Crear múltiples anotaciones",
134
134
  "prepare_document": "Prepare el documento",
135
- "delete_table": "Eliminar tabla"
135
+ "delete_table": "Eliminar tabla",
136
+ "error_downloading_file": "El documento no pudo ser descargado.",
137
+ "original_file": "Documento original",
138
+ "pdf_file": "Documento PDF"
136
139
  }
@@ -17,7 +17,7 @@ const state = {
17
17
  annotationSets: null,
18
18
  annotations: null,
19
19
  labels: [],
20
- documentId: null,
20
+ documentId: process.env.VUE_APP_DOCUMENT_ID,
21
21
  sidebarAnnotationSelected: null,
22
22
  documentAnnotationSelected: null,
23
23
  selectedDocument: null,
@@ -1256,7 +1256,9 @@ const mutations = {
1256
1256
  state.selectedDocument = document;
1257
1257
 
1258
1258
  // this is to handle cache when a document is edited or changed
1259
- state.selectedDocument.downloaded_at = Date.now();
1259
+ if (state.selectedDocument) {
1260
+ state.selectedDocument.downloaded_at = Date.now();
1261
+ }
1260
1262
  },
1261
1263
  SET_RECALCULATING_ANNOTATIONS: (state, recalculatingAnnotations) => {
1262
1264
  state.recalculatingAnnotations = recalculatingAnnotations;
@@ -2,7 +2,7 @@ import myImports from "../api";
2
2
  const HTTP = myImports.HTTP;
3
3
 
4
4
  const state = {
5
- projectId: null,
5
+ projectId: process.env.VUE_APP_PROJECT_ID,
6
6
  currentUser: null,
7
7
  documentsListPath: null,
8
8
  documentsInProject: null,