Adds option to mark trail as completed (#920)

* initial commit

* add completed for komoot tours

* mark hammerhead activities as completed

* fix review suggestions

* adds completed modal to trail edit page

---------

Co-authored-by: Christian Beutel <>
Co-authored-by: slothful-vassal <89943360+slothful-vassal@users.noreply.github.com>
This commit is contained in:
Flomp
2026-04-27 20:37:11 +02:00
committed by GitHub
parent 7e84e39cda
commit 7bdf0d3151
25 changed files with 244 additions and 18 deletions

View File

@@ -543,6 +543,7 @@ func createTrailFromActivity(app core.App, detailedTour *HammerheadActivity, gpx
"id": trailid, "id": trailid,
"name": detailedTour.ActivityData.Name, "name": detailedTour.ActivityData.Name,
"public": false, "public": false,
"completed": true,
"distance": detailedTour.ActivityData.ActivityInfo[idDistance].Value.Value, "distance": detailedTour.ActivityData.ActivityInfo[idDistance].Value.Value,
"elevation_gain": detailedTour.ActivityData.ActivityInfo[idElevationGain].Value.Value, "elevation_gain": detailedTour.ActivityData.ActivityInfo[idElevationGain].Value.Value,
"elevation_loss": detailedTour.ActivityData.ActivityInfo[idElevationLoss].Value.Value, "elevation_loss": detailedTour.ActivityData.ActivityInfo[idElevationLoss].Value.Value,

View File

@@ -292,6 +292,7 @@ func createTrailFromTour(app core.App, k *KomootApi, detailedTour *DetailedKomoo
"id": trailid, "id": trailid,
"name": detailedTour.Name, "name": detailedTour.Name,
"public": public, "public": public,
"completed": detailedTour.Type == "tour_recorded",
"distance": detailedTour.Distance, "distance": detailedTour.Distance,
"elevation_gain": detailedTour.ElevationUp, "elevation_gain": detailedTour.ElevationUp,
"elevation_loss": detailedTour.ElevationDown, "elevation_loss": detailedTour.ElevationDown,

View File

@@ -0,0 +1,62 @@
package migrations
import (
"github.com/pocketbase/pocketbase/core"
m "github.com/pocketbase/pocketbase/migrations"
)
func init() {
m.Register(func(app core.App) error {
collection, err := app.FindCollectionByNameOrId("e864strfxo14pm4")
if err != nil {
return err
}
// add field
if err := collection.Fields.AddMarshaledJSONAt(5, []byte(`{
"hidden": false,
"id": "bool989355118",
"name": "completed",
"presentable": false,
"required": false,
"system": false,
"type": "bool"
}`)); err != nil {
return err
}
err = app.Save(collection)
if err != nil {
return err
}
logs, err := app.FindAllRecords("summit_logs")
if err != nil {
return err
}
for _, l := range logs {
trail, err := app.FindRecordById("trails", l.GetString("trail"))
if err != nil {
continue
}
trail.Set("completed", true)
err = app.Save(trail)
if err != nil {
return err
}
}
return nil
}, func(app core.App) error {
collection, err := app.FindCollectionByNameOrId("e864strfxo14pm4")
if err != nil {
return err
}
// remove field
collection.Fields.RemoveById("bool989355118")
return app.Save(collection)
})
}

View File

@@ -12,7 +12,6 @@ import (
"time" "time"
"github.com/meilisearch/meilisearch-go" "github.com/meilisearch/meilisearch-go"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/core"
"github.com/tkrajina/gpxgo/gpx" "github.com/tkrajina/gpxgo/gpx"
"github.com/twpayne/go-polyline" "github.com/twpayne/go-polyline"
@@ -52,11 +51,6 @@ func documentFromTrailRecord(app core.App, r *core.Record, author *core.Record,
domain = author.GetString("domain") domain = author.GetString("domain")
} }
logCount, err := app.CountRecords("summit_logs", dbx.NewExp("trail={:id}", dbx.Params{"id": r.Id}))
if err != nil {
return nil, err
}
document := map[string]any{ document := map[string]any{
"id": r.Id, "id": r.Id,
"author": author.Id, "author": author.Id,
@@ -71,7 +65,7 @@ func documentFromTrailRecord(app core.App, r *core.Record, author *core.Record,
"duration": r.GetFloat("duration"), "duration": r.GetFloat("duration"),
"difficulty": difficultyToNumber(r.GetString("difficulty")), "difficulty": difficultyToNumber(r.GetString("difficulty")),
"category": category, "category": category,
"completed": logCount > 0, "completed": r.GetBool("completed"),
"date": r.GetDateTime("date").Time().Unix(), "date": r.GetDateTime("date").Time().Unix(),
"created": r.GetDateTime("created").Time().Unix(), "created": r.GetDateTime("created").Time().Unix(),
"public": r.GetBool("public"), "public": r.GetBool("public"),

View File

@@ -150,7 +150,9 @@
{/if} {/if}
<div class="p-4"> <div class="p-4">
<div> <div>
<h4 class="font-semibold text-lg line-clamp-2 wrap-anywhere">{trail.name}</h4> <h4 class="font-semibold text-lg line-clamp-2 wrap-anywhere">
{trail.name}
</h4>
{#if trail.date} {#if trail.date}
<p class="text-xs text-gray-500 mb-3"> <p class="text-xs text-gray-500 mb-3">
{new Date(trail.date).toLocaleDateString(undefined, { {new Date(trail.date).toLocaleDateString(undefined, {
@@ -200,7 +202,11 @@
<div class="flex gap-x-4 gap-y-1 text-base flex-wrap"> <div class="flex gap-x-4 gap-y-1 text-base flex-wrap">
{#if trail.expand?.category?.name || trail.category} {#if trail.expand?.category?.name || trail.category}
<p> <p>
<i class="fa fa-shapes mr-3"> </i>{$_(trail.expand?.category?.name ?? trail.category ?? "-")} <i class="fa fa-shapes mr-3"> </i>{$_(
trail.expand?.category?.name ??
trail.category ??
"-",
)}
</p> </p>
{/if} {/if}
{#if trail.location} {#if trail.location}

View File

@@ -56,6 +56,7 @@
import { handleFromRecordWithIRI } from "$lib/util/activitypub_util"; import { handleFromRecordWithIRI } from "$lib/util/activitypub_util";
import LikeButton from "./like_button.svelte"; import LikeButton from "./like_button.svelte";
import Editor from "../base/editor.svelte"; import Editor from "../base/editor.svelte";
import { trails_update } from "$lib/stores/trail_store";
interface Props { interface Props {
initTrail: Trail; initTrail: Trail;
@@ -75,6 +76,7 @@
let summitLogModal: SummitLogModal; let summitLogModal: SummitLogModal;
let confirmModal: ConfirmModal; let confirmModal: ConfirmModal;
let markTrailAsCompletedModal: ConfirmModal;
let trail = $state(untrack(() => initTrail)); let trail = $state(untrack(() => initTrail));
@@ -243,6 +245,13 @@
log.trail = trail.id; log.trail = trail.id;
const newLog = await summit_logs_create(log); const newLog = await summit_logs_create(log);
summitLogs.set([...$summitLogs, newLog]); summitLogs.set([...$summitLogs, newLog]);
if (
$summitLogs.length == 1 &&
trail.author == $currentUser?.actor &&
!trail.completed
) {
markTrailAsCompletedModal.openModal();
}
} }
summitLogCreateLoading = false; summitLogCreateLoading = false;
} }
@@ -259,6 +268,12 @@
); );
summitLogs.set(newSummitLogList); summitLogs.set(newSummitLogList);
} }
async function markTrailAsCompleted() {
trail.completed = true;
const updatedTrail: Trail = { ...trail };
await trails_update(trail, updatedTrail);
}
</script> </script>
<div <div
@@ -280,7 +295,7 @@
</button> </button>
{/if} {/if}
<div <div
class="grid gap-[1px] {headerPhotos.length > 1 class="grid gap-px {headerPhotos.length > 1
? 'grid-cols-[8fr_5fr]' ? 'grid-cols-[8fr_5fr]'
: 'grid-cols-1'} h-80 rounded-t-3xl overflow-hidden cursor-pointer" : 'grid-cols-1'} h-80 rounded-t-3xl overflow-hidden cursor-pointer"
> >
@@ -391,10 +406,20 @@
{trail.location} {trail.location}
</h3> </h3>
{/if} {/if}
<h3 class="text-lg"> <h3>
<i class="fa fa-gauge mr-2"></i> <i class="fa fa-gauge mr-2"></i>
{$_(trail.difficulty ?? "?")} {$_(trail.difficulty ?? "?")}
</h3> </h3>
<h3>
<i
class="fa {trail.completed
? 'fa-flag-checkered'
: 'fa-compass-drafting'} mr-2"
></i>
{$_(
trail.completed ? "completed" : "not-completed",
)}
</h3>
</div> </div>
</div> </div>
<div class="flex flex-col items-center gap-y-2"> <div class="flex flex-col items-center gap-y-2">
@@ -675,6 +700,16 @@
<SummitLogModal bind:this={summitLogModal} onsave={(log) => saveSummitLog(log)} <SummitLogModal bind:this={summitLogModal} onsave={(log) => saveSummitLog(log)}
></SummitLogModal> ></SummitLogModal>
<ConfirmModal
id="mark-trail-as-completed-modal"
title={$_("mark-trail-as-completed")}
text={$_("mark-trail-as-completed-modal-text")}
action={$_("yes")}
deny={$_("no")}
bind:this={markTrailAsCompletedModal}
onconfirm={markTrailAsCompleted}
></ConfirmModal>
<ConfirmModal <ConfirmModal
id="confirm-summit-log-delete-modal" id="confirm-summit-log-delete-modal"
text={$_("delete-summit-log-confirm")} text={$_("delete-summit-log-confirm")}

View File

@@ -133,7 +133,8 @@
{#if trail.tags.length && trail.expand?.tags} {#if trail.tags.length && trail.expand?.tags}
<div class="flex flex-wrap gap-1 mb-3 items-center"> <div class="flex flex-wrap gap-1 mb-3 items-center">
{#each expandedTags ? trail.expand.tags : trail.expand.tags.slice(0, 2) as tag} {#each expandedTags ? trail.expand.tags : trail.expand.tags.slice(0, 2) as tag}
<Chip text={tag.name} closable={false} primary={false}></Chip> <Chip text={tag.name} closable={false} primary={false}
></Chip>
{/each} {/each}
{#if trail.expand.tags.length > 2} {#if trail.expand.tags.length > 2}
@@ -154,7 +155,9 @@
<div class="flex flex-wrap gap-x-8 gap-y-1"> <div class="flex flex-wrap gap-x-8 gap-y-1">
{#if trail.expand?.category?.name || trail.category} {#if trail.expand?.category?.name || trail.category}
<p> <p>
<i class="fa fa-shapes mr-3"> </i>{$_(trail.expand?.category?.name ?? trail.category ?? "-")} <i class="fa fa-shapes mr-3"> </i>{$_(
trail.expand?.category?.name ?? trail.category ?? "-",
)}
</p> </p>
{/if} {/if}
{#if trail.location} {#if trail.location}
@@ -165,6 +168,14 @@
<p class="whitespace-nowrap"> <p class="whitespace-nowrap">
<i class="fa fa-gauge mr-3"></i>{$_(trail.difficulty ?? "?")} <i class="fa fa-gauge mr-3"></i>{$_(trail.difficulty ?? "?")}
</p> </p>
<p class="whitespace-nowrap">
<i
class="fa {trail.completed
? 'fa-flag-checkered'
: 'fa-compass-drafting'} mr-2"
></i>
{$_(trail.completed ? "completed" : "not-completed")}
</p>
</div> </div>
<div class="flex flex-wrap mt-1 gap-x-4 gap-y-2 text-sm text-gray-500"> <div class="flex flex-wrap mt-1 gap-x-4 gap-y-2 text-sm text-gray-500">

View File

@@ -3,6 +3,7 @@
"Canoeing": "Kanoistika", "Canoeing": "Kanoistika",
"Climbing": "Horolezectví", "Climbing": "Horolezectví",
"Hiking": "Turistika", "Hiking": "Turistika",
"Skiing": "",
"Walking": "Chůze", "Walking": "Chůze",
"about": "O aplikaci", "about": "O aplikaci",
"account-delete-confirm": "Chystáte se odstranit svůj účet. Všechny vaše záznamy budou odstraněny též. Chcete pokračovat?", "account-delete-confirm": "Chystáte se odstranit svůj účet. Všechny vaše záznamy budou odstraněny též. Chcete pokračovat?",
@@ -264,6 +265,8 @@
"make-thumbnail": "Vytvořit náhled", "make-thumbnail": "Vytvořit náhled",
"map": "Mapa", "map": "Mapa",
"map-style": "Styl mapy", "map-style": "Styl mapy",
"mark-trail-as-completed": "",
"mark-trail-as-completed-modal-text": "",
"max-hiking-difficulty": "Max. obtížnost túry", "max-hiking-difficulty": "Max. obtížnost túry",
"metric": "Metrický", "metric": "Metrický",
"moderate": "Středně náročné", "moderate": "Středně náročné",
@@ -291,6 +294,7 @@
"new-password-text": "Nastavit nové heslo", "new-password-text": "Nastavit nové heslo",
"new-token-generated": "", "new-token-generated": "",
"new-trail": "Nová trasa", "new-trail": "Nová trasa",
"no": "",
"no-account": "Ještě nemáte účet?", "no-account": "Ještě nemáte účet?",
"no-api-tokens": "", "no-api-tokens": "",
"no-comments-so-far": "Zatím žádné komentáře", "no-comments-so-far": "Zatím žádné komentáře",
@@ -467,5 +471,6 @@
"welcome_to": "Vítejte v", "welcome_to": "Vítejte v",
"width": "Šířka", "width": "Šířka",
"wrong-username-or-password": "Nesprávné uživatelské jméno nebo heslo", "wrong-username-or-password": "Nesprávné uživatelské jméno nebo heslo",
"yes": "",
"you-can": "Můžete" "you-can": "Můžete"
} }

View File

@@ -3,6 +3,7 @@
"Canoeing": "Kanufahren", "Canoeing": "Kanufahren",
"Climbing": "Klettern", "Climbing": "Klettern",
"Hiking": "Wandern", "Hiking": "Wandern",
"Skiing": "Skifahren",
"Walking": "Laufen", "Walking": "Laufen",
"about": "Über", "about": "Über",
"account-delete-confirm": "Du bist dabei, dein Konto zu löschen. Alle deine Routen werden ebenfalls gelöscht. Möchtest du fortfahren?", "account-delete-confirm": "Du bist dabei, dein Konto zu löschen. Alle deine Routen werden ebenfalls gelöscht. Möchtest du fortfahren?",
@@ -264,6 +265,8 @@
"make-thumbnail": "Thumbnail festlegen", "make-thumbnail": "Thumbnail festlegen",
"map": "Karte", "map": "Karte",
"map-style": "Kartenstil", "map-style": "Kartenstil",
"mark-trail-as-completed": "Route als abgeschlossen markieren",
"mark-trail-as-completed-modal-text": "Möchtest du diese Route als abgeschlossen markieren? Du kannst diesen Status jederzeit wieder ändern.",
"max-hiking-difficulty": "Max. Schwierigkeit der Wanderung", "max-hiking-difficulty": "Max. Schwierigkeit der Wanderung",
"metric": "Metrisch", "metric": "Metrisch",
"moderate": "Mittel", "moderate": "Mittel",
@@ -291,6 +294,7 @@
"new-password-text": "Wähle ein neues Passwort", "new-password-text": "Wähle ein neues Passwort",
"new-token-generated": "", "new-token-generated": "",
"new-trail": "Neue Route", "new-trail": "Neue Route",
"no": "Nein",
"no-account": "Du hast noch kein Konto?", "no-account": "Du hast noch kein Konto?",
"no-api-tokens": "", "no-api-tokens": "",
"no-comments-so-far": "Bisher keine Kommentare", "no-comments-so-far": "Bisher keine Kommentare",
@@ -467,5 +471,6 @@
"welcome_to": "Willkommen bei", "welcome_to": "Willkommen bei",
"width": "Breite", "width": "Breite",
"wrong-username-or-password": "Falscher Nutzername oder falsches Passwort", "wrong-username-or-password": "Falscher Nutzername oder falsches Passwort",
"yes": "Ja",
"you-can": "Du kannst" "you-can": "Du kannst"
} }

View File

@@ -3,6 +3,7 @@
"Canoeing": "Canoeing", "Canoeing": "Canoeing",
"Climbing": "Climbing", "Climbing": "Climbing",
"Hiking": "Hiking", "Hiking": "Hiking",
"Skiing": "",
"Walking": "Walking", "Walking": "Walking",
"about": "About", "about": "About",
"account-delete-confirm": "You are about to delete your account. All your trails will also be deleted. Do you want to proceed?", "account-delete-confirm": "You are about to delete your account. All your trails will also be deleted. Do you want to proceed?",
@@ -21,6 +22,7 @@
"already-account": "Already have an account?", "already-account": "Already have an account?",
"altitude": "Altitude", "altitude": "Altitude",
"amenity": "Amenity", "amenity": "Amenity",
"ammenity": "",
"api-documentation": "API Documentation", "api-documentation": "API Documentation",
"api-tokens": "API Tokens", "api-tokens": "API Tokens",
"api-tokens-hint": "API Tokens can be used to grant 3rd party applications access to your wanderer account.", "api-tokens-hint": "API Tokens can be used to grant 3rd party applications access to your wanderer account.",
@@ -263,6 +265,8 @@
"make-thumbnail": "Make thumbnail", "make-thumbnail": "Make thumbnail",
"map": "Map", "map": "Map",
"map-style": "Map style", "map-style": "Map style",
"mark-trail-as-completed": "Mark trail as completed",
"mark-trail-as-completed-modal-text": "Would you like to mark this trail as completed? You can change this status again at any time.",
"max-hiking-difficulty": "Max. Hiking Difficulty", "max-hiking-difficulty": "Max. Hiking Difficulty",
"metric": "Metric", "metric": "Metric",
"moderate": "Moderate", "moderate": "Moderate",
@@ -290,6 +294,7 @@
"new-password-text": "Set a new password", "new-password-text": "Set a new password",
"new-token-generated": "New API Token generated", "new-token-generated": "New API Token generated",
"new-trail": "New Trail", "new-trail": "New Trail",
"no": "No",
"no-account": "Don't have an account?", "no-account": "Don't have an account?",
"no-api-tokens": "You have no API Tokens", "no-api-tokens": "You have no API Tokens",
"no-comments-so-far": "No comments so far", "no-comments-so-far": "No comments so far",
@@ -466,5 +471,6 @@
"welcome_to": "Welcome to", "welcome_to": "Welcome to",
"width": "Width", "width": "Width",
"wrong-username-or-password": "Wrong username or password", "wrong-username-or-password": "Wrong username or password",
"yes": "Yes",
"you-can": "You can" "you-can": "You can"
} }

View File

@@ -3,6 +3,7 @@
"Canoeing": "Remo", "Canoeing": "Remo",
"Climbing": "Escalada", "Climbing": "Escalada",
"Hiking": "Senderismo", "Hiking": "Senderismo",
"Skiing": "",
"Walking": "Paseo", "Walking": "Paseo",
"about": "Sobre", "about": "Sobre",
"account-delete-confirm": "Estás al punto de borrar tu cuenta. Todas tus rutas también serán borradas. ¿Quieres proceder?", "account-delete-confirm": "Estás al punto de borrar tu cuenta. Todas tus rutas también serán borradas. ¿Quieres proceder?",
@@ -264,6 +265,8 @@
"make-thumbnail": "Generar miniaturas", "make-thumbnail": "Generar miniaturas",
"map": "Mapa", "map": "Mapa",
"map-style": "Estilo de mapa", "map-style": "Estilo de mapa",
"mark-trail-as-completed": "",
"mark-trail-as-completed-modal-text": "",
"max-hiking-difficulty": "Dificultad máxima", "max-hiking-difficulty": "Dificultad máxima",
"metric": "Métrica", "metric": "Métrica",
"moderate": "Medio", "moderate": "Medio",
@@ -291,6 +294,7 @@
"new-password-text": "Configura una nueva contraseña", "new-password-text": "Configura una nueva contraseña",
"new-token-generated": "", "new-token-generated": "",
"new-trail": "Nueva Ruta", "new-trail": "Nueva Ruta",
"no": "",
"no-account": "¿No tienes una cuenta?", "no-account": "¿No tienes una cuenta?",
"no-api-tokens": "", "no-api-tokens": "",
"no-comments-so-far": "Ningún comentario todavía", "no-comments-so-far": "Ningún comentario todavía",
@@ -467,5 +471,6 @@
"welcome_to": "Bienvenid@ a", "welcome_to": "Bienvenid@ a",
"width": "Anchura", "width": "Anchura",
"wrong-username-or-password": "Usuario o contraseña no correctas", "wrong-username-or-password": "Usuario o contraseña no correctas",
"yes": "",
"you-can": "Puedes" "you-can": "Puedes"
} }

View File

@@ -3,6 +3,7 @@
"Canoeing": "Kanoa", "Canoeing": "Kanoa",
"Climbing": "Eskalada", "Climbing": "Eskalada",
"Hiking": "Mendi-ibilaldia", "Hiking": "Mendi-ibilaldia",
"Skiing": "",
"Walking": "Oinez", "Walking": "Oinez",
"about": "Honi buruz", "about": "Honi buruz",
"account-delete-confirm": "Zure kontua ezabatzera zoaz. Zure ibilbide guztiak ere ezabatu egingo dira. Jarraitu nahi duzu?", "account-delete-confirm": "Zure kontua ezabatzera zoaz. Zure ibilbide guztiak ere ezabatu egingo dira. Jarraitu nahi duzu?",
@@ -21,6 +22,7 @@
"already-account": "Baduzu kontua lehendik?", "already-account": "Baduzu kontua lehendik?",
"altitude": "Altuera", "altitude": "Altuera",
"amenity": "Altimetria", "amenity": "Altimetria",
"ammenity": "",
"api-documentation": "API dokumentazioa", "api-documentation": "API dokumentazioa",
"api-tokens": "", "api-tokens": "",
"api-tokens-hint": "", "api-tokens-hint": "",
@@ -263,6 +265,8 @@
"make-thumbnail": "Egin iruditxoa", "make-thumbnail": "Egin iruditxoa",
"map": "Mapa", "map": "Mapa",
"map-style": "Maparen estiloa", "map-style": "Maparen estiloa",
"mark-trail-as-completed": "",
"mark-trail-as-completed-modal-text": "",
"max-hiking-difficulty": "Mendi ibilaldiaren gehienezko zailtasuna", "max-hiking-difficulty": "Mendi ibilaldiaren gehienezko zailtasuna",
"metric": "Metrikoa", "metric": "Metrikoa",
"moderate": "Erdi-bidekoa", "moderate": "Erdi-bidekoa",
@@ -290,6 +294,7 @@
"new-password-text": "Pasahitz berria sortu", "new-password-text": "Pasahitz berria sortu",
"new-token-generated": "", "new-token-generated": "",
"new-trail": "Ibilaldi berria", "new-trail": "Ibilaldi berria",
"no": "",
"no-account": "Ez duzu konturik?", "no-account": "Ez duzu konturik?",
"no-api-tokens": "", "no-api-tokens": "",
"no-comments-so-far": "Ez dago iruzkinik", "no-comments-so-far": "Ez dago iruzkinik",
@@ -466,5 +471,6 @@
"welcome_to": "Ongi etorri", "welcome_to": "Ongi etorri",
"width": "Zabalera", "width": "Zabalera",
"wrong-username-or-password": "Erabiltzaile izena edo pasahitza okerrak dira", "wrong-username-or-password": "Erabiltzaile izena edo pasahitza okerrak dira",
"yes": "",
"you-can": "Hau egin dezakezu" "you-can": "Hau egin dezakezu"
} }

View File

@@ -3,6 +3,7 @@
"Canoeing": "Canoë", "Canoeing": "Canoë",
"Climbing": "Escalade", "Climbing": "Escalade",
"Hiking": "Randonnée", "Hiking": "Randonnée",
"Skiing": "",
"Walking": "Marche", "Walking": "Marche",
"about": "Informations", "about": "Informations",
"account-delete-confirm": "Vous êtes sur le point de supprimer votre compte. Tous vos itinéraires seront également supprimés. Voulez-vous continuer ?", "account-delete-confirm": "Vous êtes sur le point de supprimer votre compte. Tous vos itinéraires seront également supprimés. Voulez-vous continuer ?",
@@ -264,6 +265,8 @@
"make-thumbnail": "Créer une miniature", "make-thumbnail": "Créer une miniature",
"map": "Carte", "map": "Carte",
"map-style": "Style de carte", "map-style": "Style de carte",
"mark-trail-as-completed": "",
"mark-trail-as-completed-modal-text": "",
"max-hiking-difficulty": "Difficulté maximale de marche", "max-hiking-difficulty": "Difficulté maximale de marche",
"metric": "Métrique", "metric": "Métrique",
"moderate": "Moyenne", "moderate": "Moyenne",
@@ -291,6 +294,7 @@
"new-password-text": "Définir un nouveau mot de passe", "new-password-text": "Définir un nouveau mot de passe",
"new-token-generated": "", "new-token-generated": "",
"new-trail": "Nouvel itinéraire", "new-trail": "Nouvel itinéraire",
"no": "",
"no-account": "Pas encore de compte ?", "no-account": "Pas encore de compte ?",
"no-api-tokens": "", "no-api-tokens": "",
"no-comments-so-far": "Aucun commentaire pour l'instant", "no-comments-so-far": "Aucun commentaire pour l'instant",
@@ -467,5 +471,6 @@
"welcome_to": "Bienvenue sur", "welcome_to": "Bienvenue sur",
"width": "Largeur", "width": "Largeur",
"wrong-username-or-password": "Nom d'utilisateur ou mot de passe incorrect", "wrong-username-or-password": "Nom d'utilisateur ou mot de passe incorrect",
"yes": "",
"you-can": "Vous pouvez" "you-can": "Vous pouvez"
} }

