From bfd1a043ea999284be568c10458402840b7e256a Mon Sep 17 00:00:00 2001 From: Brice Schaffner Date: Wed, 22 May 2024 13:28:16 +0200 Subject: [PATCH 1/2] PB-531: Improve report a problem error management for big files The backend only support attachment up 10MB, above it returns a 413 error. Same for kml it only support kml up to 2MB. So do the validation up front and provide a nice message to the user. --- src/api/feedback.api.js | 4 ++++ .../drawing/components/DrawingHeader.vue | 4 ++-- src/modules/i18n/locales/de.json | 5 +++-- src/modules/i18n/locales/en.json | 5 +++-- src/modules/i18n/locales/fr.json | 5 +++-- src/modules/i18n/locales/it.json | 5 +++-- src/modules/i18n/locales/rm.json | 5 +++-- .../settings/ReportProblemButton.vue | 22 +++++++++++++++---- src/utils/__tests__/utils.spec.js | 13 +++++++++++ src/utils/components/FileInput.vue | 7 +++++- src/utils/numberUtils.js | 2 +- src/utils/utils.js | 11 ++++++++++ 12 files changed, 70 insertions(+), 18 deletions(-) diff --git a/src/api/feedback.api.js b/src/api/feedback.api.js index 7052a2b94..2d65b387a 100644 --- a/src/api/feedback.api.js +++ b/src/api/feedback.api.js @@ -5,6 +5,10 @@ import { createShortLink } from '@/api/shortlink.api' import { API_SERVICES_BASE_URL, APP_VERSION } from '@/config' import log from '@/utils/logging' +/** Maximum size allowed by the backend, can be used to do validation up front */ +export const ATTACHMENT_MAX_SIZE = 10 * 1024 * 1024 +export const KML_MAX_SIZE = 2 * 1024 * 1024 + /** * @param {String} subject Mandatory * @param {String} text Mandatory diff --git a/src/modules/drawing/components/DrawingHeader.vue b/src/modules/drawing/components/DrawingHeader.vue index ff48b6e1f..103175dc4 100644 --- a/src/modules/drawing/components/DrawingHeader.vue +++ b/src/modules/drawing/components/DrawingHeader.vue @@ -39,10 +39,10 @@ function onClose() { {{ i18n.t(drawingTitle) }} -
+
-
+
diff --git a/src/modules/i18n/locales/de.json b/src/modules/i18n/locales/de.json index 22cfc6fe8..0d3b683a0 100644 --- a/src/modules/i18n/locales/de.json +++ b/src/modules/i18n/locales/de.json @@ -199,7 +199,7 @@ "feedback_success_message": "Danke für Ihre Nachricht. Diese wurde erfolgreich übermittelt.", "feedback_unsupported_format": "Dieser Dateityp wird leider nicht unterstützt. Bitte verwenden Sie eine .pdf, .zip, .jpg, .jpeg, .kml, .kmz oder .gpx Datei.", "file_is_not_kml": "Dieses File ist keine KML Datei. ", - "file_too_large": "Die Datei ist zu gross.", + "file_too_large": "Die Datei ist zu gross (maximal erlaubte Größe: MAX_FILE_SIZE).", "follow_us": "Folgen Sie uns", "full_screen": "Vollbild", "funksender": "Funksender", @@ -680,5 +680,6 @@ "3d_labels": "Geografische Namen", "3d_vegetation": "Vegetationen", "3d_constructions": "Gebäude und Konstruktionen", - "webmapviewer_live_disclaimer": "Grosse Veränderungen stehen bevor, erfahren Sie mehr" + "webmapviewer_live_disclaimer": "Grosse Veränderungen stehen bevor, erfahren Sie mehr", + "drawing_too_large": "Deine Zeichnung ist zu groß, entferne einige Details." } diff --git a/src/modules/i18n/locales/en.json b/src/modules/i18n/locales/en.json index 5a72d7edf..346e1a8d7 100644 --- a/src/modules/i18n/locales/en.json +++ b/src/modules/i18n/locales/en.json @@ -199,7 +199,7 @@ "feedback_success_message": "Your message was successfully sent. Thank you.", "feedback_unsupported_format": "This file format is not supported. Thanks for using another format for you attachment.", "file_is_not_kml": "The file is not a KML file.", - "file_too_large": "The file is too large.", + "file_too_large": "The file is too large (max size allowed MAX_FILE_SIZE).", "follow_us": "Follow us", "full_screen": "Full screen", "funksender": "Radio transmitter", @@ -680,5 +680,6 @@ "3d_labels": "Geographic names", "3d_vegetation": "Vegetations", "3d_constructions": "Buildings and constructions", - "webmapviewer_live_disclaimer": "Big changes are coming soon, learn more" + "webmapviewer_live_disclaimer": "Big changes are coming soon, learn more", + "drawing_too_large": "Your drawing is too large, remove some features" } diff --git a/src/modules/i18n/locales/fr.json b/src/modules/i18n/locales/fr.json index b849f5f3c..100cc4014 100644 --- a/src/modules/i18n/locales/fr.json +++ b/src/modules/i18n/locales/fr.json @@ -199,7 +199,7 @@ "feedback_success_message": "Votre message a été envoyé avec succès. Merci.", "feedback_unsupported_format": "Le format du fichier n’est pas pris en charge, merci d’utiliser un autre format pour votre pièce jointe.", "file_is_not_kml": "Ce fichier n'est pas un fichier KML.", - "file_too_large": "Ce fichier est trop volumineux", + "file_too_large": "Ce fichier est trop volumineux (taille maximale autorisée : MAX_FILE_SIZE)", "follow_us": "Suivez-nous", "full_screen": "Plein écran", "funksender": "Emetteur radio", @@ -680,5 +680,6 @@ "3d_labels": "Noms géographiques", "3d_vegetation": "Végétations", "3d_constructions": "Bâtiments et constructions", - "webmapviewer_live_disclaimer": "De grands changements sont à venir, en savoir plus" + "webmapviewer_live_disclaimer": "De grands changements sont à venir, en savoir plus", + "drawing_too_large": "Ton dessin est trop grand, enlève quelques éléments" } diff --git a/src/modules/i18n/locales/it.json b/src/modules/i18n/locales/it.json index 1a3fd4ba2..cd8a40d88 100644 --- a/src/modules/i18n/locales/it.json +++ b/src/modules/i18n/locales/it.json @@ -199,7 +199,7 @@ "feedback_success_message": "Il suo messaggio è stato inviato con successo. Grazie.", "feedback_unsupported_format": "Il formato del file selezionato non è supportato dal sistema, per favore utilizzare un altro formato per il vostro allegato.", "file_is_not_kml": "Questo file non è un file KML.", - "file_too_large": "Il file é troppo grande", + "file_too_large": "Il file é troppo grande (dimensione massima consentita: MAX_FILE_SIZE)", "follow_us": "Seguiteci", "full_screen": "Schermo intero", "funksender": "Trasmettitore radio", @@ -680,5 +680,6 @@ "3d_labels": "Nomi geografici", "3d_vegetation": "Vegetazione", "3d_constructions": "Edifici e costruzioni", - "webmapviewer_live_disclaimer": "Grandi cambiamenti in arrivo, per saperne di più" + "webmapviewer_live_disclaimer": "Grandi cambiamenti in arrivo, per saperne di più", + "drawing_too_large": "Il tuo disegno è troppo grande, rimuovi alcune caratteristiche" } diff --git a/src/modules/i18n/locales/rm.json b/src/modules/i18n/locales/rm.json index e49897b6e..f524d0b76 100644 --- a/src/modules/i18n/locales/rm.json +++ b/src/modules/i18n/locales/rm.json @@ -197,7 +197,7 @@ "feedback_success_message": "Rapport trasmess cun success. Grazia.", "feedback_unsupported_format": "Tip da datoteca betg supportà. Duvrai empè da quai pdf, .zip, .jpg, .jpeg, .kml, .kmz u .gpx", "file_is_not_kml": "Questa datoteca nun è ina datoteca KML", - "file_too_large": "Questa datoteca è memia grond", + "file_too_large": "Questa datoteca è memia grond (grondezza maximala lubida: MAX_FILE_SIZE)", "follow_us": "Giais suenter a nus", "full_screen": "Maletg entir", "funksender": "Emettur radiofonic ", @@ -678,5 +678,6 @@ "3d_labels": "Numns geografics", "3d_vegetation": "Vegietaziun", "3d_constructions": "Edifizis ed installaziuns", - "webmapviewer_live_disclaimer": "Grondas midadas vegnan prest, emprender dapli" + "webmapviewer_live_disclaimer": "Grondas midadas vegnan prest, emprender dapli", + "drawing_too_large": "Tes dissegn è memia grond, allontanescha intgins detagls" } diff --git a/src/modules/menu/components/settings/ReportProblemButton.vue b/src/modules/menu/components/settings/ReportProblemButton.vue index 12d0d3ce6..3f444f700 100644 --- a/src/modules/menu/components/settings/ReportProblemButton.vue +++ b/src/modules/menu/components/settings/ReportProblemButton.vue @@ -3,7 +3,7 @@ import { computed, nextTick, ref, watch } from 'vue' import { useI18n } from 'vue-i18n' import { useStore } from 'vuex' -import sendFeedback from '@/api/feedback.api' +import sendFeedback, { ATTACHMENT_MAX_SIZE, KML_MAX_SIZE } from '@/api/feedback.api' import { createShortLink } from '@/api/shortlink.api' import HeaderLink from '@/modules/menu/components/header/HeaderLink.vue' import SendActionButtons from '@/modules/menu/components/settings/common/SendActionButtons.vue' @@ -58,8 +58,15 @@ const showDrawingOverlay = computed(() => store.state.drawing.drawingOverlay.sho const temporaryKml = computed(() => store.state.layers.systemLayers.find((l) => l.id === temporaryKmlId) ) +const isTemporaryKmlValid = computed( + () => (temporaryKml.value?.kmlData?.length ?? 0) <= KML_MAX_SIZE +) const isFormValid = computed( - () => isMessageValid.value && isEmailValid.value && isAttachmentValid.value + () => + isMessageValid.value && + isEmailValid.value && + isAttachmentValid.value && + isTemporaryKmlValid.value ) watch( @@ -198,9 +205,12 @@ function toggleDrawingOverlay() { {{ i18n.t('feedback_drawing') }} +
+ {{ i18n.t('drawing_too_large') }} +
{{ i18n.t('drawing_attached') }}
@@ -232,6 +245,7 @@ function toggleDrawingOverlay() { :placeholder="'feedback_placeholder'" :activate-validation="activateValidation" :disabled="request.pending" + :max-file-size="ATTACHMENT_MAX_SIZE" data-cy="report-problem" @validate="onAttachmentValidate" /> diff --git a/src/utils/__tests__/utils.spec.js b/src/utils/__tests__/utils.spec.js index b134ad884..763e1ce7a 100644 --- a/src/utils/__tests__/utils.spec.js +++ b/src/utils/__tests__/utils.spec.js @@ -5,6 +5,7 @@ import { formatMeters, formatMinutesTime, formatPointCoordinates, + humanFileSize, parseUrlHashQuery, transformUrlMapToEmbed, } from '@/utils/utils' @@ -120,4 +121,16 @@ describe('utils', () => { expect(result.query).to.equal('') }) }) + describe('humanFileSize', () => { + it('format correctly valid size', () => { + expect(humanFileSize(0)).to.be.equal('0 B') + expect(humanFileSize(1)).to.be.equal('1 B') + expect(humanFileSize(800)).to.be.equal('800 B') + expect(humanFileSize(1024)).to.be.equal('1 kB') + expect(humanFileSize(1025)).to.be.equal('1 kB') + expect(humanFileSize(4 * 1024)).to.be.equal('4 kB') + expect(humanFileSize(4 * 1024 * 1024)).to.be.equal('4 MB') + expect(humanFileSize(4.3 * 1024 * 1024)).to.be.equal('4.3 MB') + }) + }) }) diff --git a/src/utils/components/FileInput.vue b/src/utils/components/FileInput.vue index 144dc717e..bac23fcfa 100644 --- a/src/utils/components/FileInput.vue +++ b/src/utils/components/FileInput.vue @@ -19,6 +19,7 @@ import { propsValidator4ValidateFunc, useFieldValidation, } from '@/utils/composables/useFieldValidation' +import { humanFileSize } from '@/utils/utils' // On each component creation set the current component ID and increase the counter const inputFileId = useComponentUniqueId('file-input', components) @@ -193,6 +194,7 @@ const InputLocalFile = ref(null) const filePathInfo = computed(() => value.value ? `${value.value.name}, ${value.value.size / 1000} kb` : '' ) +const maxFileSizeHuman = computed(() => humanFileSize(maxFileSize.value)) // Methods function validateFile() { @@ -265,7 +267,10 @@ function onFileSelected(evt) { data-cy="file-input-invalid-feedback" > {{ - i18n.t(invalidMessage).replace('ALLOWED_FORMATS', acceptedFileTypes.join(', ')) + i18n + .t(invalidMessage) + .replace('ALLOWED_FORMATS', acceptedFileTypes.join(', ')) + .replace('MAX_FILE_SIZE', maxFileSizeHuman) }}
diff --git a/src/utils/numberUtils.js b/src/utils/numberUtils.js index ea4c3fe5c..e5b5ea706 100644 --- a/src/utils/numberUtils.js +++ b/src/utils/numberUtils.js @@ -86,7 +86,7 @@ export function format(value, decimal = 2) { } /** - * Returns a string representing a number with the right thousand seaprator + * Returns a string representing a number with the right thousand separator * * @param {Number} num A number * @param {String} separator The thousand separator, default to "'" diff --git a/src/utils/utils.js b/src/utils/utils.js index 449ea62ed..9753ce326 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -240,3 +240,14 @@ export function isValidEmail(email) { /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ return EMAIL_REGEX.test(email) } + +/** + * Human readable size + * + * @param {Number} size Size in bytes + * @returns {String} Human readable size + */ +export function humanFileSize(size) { + let i = size == 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024)) + return +(size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i] +} From a4d11df37fa98bd91dd93d4e5443a04f98302bc7 Mon Sep 17 00:00:00 2001 From: Brice Schaffner Date: Thu, 23 May 2024 09:32:08 +0200 Subject: [PATCH 2/2] PB-531: Use vue-i18n tool for string interpolations --- src/modules/i18n/locales/de.json | 6 +++--- src/modules/i18n/locales/en.json | 4 ++-- src/modules/i18n/locales/fr.json | 4 ++-- src/modules/i18n/locales/it.json | 4 ++-- src/modules/i18n/locales/rm.json | 4 ++-- src/utils/components/FileInput.vue | 8 ++++---- src/utils/utils.js | 4 ++-- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/modules/i18n/locales/de.json b/src/modules/i18n/locales/de.json index 0d3b683a0..db2abe8e4 100644 --- a/src/modules/i18n/locales/de.json +++ b/src/modules/i18n/locales/de.json @@ -199,7 +199,7 @@ "feedback_success_message": "Danke für Ihre Nachricht. Diese wurde erfolgreich übermittelt.", "feedback_unsupported_format": "Dieser Dateityp wird leider nicht unterstützt. Bitte verwenden Sie eine .pdf, .zip, .jpg, .jpeg, .kml, .kmz oder .gpx Datei.", "file_is_not_kml": "Dieses File ist keine KML Datei. ", - "file_too_large": "Die Datei ist zu gross (maximal erlaubte Größe: MAX_FILE_SIZE).", + "file_too_large": "Die Datei ist zu gross (maximal erlaubte Grösse: {maxFileSize}).", "follow_us": "Folgen Sie uns", "full_screen": "Vollbild", "funksender": "Funksender", @@ -674,12 +674,12 @@ "field_required": "Dieses Feld ist erforderlich", "invalid_email": "ungültige E-Mail", "no_email": "Das E-Mail-Feld ist erforderlich", - "file_unsupported_format": "Dieses Dateiformat wird nicht unterstützt. Nur die folgenden Formate sind erlaubt: ALLOWED_FORMATS", + "file_unsupported_format": "Dieses Dateiformat wird nicht unterstützt. Nur die folgenden Formate sind erlaubt: {allowedFormats}", "form_invalid": "Das Formular ist ungültig", "file_imported_success": "Datei erfolgreich importiert", "3d_labels": "Geografische Namen", "3d_vegetation": "Vegetationen", "3d_constructions": "Gebäude und Konstruktionen", "webmapviewer_live_disclaimer": "Grosse Veränderungen stehen bevor, erfahren Sie mehr", - "drawing_too_large": "Deine Zeichnung ist zu groß, entferne einige Details." + "drawing_too_large": "Deine Zeichnung ist zu gross, entferne einige Details." } diff --git a/src/modules/i18n/locales/en.json b/src/modules/i18n/locales/en.json index 346e1a8d7..0ac92b384 100644 --- a/src/modules/i18n/locales/en.json +++ b/src/modules/i18n/locales/en.json @@ -199,7 +199,7 @@ "feedback_success_message": "Your message was successfully sent. Thank you.", "feedback_unsupported_format": "This file format is not supported. Thanks for using another format for you attachment.", "file_is_not_kml": "The file is not a KML file.", - "file_too_large": "The file is too large (max size allowed MAX_FILE_SIZE).", + "file_too_large": "The file is too large (max size allowed {maxFileSize}).", "follow_us": "Follow us", "full_screen": "Full screen", "funksender": "Radio transmitter", @@ -674,7 +674,7 @@ "field_required": "This field is required", "invalid_email": "Invalid email", "no_email": "The e-mail field is required", - "file_unsupported_format": "This file format is not supported. Only the following formats are allowed: ALLOWED_FORMATS", + "file_unsupported_format": "This file format is not supported. Only the following formats are allowed: {allowedFormats}", "form_invalid": "Form is invalid", "file_imported_success": "File successfully imported", "3d_labels": "Geographic names", diff --git a/src/modules/i18n/locales/fr.json b/src/modules/i18n/locales/fr.json index 100cc4014..0b30a095e 100644 --- a/src/modules/i18n/locales/fr.json +++ b/src/modules/i18n/locales/fr.json @@ -199,7 +199,7 @@ "feedback_success_message": "Votre message a été envoyé avec succès. Merci.", "feedback_unsupported_format": "Le format du fichier n’est pas pris en charge, merci d’utiliser un autre format pour votre pièce jointe.", "file_is_not_kml": "Ce fichier n'est pas un fichier KML.", - "file_too_large": "Ce fichier est trop volumineux (taille maximale autorisée : MAX_FILE_SIZE)", + "file_too_large": "Ce fichier est trop volumineux (taille maximale autorisée : {maxFileSize})", "follow_us": "Suivez-nous", "full_screen": "Plein écran", "funksender": "Emetteur radio", @@ -674,7 +674,7 @@ "field_required": "Ce champ est requis", "invalid_email": "e-mail invalide", "no_email": "Le champ email est requis", - "file_unsupported_format": "Ce format de fichier n'est pas pris en charge. Seuls les formats suivants sont autorisés : ALLOWED_FORMATS", + "file_unsupported_format": "Ce format de fichier n'est pas pris en charge. Seuls les formats suivants sont autorisés : {allowedFormats}", "form_invalid": "Le formulaire est invalide", "file_imported_success": "Fichier importé avec succès", "3d_labels": "Noms géographiques", diff --git a/src/modules/i18n/locales/it.json b/src/modules/i18n/locales/it.json index cd8a40d88..4d8dfd5ba 100644 --- a/src/modules/i18n/locales/it.json +++ b/src/modules/i18n/locales/it.json @@ -199,7 +199,7 @@ "feedback_success_message": "Il suo messaggio è stato inviato con successo. Grazie.", "feedback_unsupported_format": "Il formato del file selezionato non è supportato dal sistema, per favore utilizzare un altro formato per il vostro allegato.", "file_is_not_kml": "Questo file non è un file KML.", - "file_too_large": "Il file é troppo grande (dimensione massima consentita: MAX_FILE_SIZE)", + "file_too_large": "Il file é troppo grande (dimensione massima consentita: {maxFileSize})", "follow_us": "Seguiteci", "full_screen": "Schermo intero", "funksender": "Trasmettitore radio", @@ -674,7 +674,7 @@ "field_required": "Questo campo è obbligatorio", "invalid_email": "e-mail non valido", "no_email": "Il campo email è obbligatorio", - "file_unsupported_format": "Questo formato file non è supportato. Sono ammessi solo i seguenti formati: ALLOWED_FORMATS", + "file_unsupported_format": "Questo formato file non è supportato. Sono ammessi solo i seguenti formati: {allowedFormats}", "form_invalid": "Il modulo non è valido", "file_imported_success": "File importato con successo", "3d_labels": "Nomi geografici", diff --git a/src/modules/i18n/locales/rm.json b/src/modules/i18n/locales/rm.json index f524d0b76..277691540 100644 --- a/src/modules/i18n/locales/rm.json +++ b/src/modules/i18n/locales/rm.json @@ -197,7 +197,7 @@ "feedback_success_message": "Rapport trasmess cun success. Grazia.", "feedback_unsupported_format": "Tip da datoteca betg supportà. Duvrai empè da quai pdf, .zip, .jpg, .jpeg, .kml, .kmz u .gpx", "file_is_not_kml": "Questa datoteca nun è ina datoteca KML", - "file_too_large": "Questa datoteca è memia grond (grondezza maximala lubida: MAX_FILE_SIZE)", + "file_too_large": "Questa datoteca è memia grond (grondezza maximala lubida: {maxFileSize})", "follow_us": "Giais suenter a nus", "full_screen": "Maletg entir", "funksender": "Emettur radiofonic ", @@ -672,7 +672,7 @@ "field_required": "Quest champ è necessari", "invalid_email": "ungültige E-Mail", "no_email": "Il champ da posta eletronicala è necessari", - "file_unsupported_format": "Quest format da datoteca na vegn betg sustegnì. Sun ils seguents formats èn permess: ALLOWED_FORMATS", + "file_unsupported_format": "Quest format da datoteca na vegn betg sustegnì. Sun ils seguents formats èn permess: {allowedFormats}", "form_invalid": "Il formular è invalid", "file_imported_success": "Datoteca importada cun success", "3d_labels": "Numns geografics", diff --git a/src/utils/components/FileInput.vue b/src/utils/components/FileInput.vue index bac23fcfa..290d6fe95 100644 --- a/src/utils/components/FileInput.vue +++ b/src/utils/components/FileInput.vue @@ -267,10 +267,10 @@ function onFileSelected(evt) { data-cy="file-input-invalid-feedback" > {{ - i18n - .t(invalidMessage) - .replace('ALLOWED_FORMATS', acceptedFileTypes.join(', ')) - .replace('MAX_FILE_SIZE', maxFileSizeHuman) + i18n.t(invalidMessage, { + maxFileSize: maxFileSizeHuman, + allowedFormats: acceptedFileTypes.join(', '), + }) }}
diff --git a/src/utils/utils.js b/src/utils/utils.js index 9753ce326..aff7bb030 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -248,6 +248,6 @@ export function isValidEmail(email) { * @returns {String} Human readable size */ export function humanFileSize(size) { - let i = size == 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024)) - return +(size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i] + const i = size == 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024)) + return (size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i] }