+
{isDragActive ? (
-
- {getMessage('uploader_drop')}
-
+ <>
+
0
+ ? 'highlight-color inline relative bottom-0.5 text-5xl mt-1 mb-2'
+ : 'highlight-color inline relative bottom-0.5 text-6xl mt-8 mb-8'
+ }
+ />
+ {getMessage('uploader_drop')}
+ >
) : (
-
- {' '}
+ <>
+ 0
+ ? 'highlight-color inline relative bottom-0.5 text-5xl mt-1 mb-2'
+ : 'highlight-color inline relative bottom-0.5 text-6xl mt-8 mb-8'
+ }
+ />
{getMessage('uploader_description')}
-
+ >
)}
diff --git a/src/components/services/providers/index.tsx b/src/components/services/providers/index.tsx
index 8fc24f6..fc1b069 100644
--- a/src/components/services/providers/index.tsx
+++ b/src/components/services/providers/index.tsx
@@ -5,7 +5,7 @@ import { FeedbackProvider } from './feedback/FeedbackProvider'
const Providers = ({ children }: PropsWithChildren) => {
return (
- {children}
+ {children}
)
}
diff --git a/src/components/services/providers/language/data/de.json b/src/components/services/providers/language/data/de.json
index ad024e3..9fe536d 100644
--- a/src/components/services/providers/language/data/de.json
+++ b/src/components/services/providers/language/data/de.json
@@ -1,7 +1,8 @@
{
"contact": "Kontakt",
- "about": "Über uns",
- "home": "Startseite",
+ "prefix_social_icons": "Ihr findet mich am ehesten auf dem Rad: ",
+ "about": "Über",
+ "home": "Track neu erstellen",
"loading": "Wird geladen...",
"close": "Schließen",
"visualize_file": "Die Datei anzeigen.",
@@ -13,7 +14,7 @@
"uploader_drop": "Dateien hierher ziehen ...",
"intro_header": "Diese Anwendung bietet folgende Funktionalitäten:",
"intro_description_list": [
- "Upload einer oder mehrerer Dateien im GPX- oder FIT-format.",
+ "Upload von Dateien im GPX- oder FIT-format.",
"Zusammenfassen/Mergen dieser Dateien in eine Datei.",
"Kartendarstellung der zusammengefassten Datei.",
"Hinzufügen, Ändern und Löschen von Wegpunkten.",
@@ -32,5 +33,8 @@
"error_download_failure_pl": "Die Dateien konnten nicht gefunden werden. Bitte wiederhole den Upload!",
"error_delete": "Die Datei konnte leider nicht gelöscht werden.",
"error_loading_track": "Der Track konnte leider nicht geladen werden.",
- "error_track_not_found": "Der Track wurde nicht gefunden."
+ "error_track_not_found": "Der Track wurde nicht gefunden.",
+ "not_found_headline": "Not Found",
+ "not_found_text": "Die gesuchte Seite konnte leider nicht gefunden werden.",
+ "not_found_back": "Zurück zur Startseite"
}
diff --git a/src/components/services/providers/language/data/en.json b/src/components/services/providers/language/data/en.json
index e741cde..015a9ad 100644
--- a/src/components/services/providers/language/data/en.json
+++ b/src/components/services/providers/language/data/en.json
@@ -1,11 +1,12 @@
{
"contact": "Contact",
+ "prefix_social_icons": "You'll find me on my bike: ",
"about": "About",
- "home": "Home",
+ "home": "Create new track",
"loading": "Loading...",
"close": "Close",
"visualize_file": "Visualize the file.",
- "visualize_files": "Visualize the merged files.",
+ "visualize_files": "Merged and visualize the files.",
"button_new_process": "Start working on a new file.",
"app_description": "An app dealing with GPS files.",
"technologies_header": "This app was created to test some technologies. These are:",
@@ -32,5 +33,8 @@
"error_download_failure_pl": "The files could not be found on the server. Please upload again!",
"error_delete": "Failed to delete file. Sorry!",
"error_loading_track": "Failed to load track. Sorry!",
- "error_track_not_found": "Track not found."
+ "error_track_not_found": "Track not found.",
+ "not_found_headline": "Not Found",
+ "not_found_text": "Whatever you were looking for, I couldn't find it.",
+ "not_found_back": "Back to homepage"
}
diff --git a/src/components/track/DownloadLink.tsx b/src/components/track/DownloadLink.tsx
index 69b99a6..19e7bc9 100644
--- a/src/components/track/DownloadLink.tsx
+++ b/src/components/track/DownloadLink.tsx
@@ -3,6 +3,7 @@ import { FiDownload } from 'react-icons/fi'
import { GeoJsonObject } from 'geojson'
import { encodeToBase64 } from '../../utils/tools'
import useLanguage from '../../hooks/useLanguage'
+import { Link } from 'react-router-dom'
type DownloadLinkProps = {
trackId: string
@@ -21,22 +22,21 @@ const DownloadLink: React.FC
= ({
}) => {
const { getMessage } = useLanguage()
const baseUrl = import.meta.env.VITE_BACKEND_BASE_URL
+ const linkTo: string =
+ baseUrl +
+ '/tracks/' +
+ trackId +
+ '?mode=dl&type=' +
+ type +
+ (optimizeWaypoints ? '&mode=opt' : '') +
+ (trackname.length > 0 ? `&name=${encodeToBase64(trackname)}` : '') +
+ (geoJson != null ? `&wp=${encodeToBase64(JSON.stringify(geoJson))}` : '')
+
return (
- 0 ? `&name=${encodeToBase64(trackname)}` : '') +
- (geoJson != null ? `&wp=${encodeToBase64(JSON.stringify(geoJson))}` : '')
- }
- >
+
{getMessage('download_as')} {type.toUpperCase()}
-
+
)
}
diff --git a/src/components/track/MarkerSearch.tsx b/src/components/track/MarkerSearch.tsx
index 1a2696c..e559fa4 100644
--- a/src/components/track/MarkerSearch.tsx
+++ b/src/components/track/MarkerSearch.tsx
@@ -81,7 +81,7 @@ const MarkerSearch: React.FC = ({ setMarkerPositions }) => {
id="markerSearchInput"
type="text"
ref={inputRef}
- className="input input-bordered w-full"
+ className="input input-bordered w-full rounded-full"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder={getMessage('search_for_waypoints') as string}
diff --git a/src/components/track/TrackHeader.tsx b/src/components/track/TrackHeader.tsx
index 0497a84..6c10f65 100644
--- a/src/components/track/TrackHeader.tsx
+++ b/src/components/track/TrackHeader.tsx
@@ -1,10 +1,9 @@
import DownloadLink from './DownloadLink'
-import { FaCircleInfo, FaPenToSquare } from 'react-icons/fa6'
+import { FaCircleInfo, FaEye, FaEyeSlash, FaPenToSquare } from 'react-icons/fa6'
import ContentEditable, { ContentEditableEvent } from 'react-contenteditable'
import React, { useMemo, useRef, useState } from 'react'
import { WayPoint } from '../../@types/gps'
import { generateGeoJson, sanitizeFilename } from '../../utils/tools'
-import ResetButton from './ResetButton'
import useLanguage from '../../hooks/useLanguage'
type TrackHeaderProps = {
@@ -12,6 +11,8 @@ type TrackHeaderProps = {
markerPositions: WayPoint[]
trackname: string
setTrackname: React.Dispatch>
+ showPolyline: boolean
+ setShowPolyline: React.Dispatch>
}
const TrackHeader: React.FC = ({
@@ -19,6 +20,8 @@ const TrackHeader: React.FC = ({
markerPositions,
trackname,
setTrackname,
+ showPolyline,
+ setShowPolyline,
}) => {
const [optimizeWaypoints, setOptimizeWaypoints] = useState(false)
const tracknameInputFieldRef: React.RefObject = useRef(null)
@@ -41,68 +44,68 @@ const TrackHeader: React.FC = ({
}
return (
- <>
-
-
-
-
-
-
-
-
-
-
setOptimizeWaypoints(e.target.checked)}
- value=""
- className="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
- />
-
-
-
-
-
-
+
+
+
Trackname
+
+
+
+ {
+ tracknameInputFieldRef?.current?.focus()
+ }}
+ />
-
-
-
-
Trackname
-
-
-
-
{
- tracknameInputFieldRef?.current?.focus()
- }}
- />
+
+ {showPolyline ? (
+ setShowPolyline(false)} />
+ ) : (
+ setShowPolyline(true)} />
+ )}
+
+
+
+
+
+
+
+
setOptimizeWaypoints(e.target.checked)}
+ value=""
+ className="w-4 h-4"
+ />
+
+
+
+
- >
+
)
}
diff --git a/src/components/track/VisualizeTrack.tsx b/src/components/track/VisualizeTrack.tsx
index 7bedbba..def5333 100644
--- a/src/components/track/VisualizeTrack.tsx
+++ b/src/components/track/VisualizeTrack.tsx
@@ -1,4 +1,4 @@
-import React, { createRef, useCallback, useEffect, useState } from 'react'
+import React, { createRef, useCallback } from 'react'
import { LayersControl, MapContainer, Polyline, TileLayer, ZoomControl } from 'react-leaflet'
import {
LatLng,
@@ -8,19 +8,18 @@ import {
} from 'leaflet'
import 'leaflet/dist/leaflet.css'
import Control from 'react-leaflet-custom-control'
-import { FaEye, FaEyeSlash } from 'react-icons/fa6'
import { PoiType, WayPoint } from '../../@types/gps'
import DraggableMarker from './DraggableMarker'
import FitBoundsButton from './FitBoundsButton'
import NewMarkerButton from './NewMarkerButton'
import MarkerSearch from './MarkerSearch'
-import useLanguage from '../../hooks/useLanguage'
type VisualizeTrackProps = {
bounds: LatLngBoundsExpression
positions: LatLngExpression[]
markerPositions: WayPoint[]
setMarkerPositions: React.Dispatch
>
+ showPolyline: boolean
}
const VisualizeTrack: React.FC = ({
@@ -28,10 +27,9 @@ const VisualizeTrack: React.FC = ({
positions,
markerPositions,
setMarkerPositions,
+ showPolyline,
}) => {
- const [showPolyline, setShowPolyline] = useState(true)
const polylineRef = createRef()
- const { getMessage } = useLanguage()
const changeMarkerPosition = useCallback((markerId: string, newPosition: LatLng) => {
setMarkerPositions((prevState) =>
@@ -76,106 +74,60 @@ const VisualizeTrack: React.FC = ({
setMarkerPositions((prevState) => prevState.filter((wp) => wp.id !== id))
}, [])
- useEffect(() => {
- const handleKeyPress = (event: KeyboardEvent) => {
- if (
- document.activeElement?.id === 'editTrackname' ||
- document.activeElement?.id === 'markerSearchInput'
- ) {
- return null
- }
- if (event.key === 'm') {
- setShowPolyline(false)
- }
- }
-
- const handleKeyUp = (event: KeyboardEvent) => {
- if (
- document.activeElement?.id === 'editTrackname' ||
- document.activeElement?.id === 'markerSearchInput'
- ) {
- return null
- }
- if (event.key === 'm') {
- setShowPolyline(true)
- }
- }
-
- document.addEventListener('keypress', handleKeyPress)
- document.addEventListener('keyup', handleKeyUp)
-
- return () => {
- document.removeEventListener('keypress', handleKeyPress)
- document.removeEventListener('keyup', handleKeyPress)
- }
- }, [])
-
return (
- <>
-
-
- {showPolyline ? (
- setShowPolyline(false)} />
- ) : (
- setShowPolyline(true)} />
- )}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {showPolyline && (
-
+
+
+
+
+
+
+
+
+
+
- )}
- {markerPositions.map((waypoint) => (
-
+
+
- ))}
-
-
-
-
-
-
-
-
- >
+
+
+ {showPolyline && (
+
+ )}
+ {markerPositions.map((waypoint) => (
+
+ ))}
+
+
+
+
+
+
+
+
)
}
diff --git a/src/index.css b/src/index.css
index 56fecb0..9148a18 100644
--- a/src/index.css
+++ b/src/index.css
@@ -4,19 +4,40 @@
@layer base {
@font-face {
- font-family: 'Roboto';
+ font-family: 'Roboto-Thin';
+ font-style: normal;
+ font-weight: 100;
+ font-display: swap;
+ src: url(/fonts/Roboto-Thin.ttf) format('truetype');
+ }
+ @font-face {
+ font-family: 'Roboto-Light';
+ font-style: normal;
+ font-weight: 300;
+ font-display: swap;
+ src: url(/fonts/Roboto-Light.ttf) format('truetype');
+ }
+ @font-face {
+ font-family: 'Roboto-Regular';
font-style: normal;
font-weight: 400;
font-display: swap;
- src:
- url(/fonts/Roboto-Regular.woff) format('woff'),
- url(/fonts/Roboto-Regular.woff2) format('woff2');
+ src: url(/fonts/Roboto-Regular.ttf) format('truetype');
+ }
+ @font-face {
+ font-family: 'Roboto-Medium';
+ font-style: normal;
+ font-weight: 500;
+ font-display: swap;
+ src: url(/fonts/Roboto-Medium.ttf) format('truetype');
+ }
+ @font-face {
+ font-family: 'Roboto-Bold';
+ font-style: bold;
+ font-weight: 700;
+ font-display: swap;
+ src: url(/fonts/Roboto-Bold.ttf) format('truetype');
}
-}
-
-.leaflet-container {
- height: 100vh;
- width: 90vw;
}
#controlButton {
@@ -51,3 +72,66 @@
font-size: 28px;
padding: 5px;
}
+
+[data-theme='light'] .logo {
+ fill: white;
+}
+
+[data-theme='dark'] .logo {
+ fill: #202936;
+}
+
+.btn-nav {
+ font-family: 'Roboto-Medium';
+ background-color: transparent;
+ border: none;
+ --tw-text-opacity: 1;
+ color: rgb(255 255 255 / var(--tw-text-opacity));
+ padding: 5px 14px;
+ text-align: center;
+ text-decoration: none;
+ display: inline-block;
+ font-size: 16px;
+ border-radius: 20px;
+}
+
+.btn-nav:hover {
+ outline: 1px solid white;
+}
+
+.font-thin {
+ font-weight: 100;
+ font-family: Roboto-Thin;
+}
+
+.font-light {
+ font-weight: 300;
+ font-family: Roboto-Light;
+}
+
+.font-normal {
+ font-weight: 400;
+ font-family: Roboto-Regular;
+}
+
+.font-medium {
+ font-weight: 500;
+ font-family: Roboto-Medium;
+}
+
+.font-bold {
+ font-weight: 700;
+ font-family: Roboto-Bold;
+}
+
+.svg-stroke {
+ fill: none;
+ stroke: #06d26a;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-dasharray: 0 0 3 3;
+}
+
+.svg-fill {
+ stroke-width: 0px;
+}
diff --git a/src/main.tsx b/src/main.tsx
index 964aeb4..861f8c0 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -3,8 +3,19 @@ import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
-ReactDOM.createRoot(document.getElementById('root')!).render(
-
-
- ,
-)
+const prepareMockServiceWorker = async () => {
+ if (process.env.NODE_ENV === 'development') {
+ const { worker } = await import('./mocks/browser')
+ return worker.start()
+ }
+
+ return Promise.resolve()
+}
+
+prepareMockServiceWorker().then(() => {
+ ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+ ,
+ )
+})
diff --git a/src/mocks/browser.ts b/src/mocks/browser.ts
new file mode 100644
index 0000000..4d6fbb1
--- /dev/null
+++ b/src/mocks/browser.ts
@@ -0,0 +1,6 @@
+import { setupWorker } from 'msw/browser'
+import { gpsHandlers } from './gpsHandlers'
+import { tileHandlers } from './tileHandlers'
+import { photonHandlers } from './photonHandlers'
+
+export const worker = setupWorker(...gpsHandlers, ...tileHandlers, ...photonHandlers)
diff --git a/src/mocks/gpsHandlers.ts b/src/mocks/gpsHandlers.ts
new file mode 100644
index 0000000..272af10
--- /dev/null
+++ b/src/mocks/gpsHandlers.ts
@@ -0,0 +1,54 @@
+import { http, HttpResponse } from 'msw'
+import { v4 as uuidv4 } from 'uuid'
+
+export const gpsHandlers = [
+ http.get(import.meta.env.VITE_BACKEND_BASE_URL + '/version', () => {
+ return HttpResponse.json({ app: '1.2.3', git: '0.1.0-SNAPSHOT' })
+ }),
+ http.get(import.meta.env.VITE_BACKEND_BASE_URL + '/tracks/:trackId', async ({ request }) => {
+ if (request.headers.has('Accept') && request.headers.get('Accept') === 'application/geo+json') {
+ const buffer = await fetch('/mocks/track.json').then((response) => response.arrayBuffer())
+ return HttpResponse.arrayBuffer(buffer, {
+ headers: { 'Content-Type': 'application/geo+json' },
+ })
+ }
+
+ return new HttpResponse('Not found', { status: 404 })
+ }),
+ http.post(import.meta.env.VITE_BACKEND_BASE_URL + '/tracks', async ({ request }) => {
+ const data = await request.formData()
+ const file = data.get('file')
+
+ if (!file) {
+ return new HttpResponse('Missing document', { status: 400 })
+ }
+
+ if (!(file instanceof File)) {
+ return new HttpResponse('Uploaded document is not a File', { status: 400 })
+ }
+
+ const uuid = uuidv4()
+ return HttpResponse.json([
+ {
+ id: uuid,
+ filename: file.name,
+ mimeType: 'application/octet-stream',
+ href: 'http://localhost:7001/files/' + uuid,
+ size: 1733,
+ },
+ ])
+ }),
+ http.post(import.meta.env.VITE_BACKEND_BASE_URL + '/merge', async () => {
+ const uuid = uuidv4()
+ return HttpResponse.json({
+ id: uuid,
+ filename: 'merged.gpx',
+ mimeType: 'application/octet-stream',
+ href: 'http://localhost:7001/files/' + uuid,
+ size: 3444,
+ })
+ }),
+ http.delete(import.meta.env.VITE_BACKEND_BASE_URL + '/tracks/*', async () => {
+ return new HttpResponse(null, { status: 204 })
+ }),
+]
diff --git a/src/mocks/photonHandlers.ts b/src/mocks/photonHandlers.ts
new file mode 100644
index 0000000..ad58b0a
--- /dev/null
+++ b/src/mocks/photonHandlers.ts
@@ -0,0 +1,10 @@
+import { http, HttpResponse } from 'msw'
+
+export const photonHandlers = [
+ http.get('https://photon.komoot.io/api/', async () => {
+ const buffer = await fetch('/mocks/photon.json').then((response) => response.arrayBuffer())
+ return HttpResponse.arrayBuffer(buffer, {
+ headers: { 'Content-Type': 'application/json;charset=utf-8' },
+ })
+ }),
+]
diff --git a/src/mocks/tileHandlers.ts b/src/mocks/tileHandlers.ts
new file mode 100644
index 0000000..f11b465
--- /dev/null
+++ b/src/mocks/tileHandlers.ts
@@ -0,0 +1,28 @@
+import { http, HttpResponse } from 'msw'
+
+export const tileHandlers = [
+ // OpenStreetMap
+ http.get('https://*.tile.openstreetmap.org/*/*/*.png', async () => {
+ const buffer = await fetch('/mocks/tile.png').then((response) => response.arrayBuffer())
+ return HttpResponse.arrayBuffer(buffer, {
+ headers: { 'Content-Type': 'image/png' },
+ })
+ }),
+ // OpenTopoMap
+ http.get('https://*.tile.opentopomap.org/*/*/*.png', async () => {
+ const buffer = await fetch('/mocks/topo.png').then((response) => response.arrayBuffer())
+ return HttpResponse.arrayBuffer(buffer, {
+ headers: { 'Content-Type': 'image/png' },
+ })
+ }),
+ // Esri / ArcGIS
+ http.get(
+ 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/*/*/*',
+ async () => {
+ const buffer = await fetch('/mocks/tile.jpeg').then((response) => response.arrayBuffer())
+ return HttpResponse.arrayBuffer(buffer, {
+ headers: { 'Content-Type': 'image/jpeg' },
+ })
+ },
+ ),
+]
diff --git a/src/pages/AboutScreen.tsx b/src/pages/AboutScreen.tsx
index 9774532..74e637f 100644
--- a/src/pages/AboutScreen.tsx
+++ b/src/pages/AboutScreen.tsx
@@ -3,6 +3,9 @@ import { BackendVersion } from '../@types/common'
import { AxiosResponse } from 'axios'
import API from '../components/services/backend/gps-backend-api'
import useLanguage from '../hooks/useLanguage'
+import { FaArrowRight, FaGithub, FaStrava } from 'react-icons/fa6'
+import { SiKomoot } from 'react-icons/si'
+import ErtIcon from '../components/common/ErtIcon'
const NO_VERSION = { app: 'N/A', git: 'N/A' }
@@ -28,17 +31,18 @@ const AboutScreen = () => {
}, [])
return (
- <>
- GPS-Tools
+
)
}
diff --git a/src/pages/MergeScreen.tsx b/src/pages/MergeScreen.tsx
index 151d760..0ffeeba 100644
--- a/src/pages/MergeScreen.tsx
+++ b/src/pages/MergeScreen.tsx
@@ -2,15 +2,24 @@ import UploadForm from '../components/merge/UploadForm'
import MergeFiles from '../components/merge/MergeFiles'
import Intro from '../components/merge/Intro'
import { UploadProvider } from '../components/services/providers/upload/UploadProvider'
+import GpsToolsGraphic from '../components/merge/GpsToolsGraphic'
const MergeScreen = () => (
-
-
GPS-Tools
-
-
-
-
-
+
+
+
GPS-Tools
+
+
+
+
+
+
+
+
+
)
diff --git a/src/pages/NotFoundScreen.tsx b/src/pages/NotFoundScreen.tsx
index 97e9594..5e1b890 100644
--- a/src/pages/NotFoundScreen.tsx
+++ b/src/pages/NotFoundScreen.tsx
@@ -1,23 +1,27 @@
import { FaAngleRight } from 'react-icons/fa6'
import { BsEmojiDizzy } from 'react-icons/bs'
+import useLanguage from '../hooks/useLanguage'
-const NotFoundScreen = () => (
-
-
- Not Found
-
-
-
-
- Whatever you were looking for, I couldn't find it. But have you seen this already:
+const NotFoundScreen = () => {
+ const { getMessage } = useLanguage()
+
+ return (
+
+
+ {getMessage('not_found_headline')}
+
+
+
+ {getMessage('not_found_text')}
+
+ {getMessage('not_found_back')}:
GPS-Tools
- {' '}
- ?
+
-
-)
+ )
+}
export default NotFoundScreen
diff --git a/src/pages/TrackScreen.tsx b/src/pages/TrackScreen.tsx
index 7cd95c3..a686442 100644
--- a/src/pages/TrackScreen.tsx
+++ b/src/pages/TrackScreen.tsx
@@ -20,6 +20,7 @@ const TrackScreen = () => {
const [isLoading, setIsLoading] = useState(true)
const [trackname, setTrackname] = useState
('')
+ const [showPolyline, setShowPolyline] = useState(true)
const [positions, setPositions] = useState([])
const [markerPositions, setMarkerPositions] = useState([])
const [bounds, setBounds] = useState([
@@ -27,6 +28,40 @@ const TrackScreen = () => {
[0, 0],
])
+ useEffect(() => {
+ const handleKeyPress = (event: KeyboardEvent) => {
+ if (
+ document.activeElement?.id === 'editTrackname' ||
+ document.activeElement?.id === 'markerSearchInput'
+ ) {
+ return null
+ }
+ if (event.key === 'm') {
+ setShowPolyline(false)
+ }
+ }
+
+ const handleKeyUp = (event: KeyboardEvent) => {
+ if (
+ document.activeElement?.id === 'editTrackname' ||
+ document.activeElement?.id === 'markerSearchInput'
+ ) {
+ return null
+ }
+ if (event.key === 'm') {
+ setShowPolyline(true)
+ }
+ }
+
+ document.addEventListener('keypress', handleKeyPress)
+ document.addEventListener('keyup', handleKeyUp)
+
+ return () => {
+ document.removeEventListener('keypress', handleKeyPress)
+ document.removeEventListener('keyup', handleKeyPress)
+ }
+ }, [])
+
useEffect(() => {
setIsLoading(true)
const config = {
@@ -100,24 +135,28 @@ const TrackScreen = () => {
return (
<>
- GPS-Tools
{isLoading ? (
{getMessage('loading')}
) : positions.length > 0 && trackId !== undefined ? (
- <>
+
) : (
<>
{getMessage('error_track_not_found')}
diff --git a/tailwind.config.js b/tailwind.config.js
index 69549b4..6c348c9 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -8,13 +8,108 @@ export default {
theme: {
extend: {
fontFamily: {
- sans: ['Roboto', 'sans-serif'],
+ sans: ['Roboto-Regular', 'sans-serif'],
serif: ['Cooper', 'serif'],
},
},
},
plugins: [require('daisyui')],
daisyui: {
- themes: ['dark', 'light'],
+ themes: [
+ {
+ dark: {
+ ...require('daisyui/src/theming/themes')['dark'],
+ '.bg-navbar': {
+ 'background-color': '#06D26A' /* For browsers that do not support gradients */,
+ 'background-image': 'linear-gradient(to right, #06D26A , #00918C)',
+ },
+ '.bg-footer': {
+ 'background-color': '#263241',
+ 'box-shadow': '0px 0px 0.5px 0.5px #000000',
+ },
+ '.dropzone': {
+ 'background-color': '#263241',
+ 'border-color': '#06D26A',
+ },
+ '.inline-button': {
+ 'border-color': '#06D26A',
+ 'border-width': '1.5px',
+ 'border-radius': '9999px',
+ color: '#EAEAEA',
+ },
+ '.inline-button:hover': {
+ 'border-color': '#06D26A',
+ 'border-width': '1.5px',
+ 'border-radius': '9999px',
+ color: '#202936',
+ 'background-color': '#06D26A',
+ },
+ '.outline-button': {
+ 'border-width': '0px',
+ 'border-radius': '9999px',
+ 'background-color': '#06D26A',
+ color: '#263241',
+ },
+ '.text-base-content': {
+ color: '#EAEAEA',
+ },
+ '.highlight-color': {
+ color: '#06D26A',
+ },
+ 'background-color': '#202936',
+ '.svg-symbol': { fill: '#06d26a' },
+ '.svg-line': { stroke: '#06d26a' },
+ '.svg-icon': { fill: '#EAEAEA' },
+ '.svg-icon:hover': { fill: '#06d26a' },
+ },
+ },
+ {
+ light: {
+ ...require('daisyui/src/theming/themes')['light'],
+ '.bg-navbar': {
+ 'background-color': '#FF3E3E' /* For browsers that do not support gradients */,
+ 'background-image': 'linear-gradient(to right, #FF3E3E , #FFA400)',
+ },
+ '.bg-footer': {
+ 'background-color': '#FFF8F9',
+ 'box-shadow': '0px 0px 0.5px 0.5px #474F5A',
+ },
+ '.dropzone': {
+ 'background-color': '#FFF8F9',
+ 'border-color': '#FF3E3E',
+ },
+ '.inline-button': {
+ 'border-color': '#FF3E3E',
+ 'border-width': '1.5px',
+ 'border-radius': '9999px',
+ color: '#474F5A',
+ },
+ '.inline-button:hover': {
+ 'border-color': '#FF3E3E',
+ 'border-width': '1.5px',
+ 'border-radius': '9999px',
+ color: 'white',
+ 'background-color': '#FF3E3E',
+ },
+ '.outline-button': {
+ 'border-width': '0px',
+ 'border-radius': '9999px',
+ 'background-color': '#FF3E3E',
+ color: 'white',
+ },
+ '.text-base-content': {
+ color: '#474F5A',
+ },
+ '.highlight-color': {
+ color: '#FF3E3E',
+ },
+ 'background-color': 'white',
+ '.svg-symbol': { fill: '#ff7917' },
+ '.svg-line': { stroke: '#ff3e3e' },
+ '.svg-icon': { fill: '#474F5A' },
+ '.svg-icon:hover': { fill: '#FF3E3E' },
+ },
+ },
+ ],
},
}
diff --git a/vite.config.ts b/vite.config.ts
index 29763bf..ac9bd5b 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -10,5 +10,6 @@ export default defineConfig({
environment: 'jsdom',
setupFiles: './tests/vitest-setup.ts',
css: true,
+ exclude: ['**/node_modules/**', '**/dist/**', '**/e2e/**', '**/.{idea,git,cache,output,temp}/**', '**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,playwright}.config.*']
},
})