View File

@@ -3,6 +3,7 @@
"Canoeing": "Canoeing", "Canoeing": "Canoeing",
"Climbing": "Climbing", "Climbing": "Climbing",
"Hiking": "Hiking", "Hiking": "Hiking",
"Skiing": "",
"Walking": "Walking", "Walking": "Walking",
"about": "A programról", "about": "A programról",
"account-delete-confirm": "Ön most a profilját készül törölni. Minden nyomvonala törlődik. Szeretné folytatni?", "account-delete-confirm": "Ön most a profilját készül törölni. Minden nyomvonala törlődik. Szeretné folytatni?",
@@ -21,6 +22,7 @@
"already-account": "Már rendelkezik fiókkal?", "already-account": "Már rendelkezik fiókkal?",
"altitude": "Magasság", "altitude": "Magasság",
"amenity": "Amenity", "amenity": "Amenity",
"ammenity": "",
"api-documentation": "API Dokumentáció", "api-documentation": "API Dokumentáció",
"api-tokens": "", "api-tokens": "",
"api-tokens-hint": "", "api-tokens-hint": "",
@@ -263,6 +265,8 @@
"make-thumbnail": "Készítsen miniatűrképet", "make-thumbnail": "Készítsen miniatűrképet",
"map": "Térkép", "map": "Térkép",
"map-style": "Map style", "map-style": "Map style",
"mark-trail-as-completed": "",
"mark-trail-as-completed-modal-text": "",
"max-hiking-difficulty": "Max. Hiking Difficulty", "max-hiking-difficulty": "Max. Hiking Difficulty",
"metric": "Metrikus", "metric": "Metrikus",
"moderate": "Mérsékelt", "moderate": "Mérsékelt",
@@ -290,6 +294,7 @@
"new-password-text": "Set a new password", "new-password-text": "Set a new password",
"new-token-generated": "", "new-token-generated": "",
"new-trail": "Új útvonal", "new-trail": "Új útvonal",
"no": "",
"no-account": "Nincs még fiókja?", "no-account": "Nincs még fiókja?",
"no-api-tokens": "", "no-api-tokens": "",
"no-comments-so-far": "No comments so far", "no-comments-so-far": "No comments so far",
@@ -466,5 +471,6 @@
"welcome_to": "Üdvözöljük a", "welcome_to": "Üdvözöljük a",
"width": "Width", "width": "Width",
"wrong-username-or-password": "Helytelen felhasználónév vagy jelszó", "wrong-username-or-password": "Helytelen felhasználónév vagy jelszó",
"yes": "",
"you-can": "You can" "you-can": "You can"
} }

