diff --git a/frontend/src/components/FileUploadField.tsx b/frontend/src/components/FileUploadField.tsx
index f3071e3..ace1423 100644
--- a/frontend/src/components/FileUploadField.tsx
+++ b/frontend/src/components/FileUploadField.tsx
@@ -1,29 +1,31 @@
-import React from 'react'
-import {Icon, Segment, Header, Button} from 'semantic-ui-react'
+import React from "react";
+import { Icon, Segment, Header, Button } from "semantic-ui-react";
+import { useTranslation } from "react-i18next";
-import {FileDrop} from 'components'
+import { FileDrop } from "components";
-export default function FileUploadField({onSelect: onSelect_, multiple}) {
- const labelRef = React.useRef()
- const [labelRefState, setLabelRefState] = React.useState()
+export default function FileUploadField({ onSelect: onSelect_, multiple }) {
+ const { t } = useTranslation();
+ const labelRef = React.useRef();
+ const [labelRefState, setLabelRefState] = React.useState();
- const onSelect = multiple ? onSelect_ : (files) => onSelect_(files?.[0])
+ const onSelect = multiple ? onSelect_ : (files) => onSelect_(files?.[0]);
React.useLayoutEffect(
() => {
- setLabelRefState(labelRef.current)
+ setLabelRefState(labelRef.current);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[labelRef.current]
- )
+ );
function onChangeField(e) {
- e.preventDefault?.()
+ e.preventDefault?.();
if (e.target.files && e.target.files.length) {
- onSelect(e.target.files)
+ onSelect(e.target.files);
}
- e.target.value = '' // reset the form field for uploading again
+ e.target.value = ""; // reset the form field for uploading again
}
return (
@@ -31,7 +33,14 @@ export default function FileUploadField({onSelect: onSelect_, multiple}) {
{labelRefState && (
- {({draggingOverFrame, draggingOverTarget, onDragOver, onDragLeave, onDrop, onClick}) => (
+ {({
+ draggingOverFrame,
+ draggingOverTarget,
+ onDragOver,
+ onDragLeave,
+ onDrop,
+ onClick,
+ }) => (
- Drop file{multiple ? 's' : ''} here or click to select {multiple ? 'them' : 'one'} for upload
+ {multiple
+ ? t("FileUploadField.dropOrClickMultiple")
+ : t("FileUploadField.dropOrClick")}
)}
)}
- >
- )
+ >
+ );
}
diff --git a/frontend/src/pages/UploadPage.tsx b/frontend/src/pages/UploadPage.tsx
index 04ba7a8..a76919c 100644
--- a/frontend/src/pages/UploadPage.tsx
+++ b/frontend/src/pages/UploadPage.tsx
@@ -1,45 +1,46 @@
-import _ from 'lodash'
-import React from 'react'
-import {List, Loader, Table, Icon} from 'semantic-ui-react'
-import {Link} from 'react-router-dom'
+import _ from "lodash";
+import React from "react";
+import { List, Loader, Table, Icon } from "semantic-ui-react";
+import { Link } from "react-router-dom";
+import { useTranslation } from "react-i18next";
-import {FileUploadField, Page} from 'components'
-import type {Track} from 'types'
-import api from 'api'
-import configPromise from 'config'
+import { FileUploadField, Page } from "components";
+import type { Track } from "types";
+import api from "api";
+import configPromise from "config";
function isSameFile(a: File, b: File) {
- return a.name === b.name && a.size === b.size
+ return a.name === b.name && a.size === b.size;
}
function formatFileSize(bytes: number) {
if (bytes < 1024) {
- return `${bytes} bytes`
+ return `${bytes} bytes`;
}
- bytes /= 1024
+ bytes /= 1024;
if (bytes < 1024) {
- return `${bytes.toFixed(1)} KiB`
+ return `${bytes.toFixed(1)} KiB`;
}
- bytes /= 1024
+ bytes /= 1024;
if (bytes < 1024) {
- return `${bytes.toFixed(1)} MiB`
+ return `${bytes.toFixed(1)} MiB`;
}
- bytes /= 1024
- return `${bytes.toFixed(1)} GiB`
+ bytes /= 1024;
+ return `${bytes.toFixed(1)} GiB`;
}
type FileUploadResult =
| {
- track: Track
+ track: Track;
}
| {
- errors: Record
- }
+ errors: Record;
+ };
export function FileUploadStatus({
id,
@@ -47,108 +48,127 @@ export function FileUploadStatus({
onComplete,
slug,
}: {
- id: string
- file: File
- onComplete: (id: string, result: FileUploadResult) => void
- slug?: string
+ id: string;
+ file: File;
+ onComplete: (id: string, result: FileUploadResult) => void;
+ slug?: string;
}) {
- const [progress, setProgress] = React.useState(0)
+ const [progress, setProgress] = React.useState(0);
React.useEffect(
() => {
- let xhr
+ let xhr;
async function _work() {
- const formData = new FormData()
- formData.append('body', file)
+ const formData = new FormData();
+ formData.append("body", file);
- xhr = new XMLHttpRequest()
- xhr.withCredentials = true
+ xhr = new XMLHttpRequest();
+ xhr.withCredentials = true;
const onProgress = (e) => {
- const progress = (e.loaded || 0) / (e.total || 1)
- setProgress(progress)
- }
+ const progress = (e.loaded || 0) / (e.total || 1);
+ setProgress(progress);
+ };
const onLoad = (e) => {
- onComplete(id, xhr.response)
- }
+ onComplete(id, xhr.response);
+ };
- xhr.responseType = 'json'
- xhr.onload = onLoad
- xhr.upload.onprogress = onProgress
+ xhr.responseType = "json";
+ xhr.onload = onLoad;
+ xhr.upload.onprogress = onProgress;
- const config = await configPromise
+ const config = await configPromise;
if (slug) {
- xhr.open('PUT', `${config.apiUrl}/tracks/${slug}`)
+ xhr.open("PUT", `${config.apiUrl}/tracks/${slug}`);
} else {
- xhr.open('POST', `${config.apiUrl}/tracks`)
+ xhr.open("POST", `${config.apiUrl}/tracks`);
}
// const accessToken = await api.getValidAccessToken()
// xhr.setRequestHeader('Authorization', accessToken)
- xhr.send(formData)
+ xhr.send(formData);
}
- _work()
- return () => xhr.abort()
+ _work();
+ return () => xhr.abort();
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[file]
- )
+ );
+ const { t } = useTranslation();
return (
- {' '}
- {progress < 1 ? `Uploading ${(progress * 100).toFixed(0)}%` : 'Processing...'}
+ {" "}
+ {progress < 1
+ ? t("UploadPage.uploadProgress", {
+ progress: (progress * 100).toFixed(0),
+ })
+ : t("UploadPage.processing")}
- )
+ );
}
type FileEntry = {
- id: string
- file?: File | null
- size: number
- name: string
- result?: FileUploadResult
-}
+ id: string;
+ file?: File | null;
+ size: number;
+ name: string;
+ result?: FileUploadResult;
+};
export default function UploadPage() {
- const [files, setFiles] = React.useState([])
+ const [files, setFiles] = React.useState([]);
const onCompleteFileUpload = React.useCallback(
(id, result) => {
- setFiles((files) => files.map((file) => (file.id === id ? {...file, result, file: null} : file)))
+ setFiles((files) =>
+ files.map((file) =>
+ file.id === id ? { ...file, result, file: null } : file
+ )
+ );
},
[setFiles]
- )
+ );
function onSelectFiles(fileList) {
const newFiles = Array.from(fileList).map((file) => ({
- id: 'file-' + String(Math.floor(Math.random() * 1000000)),
+ id: "file-" + String(Math.floor(Math.random() * 1000000)),
file,
name: file.name,
size: file.size,
- }))
- setFiles(files.filter((a) => !newFiles.some((b) => isSameFile(a, b))).concat(newFiles))
+ }));
+ setFiles(
+ files
+ .filter((a) => !newFiles.some((b) => isSameFile(a, b)))
+ .concat(newFiles)
+ );
}
+ const { t } = useTranslation();
+
return (
{files.length ? (
- Filename
- Size
- Status / Title
+
+ {t("UploadPage.table.filename")}
+
+ {t("UploadPage.table.size")}
+
+ {t("UploadPage.table.statusTitle")}
+
- {files.map(({id, name, size, file, result}) => (
+ {files.map(({ id, name, size, file, result }) => (
@@ -159,7 +179,9 @@ export default function UploadPage() {
{result?.errors ? (
{_.sortBy(Object.entries(result.errors))
- .filter(([field, message]) => typeof message === 'string')
+ .filter(
+ ([field, message]) => typeof message === "string"
+ )
.map(([field, message]) => (
@@ -169,15 +191,29 @@ export default function UploadPage() {
) : result ? (
<>
- {result.track?.title || 'Unnamed track'}
+ {" "}
+ {result.track?.title || t("general.unnamedTrack")}
>
) : (
-
+
)}
- {result?.track ? Show : null}
- {result?.track ? Edit : null}
+ {result?.track ? (
+
+ {t("general.show")}
+
+ ) : null}
+
+
+ {result?.track ? (
+
+ {t("general.edit")}
+
+ ) : null}
))}
@@ -187,5 +223,5 @@ export default function UploadPage() {
- )
+ );
}
diff --git a/frontend/src/translations/de.yaml b/frontend/src/translations/de.yaml
index 29561aa..32b130c 100644
--- a/frontend/src/translations/de.yaml
+++ b/frontend/src/translations/de.yaml
@@ -3,6 +3,8 @@ general:
unnamedTrack: Unbenannte Fahrt
public: Öffentlich
private: Privat
+ show: Anzeigen
+ edit: Bearbeiten
App:
footer:
@@ -87,3 +89,19 @@ ExportPage:
shapefile: Shapefile (ZIP)
boundingBox:
label: Geografischer Bereich
+
+UploadPage:
+ uploadProgress: Lade hoch {progress}%
+ processing: Verarbeiten...
+
+ table:
+ filename: Dateiname
+ size: Größe
+ statusTitle: Status / Titel
+
+
+FileUploadField:
+ dropOrClick: Datei hierher ziehen oder klicken, um eine zum Hochladen auszuwählen
+ dropOrClickMultiple: Dateien hierher ziehen oder klicken, um Dateien zum Hochladen auszuwählen
+ uploadFile: Datei hochladen
+ uploadFiles: Dateien hochladen
diff --git a/frontend/src/translations/en.yaml b/frontend/src/translations/en.yaml
index fe5c91f..622cdeb 100644
--- a/frontend/src/translations/en.yaml
+++ b/frontend/src/translations/en.yaml
@@ -7,6 +7,8 @@ general:
unnamedTrack: Unnamed track
public: Public
private: Private
+ show: Show
+ edit: Edit
App:
footer:
@@ -92,3 +94,19 @@ ExportPage:
shapefile: Shapefile (ZIP)
boundingBox:
label: Bounding Box
+
+UploadPage:
+ uploadProgress: Uploading {progress}%
+ processing: Processing...
+
+ table:
+ filename: Filename
+ size: Size
+ statusTitle: Status / Title
+
+
+FileUploadField:
+ dropOrClick: Drop file here or click to select one for upload
+ dropOrClickMultiple: Drop files here or click to select them for upload
+ uploadFile: Upload file
+ uploadFiles: Upload files