+
+
+
+
{{ t('workouts.GPX_FILE') }}:
+
+ - {{ t('workouts.MAX_SIZE') }}: {{ fileSizeLimit }}
+
+
+
+
+
{{ t('workouts.ZIP_ARCHIVE') }}:
+
+ - {{ t('workouts.NO_FOLDER') }}
+ -
+ {{ t('workouts.MAX_FILES') }}: {{ gpx_limit_import }}
+
+ - {{ t('workouts.MAX_SIZE') }}: {{ zipSizeLimit }}
+
+
+
@@ -206,11 +219,13 @@
import ErrorMessage from '@/components/Common/ErrorMessage.vue'
import Loader from '@/components/Common/Loader.vue'
import { ROOT_STORE, WORKOUTS_STORE } from '@/store/constants'
+ import { IAppConfig } from '@/types/application'
import { ISport } from '@/types/sports'
import { IAuthUserProfile } from '@/types/user'
import { IWorkout, IWorkoutForm } from '@/types/workouts'
import { useStore } from '@/use/useStore'
import { formatWorkoutDate, getDateWithTZ } from '@/utils/dates'
+ import { getReadableFileSize } from '@/utils/files'
import { translateSports } from '@/utils/sports'
export default defineComponent({
@@ -257,6 +272,16 @@
const translatedSports: ComputedRef = computed(() =>
translateSports(props.sports, t)
)
+ const appConfig: ComputedRef = computed(
+ () => store.getters[ROOT_STORE.GETTERS.APP_CONFIG]
+ )
+ const fileSizeLimit = appConfig.value.max_single_file_size
+ ? getReadableFileSize(appConfig.value.max_single_file_size)
+ : ''
+ const gpx_limit_import = appConfig.value.gpx_limit_import
+ const zipSizeLimit = appConfig.value.max_zip_file_size
+ ? getReadableFileSize(appConfig.value.max_zip_file_size)
+ : ''
const errorMessages: ComputedRef = computed(
() => store.getters[ROOT_STORE.GETTERS.ERROR_MESSAGES]
)
@@ -374,10 +399,14 @@
onUnmounted(() => store.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES))
return {
+ appConfig,
errorMessages,
+ fileSizeLimit,
+ gpx_limit_import,
t,
translatedSports,
withGpx,
+ zipSizeLimit,
workoutDataObject: workoutForm,
onCancel,
updateFile,
@@ -450,15 +479,16 @@
.form-item-radio {
display: flex;
justify-content: space-around;
- .radio {
- label {
- font-weight: normal;
- }
- input {
- margin-top: -2px;
- vertical-align: middle;
+ label {
+ font-weight: normal;
+ @media screen and (max-width: $small-limit) {
+ font-size: 0.9em;
}
}
+ input {
+ margin-top: -2px;
+ vertical-align: middle;
+ }
}
}
@@ -469,6 +499,29 @@
margin: $default-padding * 0.5;
}
}
+
+ .files-help {
+ display: flex;
+ justify-content: space-around;
+
+ background-color: var(--info-background-color);
+ border-radius: $border-radius;
+ color: var(--info-color);
+ font-size: 0.8em;
+
+ margin-top: $default-margin;
+ padding: $default-padding;
+ div {
+ display: flex;
+ @media screen and (max-width: $small-limit) {
+ flex-direction: column;
+ }
+ ul {
+ margin: 0;
+ padding: 0 $default-padding * 2;
+ }
+ }
+ }
}
}
}
diff --git a/fittrackee_client/src/locales/en/api.json b/fittrackee_client/src/locales/en/api.json
index 0c70e619..56a6ab6b 100644
--- a/fittrackee_client/src/locales/en/api.json
+++ b/fittrackee_client/src/locales/en/api.json
@@ -2,10 +2,18 @@
"ERROR": {
"UNKNOWN": "Error. Please try again or contact the administrator.",
"Error, Please try again or contact the administrator": "Error. Please try again or contact the administrator.",
+ "File extension not allowed": "File extension not allowed.",
+ "File size is greater than the allowed size": "File size is greater than the allowed size.",
"Invalid credentials": "Invalid credentials.",
- "Network Error": "Network Error",
+ "Invalid payload": "Invalid data.",
+ "Invalid token, Please log in again": "Invalid token. Please log in again.",
+ "Network Error": "Network Error.",
+ "No file part": "No file provided.",
+ "No selected file": "No selected file.",
+ "Provide a valid auth token": "Provide a valid auth token.",
"Password and password confirmation don't match": "Password and password confirmation don't match.",
"Password: 8 characters required": "Password: 8 characters required.",
+ "Signature expired, Please log in again": "Signature expired. Please log in again.",
"Username: 3 to 12 characters required": "Username: 3 to 12 characters required.",
"Valid email must be provided": "Valid email must be provided."
}
diff --git a/fittrackee_client/src/locales/en/workouts.json b/fittrackee_client/src/locales/en/workouts.json
index 3fb38fc1..81760798 100644
--- a/fittrackee_client/src/locales/en/workouts.json
+++ b/fittrackee_client/src/locales/en/workouts.json
@@ -10,16 +10,19 @@
"EDIT_WORKOUT": "Edit the workout",
"ELEVATION": "elevation",
"END": "end",
- "GPX_FILE": "fichier .gpx",
+ "GPX_FILE": ".gpx file",
"KM": "km",
"LATEST_WORKOUTS": "Latest workouts",
"LOAD_MORE_WORKOUT": "Load more workouts",
"MAX_ALTITUDE": "max. altitude",
+ "MAX_FILES": "max files",
+ "MAX_SIZE": "max size",
"MAX_SPEED": "max. speed",
"MIN_ALTITUDE": "min. altitude",
"NEXT_SEGMENT": "No next segment",
"NEXT_WORKOUT": "Next workout",
"NO_DATA_CLEANING": "data from gpx, without any cleaning",
+ "NO_FOLDER": "no folder inside",
"NO_MAP": "No map",
"NO_NEXT_SEGMENT": "No next segment",
"NO_NEXT_WORKOUT": "No next workout",
@@ -66,5 +69,6 @@
"WORKOUT": "workout | workouts",
"WORKOUT_DATE": "workout date",
"WORKOUT_DELETION_CONFIRMATION": "Are you sure you want to delete this workout?",
- "ZIP_FILE": "or .zip file containing .gpx files"
+ "ZIP_ARCHIVE": ".zip file",
+ "ZIP_ARCHIVE_DESCRIPTION": "or .zip file containing .gpx files"
}
diff --git a/fittrackee_client/src/locales/fr/api.json b/fittrackee_client/src/locales/fr/api.json
index 3014a728..0a842b69 100644
--- a/fittrackee_client/src/locales/fr/api.json
+++ b/fittrackee_client/src/locales/fr/api.json
@@ -2,10 +2,18 @@
"ERROR": {
"UNKNOWN": "Erreur. Veuillez réessayer ou contacter l'administrateur.",
"Error, Please try again or contact the administrator": "Erreur. Veuillez réessayer ou contacter l'administrateur.",
+ "File extension not allowed": "Extension de fichier non autorisée.",
+ "File size is greater than the allowed size": "La taille du fichier est supérieure à la limite autorisée.",
"Invalid credentials": "Identifiants invalides.",
- "Network Error": "Erreur Réseau",
+ "Invalid payload": "Données incorrectes.",
+ "Invalid token, Please log in again": "Jeton invalide. Merci de vous reconnecter.",
+ "No file part": "Pas de fichier fourni.",
+ "No selected file": "Pas de fichier sélectionné.",
+ "Network Error": "Erreur Réseau.",
+ "Provide a valid auth token": "Merci de fournir un jeton valide.",
"Password and password confirmation don't match": "Les mots de passe saisis sont différents.",
"Password: 8 characters required": "8 caractères minimum pour le mot de passe.",
+ "Signature expired, Please log in again": "Signature expirée. Merci de vous reconnecter.",
"Username: 3 to 12 characters required": "3 à 12 caractères requis pour le nom.",
"Valid email must be provided": "L'email fourni n'est pas valide."
}
diff --git a/fittrackee_client/src/locales/fr/workouts.json b/fittrackee_client/src/locales/fr/workouts.json
index 7b2c6fe0..3550ed11 100644
--- a/fittrackee_client/src/locales/fr/workouts.json
+++ b/fittrackee_client/src/locales/fr/workouts.json
@@ -10,16 +10,19 @@
"EDIT_WORKOUT": "Modifier la séance",
"ELEVATION": "altitude",
"END": "fin",
- "GPX_FILE": ".gpx file",
+ "GPX_FILE": "fichier .gpx",
"KM": "km",
"LATEST_WORKOUTS": "Séances récentes",
"LOAD_MORE_WORKOUT": "Charger les séances suivantes",
"MAX_ALTITUDE": "altitude max",
+ "MAX_FILES": "fichiers max. ",
+ "MAX_SIZE": "taille max. ",
"MAX_SPEED": "vitesse max",
"MIN_ALTITUDE": "altitude min",
"NEXT_SEGMENT": "Segment suivant",
"NEXT_WORKOUT": "Séance suivante",
"NO_DATA_CLEANING": "données issues du fichier gpx, sans correction",
+ "NO_FOLDER": "pas de répertoire",
"NO_MAP": "Pas de carte",
"NO_NEXT_SEGMENT": "Pas de segment suivant",
"NO_NEXT_WORKOUT": "Pas de séance suivante",
@@ -66,5 +69,6 @@
"WORKOUT": "séance | séances",
"WORKOUT_DATE": "date de la séance",
"WORKOUT_DELETION_CONFIRMATION": "Etes-vous sûr de vouloir supprimer cette séance ?",
- "ZIP_FILE": "ou une archive .zip contenant des fichiers .gpx"
+ "ZIP_ARCHIVE": "archive .zip",
+ "ZIP_ARCHIVE_DESCRIPTION": "ou une archive .zip contenant des fichiers .gpx"
}
diff --git a/fittrackee_client/src/scss/colors.scss b/fittrackee_client/src/scss/colors.scss
index b04df8e9..9ec430ab 100644
--- a/fittrackee_client/src/scss/colors.scss
+++ b/fittrackee_client/src/scss/colors.scss
@@ -32,7 +32,8 @@
--alert-background-color: #c8cdd3;
--alert-color: #3f3f3f;
-
+ --info-background-color: #e5e7ea;
+ --info-color: var(--app-color);
--error-background-color: #ffd2d2;
--error-color: #db1924;
diff --git a/fittrackee_client/src/store/modules/workouts/actions.ts b/fittrackee_client/src/store/modules/workouts/actions.ts
index 897ec137..a49299a6 100644
--- a/fittrackee_client/src/store/modules/workouts/actions.ts
+++ b/fittrackee_client/src/store/modules/workouts/actions.ts
@@ -168,7 +168,7 @@ export const actions: ActionTree &
context.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)
context.commit(WORKOUTS_STORE.MUTATIONS.SET_WORKOUT_LOADING, true)
if (!payload.file) {
- throw new Error('No gpx file provided')
+ throw new Error('No file part')
}
const form = new FormData()
form.append('file', payload.file)
diff --git a/fittrackee_client/src/utils/files.ts b/fittrackee_client/src/utils/files.ts
new file mode 100644
index 00000000..5292b0d8
--- /dev/null
+++ b/fittrackee_client/src/utils/files.ts
@@ -0,0 +1,14 @@
+const suffixes = ['bytes', 'KB', 'MB', 'GB', 'TB']
+
+export const getReadableFileSize = (
+ fileSize: number,
+ asText = true
+): string | Record => {
+ const i = Math.floor(Math.log(fileSize) / Math.log(1024))
+ if (!fileSize) {
+ return asText ? '0 bytes' : { size: '0', suffix: 'bytes' }
+ }
+ const size = (fileSize / Math.pow(1024, i)).toFixed(1)
+ const suffix = suffixes[i]
+ return asText ? `${size}${suffix}` : { size, suffix }
+}
diff --git a/fittrackee_client/src/utils/index.ts b/fittrackee_client/src/utils/index.ts
index 83084044..4fe7d94d 100644
--- a/fittrackee_client/src/utils/index.ts
+++ b/fittrackee_client/src/utils/index.ts
@@ -17,8 +17,7 @@ export const getApiUrl = (): string => {
// TODO: update api error messages to remove these workarounds
const removeLastEndOfLine = (text: string): string => text.replace(/\n$/gm, '')
const removeLastDot = (text: string): string => text.replace(/\.$/gm, '')
-const removeErrorDot = (text: string): string =>
- text.replace(/^Error\./gm, 'Error,')
+const replaceInternalDots = (text: string): string => text.replace(/\./gm, ',')
export const handleError = (
context:
@@ -33,14 +32,16 @@ export const handleError = (
let errorMessages = !error
? msg
: error.response
- ? error.response.data.message
+ ? error.response.status === 413
+ ? 'File size is greater than the allowed size'
+ : error.response.data.message
? error.response.data.message
: msg
: error.message
? error.message
: msg
errorMessages = removeLastEndOfLine(errorMessages)
- errorMessages = removeErrorDot(errorMessages)
+ errorMessages = replaceInternalDots(errorMessages)
context.commit(
ROOT_STORE.MUTATIONS.SET_ERROR_MESSAGES,
errorMessages.includes('\n')
diff --git a/fittrackee_client/tests/unit/utils/files.spec.ts b/fittrackee_client/tests/unit/utils/files.spec.ts
new file mode 100644
index 00000000..63ee904b
--- /dev/null
+++ b/fittrackee_client/tests/unit/utils/files.spec.ts
@@ -0,0 +1,61 @@
+import { assert } from 'chai'
+
+import { getReadableFileSize } from '@/utils/files'
+
+describe('getReadableFileSize (as text)', () => {
+ const testsParams = [
+ {
+ description: 'returns 0 bytes if provided file size is 0',
+ inputFileSize: 0,
+ expectedReadableFileSize: '0 bytes',
+ },
+ {
+ description: 'returns 1.0KB if provided file size is 1024',
+ inputFileSize: 1024,
+ expectedReadableFileSize: '1.0KB',
+ },
+ {
+ description: 'returns 43.5MB if provided file size is 45654654',
+ inputFileSize: 45654654,
+ expectedReadableFileSize: '43.5MB',
+ },
+ ]
+
+ testsParams.map((testParams) => {
+ it(testParams.description, () => {
+ assert.equal(
+ getReadableFileSize(testParams.inputFileSize, true),
+ testParams.expectedReadableFileSize
+ )
+ })
+ })
+})
+
+describe('getReadableFileSize (as object)', () => {
+ const testsParams = [
+ {
+ description: 'returns 0 bytes if provided file size is 0',
+ inputFileSize: 0,
+ expectedReadableFileSize: { size: '0', suffix: 'bytes' },
+ },
+ {
+ description: 'returns 1.0KB if provided file size is 1024',
+ inputFileSize: 1024,
+ expectedReadableFileSize: { size: '1.0', suffix: 'KB' },
+ },
+ {
+ description: 'returns 43.5MB if provided file size is 45654654',
+ inputFileSize: 45654654,
+ expectedReadableFileSize: { size: '43.5', suffix: 'MB' },
+ },
+ ]
+
+ testsParams.map((testParams) => {
+ it(testParams.description, () => {
+ assert.deepEqual(
+ getReadableFileSize(testParams.inputFileSize, false),
+ testParams.expectedReadableFileSize
+ )
+ })
+ })
+})