View File

@@ -3,6 +3,7 @@
"Canoeing": "Canoa", "Canoeing": "Canoa",
"Climbing": "Arrampicata", "Climbing": "Arrampicata",
"Hiking": "Escursionismo", "Hiking": "Escursionismo",
"Skiing": "",
"Walking": "Camminare", "Walking": "Camminare",
"about": "Su di noi", "about": "Su di noi",
"account-delete-confirm": "Stai per eliminare il tuo account. Tutti i tuoi percorsi saranno cancellati. Vuoi procedere?", "account-delete-confirm": "Stai per eliminare il tuo account. Tutti i tuoi percorsi saranno cancellati. Vuoi procedere?",
@@ -21,6 +22,7 @@
"already-account": "Hai già un account?", "already-account": "Hai già un account?",
"altitude": "Altitudine", "altitude": "Altitudine",
"amenity": "Amenity", "amenity": "Amenity",
"ammenity": "",
"api-documentation": "Documentazione API", "api-documentation": "Documentazione API",
"api-tokens": "", "api-tokens": "",
"api-tokens-hint": "", "api-tokens-hint": "",
@@ -263,6 +265,8 @@
"make-thumbnail": "Imposta miniatura", "make-thumbnail": "Imposta miniatura",
"map": "Mappa", "map": "Mappa",
"map-style": "Map style", "map-style": "Map style",
"mark-trail-as-completed": "",
"mark-trail-as-completed-modal-text": "",
"max-hiking-difficulty": "Max. Hiking Difficulty", "max-hiking-difficulty": "Max. Hiking Difficulty",
"metric": "Metrico", "metric": "Metrico",
"moderate": "Moderato", "moderate": "Moderato",
@@ -290,6 +294,7 @@
"new-password-text": "Definire una nuova password", "new-password-text": "Definire una nuova password",
"new-token-generated": "", "new-token-generated": "",
"new-trail": "Nuovo percorso", "new-trail": "Nuovo percorso",
"no": "",
"no-account": "Non hai ancora un account?", "no-account": "Non hai ancora un account?",
"no-api-tokens": "", "no-api-tokens": "",
"no-comments-so-far": "Nessun commento per il momento", "no-comments-so-far": "Nessun commento per il momento",
@@ -466,5 +471,6 @@
"welcome_to": "Benvenuti a", "welcome_to": "Benvenuti a",
"width": "Width", "width": "Width",
"wrong-username-or-password": "Nome utente o password errati", "wrong-username-or-password": "Nome utente o password errati",
"yes": "",
"you-can": "Puoi" "you-can": "Puoi"
} }

