adds trail export

This commit is contained in:
Christian Beutel
2024-05-05 15:46:17 +02:00
parent 930feda4d6
commit 756a9fa79f
18 changed files with 310 additions and 31 deletions

80
web/package-lock.json generated
View File

@@ -18,6 +18,7 @@
"crypto-random-string": "^5.0.0",
"isomorphic-xml2js": "^0.1.3",
"jspdf": "^2.5.1",
"jszip": "^3.10.1",
"leaflet": "^1.9.4",
"leaflet-gpx": "^1.7.0",
"leaflet.awesome-markers": "^2.0.5",
@@ -1884,6 +1885,11 @@
"url": "https://opencollective.com/core-js"
}
},
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
},
"node_modules/cross-fetch": {
"version": "3.1.8",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz",
@@ -2879,6 +2885,11 @@
"node": ">=0.10.0"
}
},
"node_modules/immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
},
"node_modules/import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -3442,6 +3453,22 @@
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
"optional": true
},
"node_modules/jszip": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
"dependencies": {
"lie": "~3.3.0",
"pako": "~1.0.2",
"readable-stream": "~2.3.6",
"setimmediate": "^1.0.5"
}
},
"node_modules/jszip/node_modules/pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
},
"node_modules/kleur": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
@@ -3465,6 +3492,14 @@
"resolved": "https://registry.npmjs.org/leaflet.awesome-markers/-/leaflet.awesome-markers-2.0.5.tgz",
"integrity": "sha512-Ne/xDjkGyaujwNVVkv2tyXQUV0ZW7gZ0Mo0FuQY4jp2qWrvXi0hwDBvmZyF/8YOvybyMabTMM/mFWCTd1jZIQA=="
},
"node_modules/lie": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
"dependencies": {
"immediate": "~3.0.5"
}
},
"node_modules/lilconfig": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
@@ -4394,6 +4429,11 @@
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"node_modules/property-expr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz",
@@ -4486,6 +4526,25 @@
"pify": "^2.3.0"
}
},
"node_modules/readable-stream": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/readable-stream/node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -4682,6 +4741,11 @@
"node": ">=6"
}
},
"node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -4761,6 +4825,11 @@
"node": ">= 0.4"
}
},
"node_modules/setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
},
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -4884,6 +4953,14 @@
"node": ">= 0.4"
}
},
"node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/string-width": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
@@ -5611,8 +5688,7 @@
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"dev": true
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"node_modules/utrie": {
"version": "1.0.2",

View File

@@ -42,6 +42,7 @@
"crypto-random-string": "^5.0.0",
"isomorphic-xml2js": "^0.1.3",
"jspdf": "^2.5.1",
"jszip": "^3.10.1",
"leaflet": "^1.9.4",
"leaflet-gpx": "^1.7.0",
"leaflet.awesome-markers": "^2.0.5",

View File

@@ -1,8 +1,6 @@
import type { User } from '$lib/models/user'
import { pb } from '$lib/pocketbase'
import { settings_show } from '$lib/stores/settings_store';
import { currentUser } from '$lib/stores/user_store'
import { get } from 'svelte/store';
import type { User } from '$lib/models/user';
import { pb } from '$lib/pocketbase';
import { currentUser } from '$lib/stores/user_store';
pb.authStore.loadFromCookie(document.cookie)
pb.authStore.onChange(() => {

View File

@@ -9,17 +9,22 @@
} from "$lib/stores/list_store";
import { show_toast } from "$lib/stores/toast_store";
import { trails_delete } from "$lib/stores/trail_store";
import { getFileURL } from "$lib/util/file_util";
import { trail2gpx } from "$lib/util/gpx_util";
import { gpx } from "$lib/vendor/toGeoJSON/toGeoJSON";
import { _ } from "svelte-i18n";
import Dropdown, { type DropdownItem } from "../base/dropdown.svelte";
import ConfirmModal from "../confirm_modal.svelte";
import ListSelectModal from "../list/list_select_modal.svelte";
import { _ } from "svelte-i18n";
import TrailExportModal from "./trail_export_modal.svelte";
import JSZip from "jszip";
import { getFileURL, saveAs } from "$lib/util/file_util";
export let trail: Trail;
export let mode: "overview" | "map";
let openConfirmModal: () => void;
let openListSelectModal: () => void;
let openExportModal: () => void;
const dropdownItems: DropdownItem[] = [
mode == "overview"
@@ -34,7 +39,7 @@
...(trail.gpx
? [
{
text: $_("download-gpx"),
text: $_("export"),
value: "download",
icon: "download",
},
@@ -65,7 +70,7 @@
} else if (item.value == "print") {
goto(`/map/trail/${trail.id}/print`);
} else if (item.value == "download") {
downloadURI(getFileURL(trail, trail.gpx), trail.gpx!);
openExportModal();
} else if (item.value == "edit") {
goto(`/trail/edit/${trail.id}`);
} else if (item.value == "delete") {
@@ -73,13 +78,46 @@
}
}
function downloadURI(uri: string, name: string) {
var link = document.createElement("a");
link.setAttribute("download", name);
link.href = uri;
document.body.appendChild(link);
link.click();
link.remove();
async function exportTrail(exportSettings: {
fileFormat: "gpx" | "json";
photos: boolean;
summitLog: boolean;
}) {
try {
let fileData: string = await trail2gpx(trail);
if (exportSettings.fileFormat == "json") {
fileData = JSON.stringify(gpx(new DOMParser().parseFromString(fileData, "text/xml")));
}
const zip = new JSZip();
zip.file(`${trail.name}.${exportSettings.fileFormat}`, fileData);
if (exportSettings.photos) {
const photoFolder = zip.folder($_("photos"));
for (const photo of trail.photos) {
const photoURL = getFileURL(trail, photo);
const photoBlob = await fetch(photoURL).then((response) =>
response.blob(),
);
const photoData = new File([photoBlob], photo);
photoFolder?.file(photo, photoData, { base64: true });
}
}
if(exportSettings.summitLog) {
let summitLogString = ""
for (const summitLog of trail.expand.summit_logs) {
summitLogString += `${summitLog.date},${summitLog.text}\n`
}
zip.file(`${trail.name} - ${$_("summit-book")}.csv`, summitLogString)
}
const blob = await zip.generateAsync({ type: "blob" });
saveAs(blob, `${trail.name}.zip`);
} catch (e) {
console.error(e);
show_toast({
type: "error",
icon: "close",
text: $_("error-exporting-trail"),
});
}
}
async function deleteTrail() {
@@ -137,3 +175,7 @@
bind:openModal={openListSelectModal}
on:change={(e) => handleListSelection(e.detail)}
></ListSelectModal>
<TrailExportModal
bind:openModal={openExportModal}
on:export={(e) => exportTrail(e.detail)}
></TrailExportModal>

View File

@@ -0,0 +1,80 @@
<script lang="ts">
import Modal from "$lib/components/base/modal.svelte";
import { createEventDispatcher } from "svelte";
import { _ } from "svelte-i18n";
import Select from "../base/select.svelte";
export let openModal: (() => void) | undefined = undefined;
export let closeModal: (() => void) | undefined = undefined;
const dispatch = createEventDispatcher();
const fileFormats = [
{ text: "GPX", value: "gpx" },
{ text: "GeoJSON", value: "json" },
];
const exportSettings = {
fileFormat: "gpx",
photos: false,
summitLog: false,
};
function exportTrail() {
dispatch("export", exportSettings);
closeModal!();
}
</script>
<Modal
id="export-modal"
title="Export trail"
size="max-w-sm"
bind:openModal
bind:closeModal
>
<div slot="content">
<div class="mb-3">
<Select
bind:value={exportSettings.fileFormat}
items={fileFormats}
label={$_("file-format")}
></Select>
</div>
<div class="mb-2">
<input
id="include-photos-checkbox"
type="checkbox"
bind:checked={exportSettings.photos}
class="w-4 h-4 bg-input-background accent-primary border-input-border focus:ring-input-ring focus:ring-2"
/>
<label for="include-photos-checkbox" class="ms-2 text-sm"
>{$_("photos")}</label
>
</div>
<div class="mb-2">
<input
id="include-summit-log-checkbox"
type="checkbox"
bind:checked={exportSettings.summitLog}
class="w-4 h-4 bg-input-background accent-primary border-input-border focus:ring-input-ring focus:ring-2"
/>
<label for="include-summit-log-checkbox" class="ms-2 text-sm"
>{$_("summit-book")}</label
>
</div>
</div>
<div slot="footer" class="flex items-center gap-4">
<button class="btn-secondary" on:click={closeModal}
>{$_("cancel")}</button
>
<button
class="btn-primary"
type="button"
on:click={exportTrail}
name="save">{$_("export")}</button
>
</div></Modal
>

View File

@@ -55,12 +55,15 @@
"entry": "Eintrag",
"error-creating-user": "Fehler beim Erstellen des Nutzers",
"error-during-login": "Fehler beim Login",
"error-exporting-trail": "Fehler beim Exportieren des Trails",
"error-printing-map": "Fehler beim Drucken der Karte",
"error-reading-file": "Fehler beim Lesen der Datei",
"est-duration": "Gesch. Dauer",
"explore": "Erkunden",
"explore-some-trails": "Erkunden Sie einige Routen",
"export": "Exportieren",
"features": "Features",
"file-format": "Dateiformat",
"french": "Französisch",
"german": "Deutsch",
"grid": "Gitter",

View File

@@ -55,12 +55,15 @@
"entry": "Entry",
"error-creating-user": "Error creating user",
"error-during-login": "Error during login",
"error-exporting-trail": "Error exporting trail",
"error-printing-map": "Error printing map",
"error-reading-file": "Error reading file",
"est-duration": "Est. duration",
"explore": "Explore",
"explore-some-trails": "Explore some trails",
"export": "Export",
"features": "Features",
"file-format": "File format",
"french": "French",
"german": "German",
"grid": "Grid",

View File

@@ -55,12 +55,15 @@
"entry": "Entrée",
"error-creating-user": "Erreur durant la création de l'utilisateur",
"error-during-login": "Erreur durant la connexion",
"error-exporting-trail": "",
"error-printing-map": "Erreur d'impression de la carte",
"error-reading-file": "Erreur de lecture du fichier",
"est-duration": "Temps estimé",
"explore": "Explorer",
"explore-some-trails": "Explorer certains itinéraires",
"export": "",
"features": "Fonctionnalités",
"file-format": "",
"french": "Français",
"german": "Allemand",
"grid": "Grille",

View File

@@ -55,12 +55,15 @@
"entry": "Bejegyzés",
"error-creating-user": "Hiba felhasználó hozzáadása közben",
"error-during-login": "Hiba bejelentkezés közben",
"error-exporting-trail": "",
"error-printing-map": "",
"error-reading-file": "Hiba a fájl olvasása közben",
"est-duration": "Becsült időtartam",
"explore": "Felfedezés",
"explore-some-trails": "Fedezzen fel néhány ösvényt",
"export": "",
"features": "Jellemzők",
"file-format": "",
"french": "Francia",
"german": "Német",
"grid": "",

View File

@@ -55,12 +55,15 @@
"entry": "Item",
"error-creating-user": "Het account kan niet worden aangemaakt",
"error-during-login": "Het inloggen is mislukt",
"error-exporting-trail": "",
"error-printing-map": "Fout bij afdrukken van kaart",
"error-reading-file": "Het bestand kan niet worden ingelezen",
"est-duration": "Geschatte duur",
"explore": "Verkennen",
"explore-some-trails": "Verken enkele wandelroutes",
"export": "",
"features": "Kenmerken",
"file-format": "",
"french": "Frans",
"german": "Duits",
"grid": "Rooster",

View File

@@ -55,12 +55,15 @@
"entry": "Pozycja",
"error-creating-user": "Błąd tworzenia użytkownika",
"error-during-login": "Błąd podczas logowania",
"error-exporting-trail": "",
"error-printing-map": "",
"error-reading-file": "Błąd wczytywania pliku",
"est-duration": "Szacowany czas",
"explore": "Eksploruj",
"explore-some-trails": "Eksploruj różne ścieżki",
"export": "",
"features": "Funkcje",
"file-format": "",
"french": "Francuski",
"german": "Niemiecki",
"grid": "",

View File

@@ -55,12 +55,15 @@
"entry": "Entrada",
"error-creating-user": "Erro ao criar usuário",
"error-during-login": "Erro durante o login",
"error-exporting-trail": "",
"error-printing-map": "",
"error-reading-file": "Erro ao ler o arquivo",
"est-duration": "Duração prevista",
"explore": "Explorar",
"explore-some-trails": "Explore algumas trilhas",
"export": "",
"features": "Características",
"file-format": "",
"french": "Francês",
"german": "Alemão",
"grid": "",

View File

@@ -55,12 +55,15 @@
"entry": "日程",
"error-creating-user": "创建用户错误",
"error-during-login": "登录错误",
"error-exporting-trail": "",
"error-printing-map": "",
"error-reading-file": "读取文件错误",
"est-duration": "预计时长",
"explore": "探索",
"explore-some-trails": "探索行程",
"export": "",
"features": "特性",
"file-format": "",
"french": "法语",
"german": "德语",
"grid": "",

View File

@@ -8,7 +8,7 @@ export default class Metadata {
desc: string;
time: Date;
keywords: string;
extensions: string;
extensions?: string;
author?: Person;
link?: Link[];
bounds?: Bounds;
@@ -18,7 +18,7 @@ export default class Metadata {
desc: string,
time: string,
keywords: string,
extensions: string,
extensions?: string,
author?: Person,
link?: Link | Link[],
bounds?: Bounds,

View File

@@ -61,7 +61,7 @@ class Trail {
this.id = params?.id;
this.name = name;
this.location = params?.location;
this.date = params?.date;
this.date = params?.date ?? new Date().toISOString().split('T')[0];
this.public = params?.public ?? false
this.distance = params?.distance;
this.elevation_gain = params?.elevation_gain;

View File

@@ -17,3 +17,14 @@ export function readAsDataURLAsync(file: File) {
fr.readAsDataURL(file);
});
}
export function saveAs(data: Blob, fileName: string) {
var a = document.createElement("a") as HTMLAnchorElement;
a.setAttribute("style", "display: none");
document.body.appendChild(a);
const url = window.URL.createObjectURL(data);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
};

View File

@@ -1,9 +1,11 @@
import GPX from "$lib/models/gpx/gpx";
import { Trail } from "$lib/models/trail";
import { Waypoint } from "$lib/models/waypoint";
import { currentUser } from "$lib/stores/user_store";
import GeoJsonToGpx from "$lib/vendor/geoJSONToGPX";
import { kml, tcx } from "$lib/vendor/toGeoJSON/toGeoJSON";
import cryptoRandomString from "crypto-random-string";
import { get } from "svelte/store";
export async function gpx2trail(gpxString: string) {
const gpx = await GPX.parse(gpxString);
@@ -18,7 +20,7 @@ export async function gpx2trail(gpxString: string) {
trail.description = gpx.metadata?.desc;
for (const wpt of gpx.wpt ?? []) {
for (const wpt of gpx.wpt ?? []) {
const wp = new Waypoint(wpt.$.lat ?? 0, wpt.$.lon ?? 0);
wp.id = cryptoRandomString({ length: 15 });
wp.name = wpt.name
@@ -29,13 +31,13 @@ export async function gpx2trail(gpxString: string) {
const totals = gpx.getTotals()
const trackPoints = gpx.trk?.at(0)?.trkseg?.at(0)?.trkpt
const routePoints = gpx.rte?.at(0)?.rtept;
const routePoints = gpx.rte?.at(0)?.rtept;
const startPoint = trackPoints?.at(0) ?? routePoints?.at(0);
const startPoint = trackPoints?.at(0) ?? routePoints?.at(0);
if (startPoint) {
trail.lat = startPoint.$.lat
trail.lon = startPoint.$.lon
}
}
const startTime = trackPoints?.at(0)?.time;
const endTime = trackPoints?.at((trackPoints?.length ?? 1) - 1)?.time
@@ -49,7 +51,44 @@ export async function gpx2trail(gpxString: string) {
trail.elevation_gain = totals.elevationGain;
trail.distance = totals.distance
return {gpx: gpx, trail: trail}
return { gpx: gpx, trail: trail }
}
export async function trail2gpx(trail: Trail) {
if (!trail.expand.gpx_data) {
throw Error("Trail has no GPX data")
}
const gpx = await GPX.parse(trail.expand.gpx_data) as GPX;
if (gpx instanceof Error) {
throw gpx;
}
gpx.metadata = {
name: trail.name,
desc: trail.description ?? "",
time: trail.date ? new Date(trail.date) : new Date(),
keywords: `${trail.category ?? ""}, ${trail.location ?? ""}`,
author: { name: trail.author ?? "", email: get(currentUser)?.email ?? "" }
}
if(!gpx.wpt) {
gpx.wpt = [];
}
for (const wp of trail.expand.waypoints) {
const gpxWpt = gpx.wpt.find((w) => w.$.lat == wp.lat && w.$.lon == wp.lon)
if(!gpxWpt) {
gpx.wpt.push({
$: {
lat: wp.lat,
lon: wp.lon
}
})
}
}
return gpx.toString();
}
export function fromKML(kmlData: string) {

View File

@@ -101,9 +101,9 @@
});
const modesOfTransport = [
{ text: $_('hiking'), value: "pedestrian" },
{ text: $_('cycling'), value: "bicycle" },
{ text: $_('driving'), value: "auto" },
{ text: $_("hiking"), value: "pedestrian" },
{ text: $_("cycling"), value: "bicycle" },
{ text: $_("driving"), value: "auto" },
];
let selectedModeOfTransport = modesOfTransport[0].value;
@@ -385,6 +385,11 @@
savedWaypoint.marker = marker;
}
function beforSummitLogModalOpen() {
summitLog.set(new SummitLog(new Date().toISOString().split('T')[0]));
openSummitLogModal();
}
function saveSummitLog(e: CustomEvent<SummitLog>) {
const savedSummitLog = e.detail;
let editedSummitLogIndex = $form.expand.summit_logs.findIndex(
@@ -791,7 +796,7 @@
<button
class="btn-secondary"
type="button"
on:click={openSummitLogModal}
on:click={beforSummitLogModalOpen}
><i class="fa fa-plus mr-2"></i>{$_("add-entry")}</button
>
{#if $lists.length}
@@ -846,7 +851,10 @@
>
<Toggle bind:value={autoRouting} label="Enable auto-routing"
></Toggle>
<Select items={modesOfTransport} bind:value={selectedModeOfTransport} disabled={!autoRouting}
<Select
items={modesOfTransport}
bind:value={selectedModeOfTransport}
disabled={!autoRouting}
></Select>
</div>
{/if}