adds profile page

This commit is contained in:
Christian Beutel
2024-11-03 15:13:25 +01:00
parent 0d8ff5c83b
commit bab22301af
31 changed files with 882 additions and 133 deletions

View File

@@ -28,12 +28,6 @@
{ text: $_("delete"), value: "delete" },
];
let totals: {
distance: number;
elevationGain: number;
duration: number;
} | null = null;
onMount(async () => {
if (!map) {
await initMap();
@@ -65,13 +59,6 @@
return;
}
const gpxObject = await GPX.parse(log.expand.gpx_data);
if (gpxObject instanceof Error) {
throw gpxObject;
}
totals = gpxObject.getTotals();
const geoJson = gpx(
new DOMParser().parseFromString(log.expand.gpx_data, "text/xml"),
);
@@ -79,7 +66,7 @@
filter: (feature: any, layer: any) => {
return feature.geometry.type !== "Point";
},
}).addTo(map)
}).addTo(map);
map.fitBounds(layer.getBounds());
map.invalidateSize();
}
@@ -88,7 +75,6 @@
if (layer) {
map?.removeLayer(layer);
}
totals = null;
}
</script>
@@ -117,25 +103,28 @@
<Dropdown items={dropdownItems} on:change></Dropdown>
{/if}
</div>
{#if totals}
{#if log.distance || log.elevation_gain || log.elevation_loss || log.duration}
<div
class="flex mt-1 gap-x-4 text-sm text-gray-500 flex-wrap mb-2"
>
<span
><i class="fa fa-left-right mr-2"></i>{formatDistance(
totals.distance,
)}</span
>
<span
><i class="fa fa-up-down mr-2"></i>{formatElevation(
totals.elevationGain,
log.distance,
)}</span
>
<span
><i class="fa fa-clock mr-2"></i>{formatTimeHHMM(
totals.duration / 1000 / 60,
log.duration,
)}</span
>
<span
><i class="fa fa-arrow-trend-up mr-2"
></i>{formatElevation(log.elevation_gain)}</span
>
<span
><i class="fa fa-arrow-trend-down mr-2"
></i>{formatElevation(log.elevation_loss)}</span
>
</div>
{/if}
<span class="whitespace-pre-wrap">{log.text}</span>

View File

@@ -11,6 +11,7 @@
import Modal from "../base/modal.svelte";
import Textarea from "../base/textarea.svelte";
import TrailPicker from "../trail/trail_picker.svelte";
import GPX from "$lib/models/gpx/gpx";
export let openModal: (() => void) | undefined = undefined;
export let closeModal: (() => void) | undefined = undefined;
@@ -26,7 +27,7 @@
initialValues: $summitLog,
validationSchema: summitLogSchema,
onSubmit: async (submittedValues) => {
if(!$form.expand.gpx_data) {
if (!$form.expand.gpx_data) {
$form.gpx = "";
}
dispatch("save", submittedValues);
@@ -38,6 +39,27 @@
$: if ($summitLog._gpx) {
$form._gpx = $summitLog._gpx;
}
async function handleTrailSelection(trailData: string | null) {
if (!trailData) {
$form.duration = undefined;
$form.elevation_gain = undefined;
$form.elevation_loss = undefined;
$form.distance = undefined;
return;
}
const gpxObject = await GPX.parse(trailData);
if (gpxObject instanceof Error) {
throw gpxObject;
}
const totals = gpxObject.getTotals();
$form.duration = totals.duration / 1000 / 60;
$form.elevation_gain = totals.elevationGain;
$form.elevation_loss = totals.elevationLoss;
$form.distance = totals.distance;
}
</script>
<Modal
@@ -68,6 +90,7 @@
bind:trailFile={$form._gpx}
bind:trailData={$form.expand.gpx_data}
label={$_("trail", { values: { n: 1 } })}
on:change={(e) => handleTrailSelection(e.detail)}
></TrailPicker>
<div class="basis-full">
<Textarea

View File

@@ -8,28 +8,30 @@
import type { Map } from "leaflet";
import { gpx } from "$lib/vendor/toGeoJSON/toGeoJSON";
import { endIcon, startIcon } from "$lib/util/leaflet_util";
import { _ } from "svelte-i18n";
import MapWithElevation from "../trail/map_with_elevation.svelte";
import type { Trail } from "$lib/models/trail";
import { gpx2trail } from "$lib/util/gpx_util";
export let summitLogs: SummitLog[];
export let showCategory: boolean = false;
let openModal: () => void;
let closeModal: () => void;
let map: Map;
let L: any;
let layerGroup: any;
let trail: Trail | null = null;
onMount(async () => {
L = (await import("leaflet")).default;
map = L.map("summit-log-table-map");
map.attributionControl.setPrefix(false);
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: "© OpenStreetMap contributors",
}).addTo(map);
layerGroup = L.layerGroup();
layerGroup.addTo(map);
// L = (await import("leaflet")).default;
// map = L.map("summit-log-table-map");
// map.attributionControl.setPrefix(false);
// L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
// attribution: "© OpenStreetMap contributors",
// }).addTo(map);
// layerGroup = L.layerGroup();
// layerGroup.addTo(map);
});
async function openMap(log: SummitLog) {
@@ -37,61 +39,14 @@
return;
}
layerGroup.clearLayers();
const geoJson = gpx(
new DOMParser().parseFromString(log.expand.gpx_data, "text/xml"),
);
const layer = L.geoJson(geoJson, {
onEachFeature: (feature: any, layer: any) => {
let startCoords, endCoords;
if (geoJson.features && geoJson.features.length > 0) {
const geometry = geoJson.features[0].geometry;
if (geometry.type === "LineString") {
startCoords = geometry.coordinates[0];
endCoords =
geometry.coordinates[
geometry.coordinates.length - 1
];
} else if (geometry.type === "MultiLineString") {
startCoords = (geometry as any).coordinates[0][0];
endCoords = (geometry as any).coordinates[
geometry.coordinates.length - 1
][
(geometry as any).coordinates[
geometry.coordinates.length - 1
].length - 1
];
}
}
if (startCoords && endCoords) {
const startMarker = L.marker(
[startCoords[1], startCoords[0]],
{
icon: startIcon(),
},
);
const endMarker = L.marker([endCoords[1], endCoords[0]], {
icon: endIcon(),
});
layerGroup.addLayer(startMarker);
layerGroup.addLayer(endMarker);
}
},
filter: (feature: any, layer: any) => {
return feature.geometry.type !== "Point";
},
}).addTo(map);
layerGroup.addLayer(layer);
trail = (await gpx2trail(log.expand.gpx_data)).trail;
trail.expand.gpx_data = log.expand.gpx_data;
openModal();
await tick();
map.fitBounds(layer.getBounds());
map.invalidateSize();
return;
}
</script>
@@ -99,23 +54,41 @@
<thead>
<tr class="text-sm">
<th class="w-24"></th>
<th>Date</th>
<th>Distance</th>
<th class="whitespace-nowrap">Elevation gain</th>
<th>Duration</th>
<th>{$_("date")}</th>
<th>{$_("distance")}</th>
<th>{$_("elevation-gain")}</th>
<th>{$_("elevation-loss")}</th>
<th>{$_("duration")}</th>
{#if showCategory}
<th>
{$_("category")}
</th>
{/if}
<th></th>
</tr>
</thead>
<tbody>
{#each summitLogs as log, i}
<SummitLogTableRow index={i} {log} on:open={() => openMap(log)}
<SummitLogTableRow
index={i}
{log}
on:open={() => openMap(log)}
{showCategory}
></SummitLogTableRow>
{/each}
</tbody>
</table>
<Modal id="summit-log-table-modal" title="" bind:openModal bind:closeModal>
<div slot="content" id="summit-log-table-map" class="h-96"></div>
<Modal
id="summit-log-table-modal"
size="max-w-4xl"
title=""
bind:openModal
bind:closeModal
>
<div slot="content" id="summit-log-table-map" class="h-[32rem]">
<MapWithElevation {trail} bind:map></MapWithElevation>
</div>
</Modal>
<style>

View File

@@ -2,7 +2,6 @@
import type { SummitLog } from "$lib/models/summit_log";
import "leaflet/dist/leaflet.css";
import GPX from "$lib/models/gpx/gpx";
import {
formatDistance,
formatElevation,
@@ -11,15 +10,11 @@
import { gpx } from "$lib/vendor/toGeoJSON/toGeoJSON";
import type { Map } from "leaflet";
import { createEventDispatcher, onMount } from "svelte";
import { _ } from "svelte-i18n";
export let index: number;
export let log: SummitLog;
let totals: {
distance: number;
elevationGain: number;
duration: number;
} | null = null;
export let showCategory: boolean = false;
let map: Map;
@@ -47,13 +42,6 @@
return;
}
const gpxObject = await GPX.parse(log.expand.gpx_data);
if (gpxObject instanceof Error) {
throw gpxObject;
}
totals = gpxObject.getTotals();
const geoJson = gpx(
new DOMParser().parseFromString(log.expand.gpx_data, "text/xml"),
);
@@ -78,12 +66,12 @@
<button
type="button"
on:click={openMap}
class="h-20 aspect-square shrink-0 rounded-xl !bg-background"
class="h-20 aspect-square shrink-0 rounded-xl !bg-background hover:!bg-secondary-hover transition-colors"
class:hidden={!log.expand.gpx_data}
id="mini-map-{index}"
></button>
</td>
<td class:py-4={!log.text && !log.expand.gpx_data}
<td class:py-4={!log.expand.gpx_data}
>{new Date(log.date).toLocaleDateString(undefined, {
month: "2-digit",
day: "2-digit",
@@ -92,18 +80,30 @@
})}</td
>
<td>
{formatDistance(totals?.distance)}
{formatDistance(log.distance)}
</td>
<td>
{formatElevation(totals?.elevationGain)}
</td><td>
{formatTimeHHMM(totals ? (totals?.duration / 1000 / 60) : undefined)}
{formatElevation(log.elevation_gain)}
</td>
<td>
{formatElevation(log.elevation_loss)}
</td>
<td>
{formatTimeHHMM(log.duration)}
</td>
{#if showCategory}
<td>
{$_(
log.expand.trails_via_summit_logs?.at(0)?.expand.category
?.name ?? "-",
)}
</td>
{/if}
<td>
{#if log.text}
<button on:click={() => (showText = !showText)} class="btn-icon"
><i class="fa{showText ? '' : '-regular'} fa-message"
><i class="fa{showText ? '' : '-regular'} fa-message text-gray-500"
></i></button
>
{/if}
@@ -111,11 +111,12 @@
</tr>
{#if showText}
<tr
><td class="text-left text-sm whitespace-pre-wrap pb-4" colspan="6"
>{log.text}</td
><td
class="text-left text-sm whitespace-pre-wrap pb-4"
colspan={showCategory ? 8 : 7}>{log.text}</td
></tr
>
{/if}
<tr>
<td colspan="6"> <hr /> </td>
<td colspan={showCategory ? 8 : 7}> <hr class="border-input-border" /> </td>
</tr>