View File

@@ -3,6 +3,7 @@
"Canoeing": "Kanoën", "Canoeing": "Kanoën",
"Climbing": "Klimmen", "Climbing": "Klimmen",
"Hiking": "Hiken", "Hiking": "Hiken",
"Skiing": "",
"Walking": "Wandelen", "Walking": "Wandelen",
"about": "Over", "about": "Over",
"account-delete-confirm": "Je staat op het punt je account te verwijderen. Al je routes worden hierdoor eveneens verwijderd. Wil je doorgaan?", "account-delete-confirm": "Je staat op het punt je account te verwijderen. Al je routes worden hierdoor eveneens verwijderd. Wil je doorgaan?",
@@ -264,6 +265,8 @@
"make-thumbnail": "Miniatuur maken", "make-thumbnail": "Miniatuur maken",
"map": "Kaart", "map": "Kaart",
"map-style": "Kaartstijl", "map-style": "Kaartstijl",
"mark-trail-as-completed": "",
"mark-trail-as-completed-modal-text": "",
"max-hiking-difficulty": "Max. Hiking Moeilijkheid", "max-hiking-difficulty": "Max. Hiking Moeilijkheid",
"metric": "Metrisch", "metric": "Metrisch",
"moderate": "Gemiddeld", "moderate": "Gemiddeld",
@@ -291,6 +294,7 @@
"new-password-text": "Stel nieuw wachtwoord in", "new-password-text": "Stel nieuw wachtwoord in",
"new-token-generated": "", "new-token-generated": "",
"new-trail": "Nieuwe Route", "new-trail": "Nieuwe Route",
"no": "",
"no-account": "Heb je nog geen account?", "no-account": "Heb je nog geen account?",
"no-api-tokens": "", "no-api-tokens": "",
"no-comments-so-far": "Tot nu toe geen opmerkingen", "no-comments-so-far": "Tot nu toe geen opmerkingen",
@@ -467,5 +471,6 @@
"welcome_to": "Welkom bij", "welcome_to": "Welkom bij",
"width": "Breedte", "width": "Breedte",
"wrong-username-or-password": "Onjuiste gebruikersnaam of wachtwoord", "wrong-username-or-password": "Onjuiste gebruikersnaam of wachtwoord",
"yes": "",
"you-can": "Jij kunt" "you-can": "Jij kunt"
} }

View File

@@ -265,6 +265,8 @@
"make-thumbnail": "Lag miniatyrbilde", "make-thumbnail": "Lag miniatyrbilde",
"map": "Kart", "map": "Kart",
"map-style": "Kartstil", "map-style": "Kartstil",
"mark-trail-as-completed": "",
"mark-trail-as-completed-modal-text": "",
"max-hiking-difficulty": "Maks. vanskelighetsgrad", "max-hiking-difficulty": "Maks. vanskelighetsgrad",
"metric": "Metrisk", "metric": "Metrisk",
"moderate": "Moderat", "moderate": "Moderat",
@@ -292,6 +294,7 @@
"new-password-text": "Sett et nytt passord", "new-password-text": "Sett et nytt passord",
"new-token-generated": "Nytt API-token generert", "new-token-generated": "Nytt API-token generert",
"new-trail": "Ny sti", "new-trail": "Ny sti",
"no": "",
"no-account": "Har du ikke en konto?", "no-account": "Har du ikke en konto?",
"no-api-tokens": "Du har ingen API-tokens", "no-api-tokens": "Du har ingen API-tokens",
"no-comments-so-far": "Ingen kommentarer ennå", "no-comments-so-far": "Ingen kommentarer ennå",
@@ -468,5 +471,6 @@
"welcome_to": "Velkommen til", "welcome_to": "Velkommen til",
"width": "Bredde", "width": "Bredde",
"wrong-username-or-password": "Feil brukernavn eller passord", "wrong-username-or-password": "Feil brukernavn eller passord",
"yes": "",
"you-can": "Du kan" "you-can": "Du kan"
} }

View File

@@ -3,6 +3,7 @@
"Canoeing": "Kajak", "Canoeing": "Kajak",
"Climbing": "Wspinaczka", "Climbing": "Wspinaczka",
"Hiking": "Wędrówka", "Hiking": "Wędrówka",
"Skiing": "",
"Walking": "Spacer", "Walking": "Spacer",
"about": "Na temat", "about": "Na temat",
"account-delete-confirm": "Za chwilę usuniesz swoje konto. Wszystkie twoje szlaki zostaną usunięte. Czy chcesz kontynuować?", "account-delete-confirm": "Za chwilę usuniesz swoje konto. Wszystkie twoje szlaki zostaną usunięte. Czy chcesz kontynuować?",
@@ -21,6 +22,7 @@
"already-account": "Czy masz już konto?", "already-account": "Czy masz już konto?",
"altitude": "Wysokość", "altitude": "Wysokość",
"amenity": "Amenity", "amenity": "Amenity",
"ammenity": "",
"api-documentation": "Dokumentacja API", "api-documentation": "Dokumentacja API",
"api-tokens": "", "api-tokens": "",
"api-tokens-hint": "", "api-tokens-hint": "",
@@ -263,6 +265,8 @@
"make-thumbnail": "Zrób miniaturkę", "make-thumbnail": "Zrób miniaturkę",
"map": "Mapa", "map": "Mapa",
"map-style": "Map style", "map-style": "Map style",
"mark-trail-as-completed": "",
"mark-trail-as-completed-modal-text": "",
"max-hiking-difficulty": "Maksymalny poziom trudności wędrówki", "max-hiking-difficulty": "Maksymalny poziom trudności wędrówki",
"metric": "Metryczne", "metric": "Metryczne",
"moderate": "Średni", "moderate": "Średni",
@@ -290,6 +294,7 @@
"new-password-text": "Ustaw nowe hasło", "new-password-text": "Ustaw nowe hasło",
"new-token-generated": "", "new-token-generated": "",
"new-trail": "Nowy szlak", "new-trail": "Nowy szlak",
"no": "",
"no-account": "Nie masz konta?", "no-account": "Nie masz konta?",
"no-api-tokens": "", "no-api-tokens": "",
"no-comments-so-far": "Nie ma jeszcze komentarzy", "no-comments-so-far": "Nie ma jeszcze komentarzy",
@@ -466,5 +471,6 @@
"welcome_to": "Witaj w", "welcome_to": "Witaj w",
"width": "Szerokość", "width": "Szerokość",
"wrong-username-or-password": "Zła nazwa użytkownika lub hasło", "wrong-username-or-password": "Zła nazwa użytkownika lub hasło",
"yes": "",
"you-can": "Możesz" "you-can": "Możesz"
} }

View File

@@ -3,6 +3,7 @@
"Canoeing": "Canoagem", "Canoeing": "Canoagem",
"Climbing": "Escalada", "Climbing": "Escalada",
"Hiking": "Montanhismo", "Hiking": "Montanhismo",
"Skiing": "",
"Walking": "Caminhada", "Walking": "Caminhada",
"about": "Sobre", "about": "Sobre",
"account-delete-confirm": "Está prestes a excluir a sua conta. Todos os seus percursos também serão excluídos. Quer continuar?", "account-delete-confirm": "Está prestes a excluir a sua conta. Todos os seus percursos também serão excluídos. Quer continuar?",
@@ -21,6 +22,7 @@
"already-account": "Já tem uma conta?", "already-account": "Já tem uma conta?",
"altitude": "Altitude", "altitude": "Altitude",
"amenity": "Amenity", "amenity": "Amenity",
"ammenity": "",
"api-documentation": "Documentação da API", "api-documentation": "Documentação da API",
"api-tokens": "", "api-tokens": "",
"api-tokens-hint": "", "api-tokens-hint": "",
@@ -263,6 +265,8 @@
"make-thumbnail": "Faça miniatura", "make-thumbnail": "Faça miniatura",
"map": "Mapa", "map": "Mapa",
"map-style": "Map style", "map-style": "Map style",
"mark-trail-as-completed": "",
"mark-trail-as-completed-modal-text": "",
"max-hiking-difficulty": "Max. Hiking Difficulty", "max-hiking-difficulty": "Max. Hiking Difficulty",
"metric": "Métrica", "metric": "Métrica",
"moderate": "Moderado", "moderate": "Moderado",
@@ -290,6 +294,7 @@
"new-password-text": "Set a new password", "new-password-text": "Set a new password",
"new-token-generated": "", "new-token-generated": "",
"new-trail": "Nova trilha", "new-trail": "Nova trilha",
"no": "",
"no-account": "Não tem uma conta?", "no-account": "Não tem uma conta?",
"no-api-tokens": "", "no-api-tokens": "",
"no-comments-so-far": "No comments so far", "no-comments-so-far": "No comments so far",
@@ -466,5 +471,6 @@
"welcome_to": "Bem-vindo ao", "welcome_to": "Bem-vindo ao",
"width": "Width", "width": "Width",
"wrong-username-or-password": "Nome de utilizador ou palavra-passe errados", "wrong-username-or-password": "Nome de utilizador ou palavra-passe errados",
"yes": "",
"you-can": "Podes" "you-can": "Podes"
} }

View File

@@ -3,6 +3,7 @@
"Canoeing": "Каякинг", "Canoeing": "Каякинг",
"Climbing": "Скалолазание", "Climbing": "Скалолазание",
"Hiking": "Пеший туризм", "Hiking": "Пеший туризм",
"Skiing": "",
"Walking": "Прогулка", "Walking": "Прогулка",
"about": "О", "about": "О",
"account-delete-confirm": "Вы собираетесь удалить аккаунт. Все ваши треки также будут удалены. Продолжить?", "account-delete-confirm": "Вы собираетесь удалить аккаунт. Все ваши треки также будут удалены. Продолжить?",
@@ -21,6 +22,7 @@
"already-account": "Уже есть аккаунт?", "already-account": "Уже есть аккаунт?",
"altitude": "Высота", "altitude": "Высота",
"amenity": "Amenity", "amenity": "Amenity",
"ammenity": "",
"api-documentation": "Документация API", "api-documentation": "Документация API",
"api-tokens": "", "api-tokens": "",
"api-tokens-hint": "", "api-tokens-hint": "",
@@ -263,6 +265,8 @@
"make-thumbnail": "Сделать миниатюру", "make-thumbnail": "Сделать миниатюру",
"map": "Карта", "map": "Карта",
"map-style": "Стиль карты", "map-style": "Стиль карты",
"mark-trail-as-completed": "",
"mark-trail-as-completed-modal-text": "",
"max-hiking-difficulty": "Макс. сложность", "max-hiking-difficulty": "Макс. сложность",
"metric": "Метрическая", "metric": "Метрическая",
"moderate": "Средний", "moderate": "Средний",
@@ -290,6 +294,7 @@
"new-password-text": "Установить новый пароль", "new-password-text": "Установить новый пароль",
"new-token-generated": "", "new-token-generated": "",
"new-trail": "Новый трек", "new-trail": "Новый трек",
"no": "",
"no-account": "Нет аккаунта?", "no-account": "Нет аккаунта?",
"no-api-tokens": "", "no-api-tokens": "",
"no-comments-so-far": "Пока нет комментариев", "no-comments-so-far": "Пока нет комментариев",
@@ -466,5 +471,6 @@
"welcome_to": "Добро пожаловать в", "welcome_to": "Добро пожаловать в",
"width": "Ширина", "width": "Ширина",
"wrong-username-or-password": "Неверный логин или пароль", "wrong-username-or-password": "Неверный логин или пароль",
"yes": "",
"you-can": "Вы можете" "you-can": "Вы можете"
} }

View File

@@ -3,6 +3,7 @@
"Canoeing": "划艇", "Canoeing": "划艇",
"Climbing": "攀岩", "Climbing": "攀岩",
"Hiking": "徒步", "Hiking": "徒步",
"Skiing": "",
"Walking": "步行", "Walking": "步行",
"about": "关于", "about": "关于",
"account-delete-confirm": "您现在要删除当前账户,所有的路线都会删除无法恢复,确认继续操作吗?", "account-delete-confirm": "您现在要删除当前账户,所有的路线都会删除无法恢复,确认继续操作吗?",
@@ -21,6 +22,7 @@
"already-account": "已注册账户?", "already-account": "已注册账户?",
"altitude": "海拔", "altitude": "海拔",
"amenity": "友好性", "amenity": "友好性",
"ammenity": "",
"api-documentation": "API 文档", "api-documentation": "API 文档",
"api-tokens": "", "api-tokens": "",
"api-tokens-hint": "", "api-tokens-hint": "",
@@ -263,6 +265,8 @@
"make-thumbnail": "生成缩略图", "make-thumbnail": "生成缩略图",
"map": "地图", "map": "地图",
"map-style": "地图样式", "map-style": "地图样式",
"mark-trail-as-completed": "",
"mark-trail-as-completed-modal-text": "",
"max-hiking-difficulty": "最大徒步难度", "max-hiking-difficulty": "最大徒步难度",
"metric": "公制", "metric": "公制",
"moderate": "中等", "moderate": "中等",
@@ -290,6 +294,7 @@
"new-password-text": "设置新密码", "new-password-text": "设置新密码",
"new-token-generated": "", "new-token-generated": "",
"new-trail": "创建新路线", "new-trail": "创建新路线",
"no": "",
"no-account": "还未注册?", "no-account": "还未注册?",
"no-api-tokens": "", "no-api-tokens": "",
"no-comments-so-far": "到目前为止没有评论", "no-comments-so-far": "到目前为止没有评论",
@@ -466,5 +471,6 @@
"welcome_to": "欢迎", "welcome_to": "欢迎",
"width": "宽度", "width": "宽度",
"wrong-username-or-password": "用户名或密码无效", "wrong-username-or-password": "用户名或密码无效",
"yes": "",
"you-can": "你可以" "you-can": "你可以"
} }

View File

@@ -9,6 +9,7 @@ const TrailCreateSchema = z.object({
location: z.string().optional(), location: z.string().optional(),
date: z.string().optional().refine((val) => !val || !isNaN(Date.parse(val)), "invalid-date"), date: z.string().optional().refine((val) => !val || !isNaN(Date.parse(val)), "invalid-date"),
public: z.boolean(), public: z.boolean(),
completed: z.boolean(),
difficulty: z.enum(["easy", "moderate", "difficult"]).optional(), difficulty: z.enum(["easy", "moderate", "difficult"]).optional(),
lat: z.number().min(-90).max(90).optional(), lat: z.number().min(-90).max(90).optional(),
lon: z.number().min(-180).max(180).optional(), lon: z.number().min(-180).max(180).optional(),
@@ -32,6 +33,7 @@ const TrailUpdateSchema = z.object({
location: z.string().optional(), location: z.string().optional(),
date: z.string().optional().refine((val) => !val || !isNaN(Date.parse(val)), "invalid-date"), date: z.string().optional().refine((val) => !val || !isNaN(Date.parse(val)), "invalid-date"),
public: z.boolean().optional(), public: z.boolean().optional(),
completed: z.boolean().optional(),
difficulty: z.enum(["easy", "moderate", "difficult"]).optional(), difficulty: z.enum(["easy", "moderate", "difficult"]).optional(),
lat: z.number().min(-90).max(90).optional(), lat: z.number().min(-90).max(90).optional(),
lon: z.number().min(-180).max(180).optional(), lon: z.number().min(-180).max(180).optional(),

View File

@@ -15,6 +15,7 @@ class Trail {
location?: string; location?: string;
date?: string; date?: string;
public: boolean; public: boolean;
completed: boolean;
distance?: number; distance?: number;
elevation_gain?: number; elevation_gain?: number;
elevation_loss?: number; elevation_loss?: number;
@@ -55,6 +56,7 @@ class Trail {
location?: string, location?: string,
date?: string, date?: string,
public?: boolean, public?: boolean,
completed?: boolean,
distance?: number, distance?: number,
elevation_gain?: number, elevation_gain?: number,
elevation_loss?: number, elevation_loss?: number,
@@ -82,6 +84,7 @@ class Trail {
this.location = params?.location; this.location = params?.location;
this.date = params?.date ?? new Date().toISOString().split('T')[0]; this.date = params?.date ?? new Date().toISOString().split('T')[0];
this.public = params?.public ?? false this.public = params?.public ?? false
this.completed = params?.completed ?? false,
this.distance = params?.distance ?? 0; this.distance = params?.distance ?? 0;
this.elevation_gain = params?.elevation_gain ?? 0; this.elevation_gain = params?.elevation_gain ?? 0;
this.elevation_loss = params?.elevation_loss ?? 0; this.elevation_loss = params?.elevation_loss ?? 0;

View File

@@ -441,6 +441,7 @@ export async function searchResultToTrailList(hits: Hits<TrailSearchResult>): Pr
name: h.name, name: h.name,
photos: h.thumbnail ? [h.thumbnail] : [], photos: h.thumbnail ? [h.thumbnail] : [],
public: h.public, public: h.public,
completed: h.completed,
summit_logs: [], summit_logs: [],
waypoints: [], waypoints: [],
tags: h.tags ?? [], tags: h.tags ?? [],

View File

@@ -101,6 +101,7 @@
import { z } from "zod"; import { z } from "zod";
import Track from "$lib/models/gpx/track.js"; import Track from "$lib/models/gpx/track.js";
import TrackSegment from "$lib/models/gpx/track-segment.js"; import TrackSegment from "$lib/models/gpx/track-segment.js";
import ConfirmModal from "$lib/components/confirm_modal.svelte";
let { data } = $props(); let { data } = $props();
@@ -112,6 +113,7 @@
let waypointModal: WaypointModal; let waypointModal: WaypointModal;
let summitLogModal: SummitLogModal; let summitLogModal: SummitLogModal;
let listSelectModal: ListSearchModal; let listSelectModal: ListSearchModal;
let markTrailAsCompletedModal: ConfirmModal;
let loading = $state(false); let loading = $state(false);
@@ -452,7 +454,8 @@
if (!$formData.expand!.waypoints_via_trail?.length) { if (!$formData.expand!.waypoints_via_trail?.length) {
$formData.expand!.waypoints_via_trail = []; $formData.expand!.waypoints_via_trail = [];
} }
$formData.expand!.waypoints_via_trail = $formData.expand!.waypoints_via_trail; $formData.expand!.waypoints_via_trail =
$formData.expand!.waypoints_via_trail;
// updateTrailOnMap(); // updateTrailOnMap();
} }
@@ -464,7 +467,8 @@
) ?? -1; ) ?? -1;
if (editedWaypointIndex >= 0) { if (editedWaypointIndex >= 0) {
$formData.expand!.waypoints_via_trail![editedWaypointIndex] = savedWaypoint; $formData.expand!.waypoints_via_trail![editedWaypointIndex] =
savedWaypoint;
} else { } else {
savedWaypoint.id = cryptoRandomString({ length: 15 }); savedWaypoint.id = cryptoRandomString({ length: 15 });
$formData.expand!.waypoints_via_trail = [ $formData.expand!.waypoints_via_trail = [
@@ -479,7 +483,9 @@
function moveMarker(marker: M.Marker, wpId?: string) { function moveMarker(marker: M.Marker, wpId?: string) {
const position = marker.getLngLat(); const position = marker.getLngLat();
const editableWaypointIndex = const editableWaypointIndex =
$formData.expand!.waypoints_via_trail?.findIndex((w) => w.id == wpId) ?? -1; $formData.expand!.waypoints_via_trail?.findIndex(
(w) => w.id == wpId,
) ?? -1;
const editableWaypoint = const editableWaypoint =
$formData.expand!.waypoints_via_trail![editableWaypointIndex]; $formData.expand!.waypoints_via_trail![editableWaypointIndex];
if (!editableWaypoint) { if (!editableWaypoint) {
@@ -487,7 +493,9 @@
} }
editableWaypoint.lat = position.lat; editableWaypoint.lat = position.lat;
editableWaypoint.lon = position.lng; editableWaypoint.lon = position.lng;
$formData.expand!.waypoints_via_trail = [...($formData.expand!.waypoints_via_trail ?? [])]; $formData.expand!.waypoints_via_trail = [
...($formData.expand!.waypoints_via_trail ?? []),
];
// updateTrailOnMap(); // updateTrailOnMap();
} }
@@ -515,6 +523,13 @@
log, log,
]; ];
} }
if (
$formData.expand?.summit_logs_via_trail?.length == 1 &&
!$formData.completed
) {
markTrailAsCompletedModal.openModal();
}
} }
function handleSummitLogMenuClick( function handleSummitLogMenuClick(
@@ -1167,6 +1182,10 @@
initRouteAnchors(valhallaStore.route, true); initRouteAnchors(valhallaStore.route, true);
updateTrailWithRouteData(); updateTrailWithRouteData();
} }
function markTrailAsCompleted() {
setFields("completed", true);
}
</script> </script>
<svelte:head> <svelte:head>
@@ -1180,7 +1199,7 @@
<main class="grid grid-cols-1 md:grid-cols-[400px_1fr]"> <main class="grid grid-cols-1 md:grid-cols-[400px_1fr]">
<form <form
id="trail-form" id="trail-form"
class="overflow-y-auto overflow-x-hidden flex flex-col gap-4 px-8 order-1 md:order-none mt-8 md:mt-0" class="overflow-y-auto overflow-x-hidden flex flex-col gap-4 px-8 order-1 md:order-0 mt-8 md:mt-0"
use:form use:form
> >
<Search <Search
@@ -1360,6 +1379,11 @@
></Select> ></Select>
</div> </div>
<Toggle
name="completed"
label={$formData.completed ? $_("completed") : $_("not-completed")}
icon={$formData.completed ? "flag-checkered" : "compass-drafting"}
></Toggle>
<Toggle <Toggle
name="public" name="public"
label={$formData.public ? $_("public") : $_("private")} label={$formData.public ? $_("public") : $_("private")}
@@ -1525,6 +1549,15 @@
bind:this={listSelectModal} bind:this={listSelectModal}
onchange={(e) => handleListSelection(e)} onchange={(e) => handleListSelection(e)}
></ListSearchModal> ></ListSearchModal>
<ConfirmModal
id="mark-trail-as-completed-modal"
title={$_("mark-trail-as-completed")}
text={$_("mark-trail-as-completed-modal-text")}
action={$_("yes")}
deny={$_("no")}
bind:this={markTrailAsCompletedModal}
onconfirm={markTrailAsCompleted}
></ConfirmModal>
<style> <style>
#trail-map { #trail-map {