adds trail difficulty property

This commit is contained in:
Christian Beutel
2024-03-10 21:02:49 +01:00
parent 4b8e740730
commit a2819f3b45
15 changed files with 152 additions and 29 deletions

View File

@@ -11,22 +11,11 @@
import RadioGroup from "../base/radio_group.svelte";
import Search, { type SearchItem } from "../base/search.svelte";
import Slider from "../base/slider.svelte";
import type { SelectItem } from "../base/select.svelte";
export let categories: Category[];
export let filterExpanded: boolean = true;
export let filter: TrailFilter = {
q: "",
category: [],
near: {
radius: 2000,
},
distanceMin: 0,
distanceMax: 20000,
elevationGainMin: 0,
elevationGainMax: 4000,
sort: "created",
sortOrder: "+",
};
export let filter: TrailFilter;
export let showTrailSearch: boolean = true;
export let showCitySearch: boolean = true;
@@ -38,6 +27,12 @@
{ text: $_("no-preference"), value: "no_preference" },
];
const difficultyItems: SelectItem[] = [
{ text: $_("easy"), value: "easy" },
{ text: $_("moderate"), value: "moderate" },
{ text: $_("difficult"), value: "difficult" },
];
let searchDropdownItems: SearchItem[] = [];
let citySearchQuery: string = "";
@@ -59,6 +54,19 @@
update();
}
function setDifficultyFilter(difficulty: "easy" | "moderate" | "difficult") {
const difficultyIndex = filter.difficulty.findIndex(
(d) => d == difficulty,
);
if (difficultyIndex !== -1) {
filter.difficulty.splice(difficultyIndex, 1);
} else {
filter.difficulty.push(difficulty);
}
update();
}
function setCompletedFilter(item: RadioItem) {
switch (item.value) {
case "no_preference":
@@ -150,6 +158,24 @@
</div>
{/each}
<hr class="my-4 border-separator" />
<p class="text-sm font-medium pb-4">{$_("difficulty")}</p>
{#each difficultyItems as difficulty, i}
<div class="flex items-center mb-4">
<input
id="{difficulty.value}-checkbox"
type="checkbox"
checked={filter.difficulty.includes(difficulty.value)}
value={difficulty.value}
class="w-4 h-4 bg-input-background accent-primary border-input-border focus:ring-input-ring focus:ring-2"
on:change={() => setDifficultyFilter(difficulty.value)}
/>
<label
for="{difficulty.value}-checkbox"
class="ms-2 text-sm">{difficulty.text}</label
>
</div>
{/each}
<hr class="my-4 border-separator" />
{#if showCitySearch}
<p class="text-sm font-medium pb-4">{$_("near")}</p>
<div class="mb-8">

View File

@@ -1,13 +1,12 @@
<script lang="ts">
import type { Trail, TrailFilter } from "$lib/models/trail";
import { createEventDispatcher, onMount } from "svelte";
import { _ } from "svelte-i18n";
import Pagination from "../base/pagination.svelte";
import Select, { type SelectItem } from "../base/select.svelte";
import EmptyStateSearch from "../empty_states/empty_state_search.svelte";
import TrailCard from "./trail_card.svelte";
import TrailListItem from "./trail_list_item.svelte";
import { _ } from "svelte-i18n";
import Pagination from "../base/pagination.svelte";
import TextField from "../base/text_field.svelte";
export let filter: TrailFilter;
export let trails: Trail[];
@@ -28,6 +27,7 @@
const sortOptions: SelectItem[] = [
{ text: $_("alphabetical"), value: "name" },
{ text: $_("creation-date"), value: "created" },
{ text: $_("difficulty"), value: "difficulty" },
{ text: $_("distance"), value: "distance" },
{ text: $_("elevation-gain"), value: "elevation_gain" },
];

View File

@@ -28,10 +28,13 @@
"delete-trail-confirm": "Möchtest du diese Route wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.",
"describe-your-trail": "Beschreibe deine Route",
"description": "Beschreibung",
"difficult": "Schwierig",
"difficulty": "Schwierigkeit",
"directions": "Wegbeschreibung",
"display-as": "Anzeigen als",
"distance": "Distanz",
"download-gpx": "GPX herunterladen",
"easy": "Einfach",
"edit": "Bearbeiten",
"edit-entry": "Eintrag bearbeiten",
"edit-list": "Liste bearbeiten",
@@ -61,6 +64,7 @@
"make-thumbnail": "Thumbnail festlegen",
"map": "Karte",
"metric": "Metrisch",
"moderate": "Mittel",
"name": "Name",
"near": "Nahe",
"new-list": "Neue Liste",

View File

@@ -28,10 +28,13 @@
"delete-trail-confirm": "Do you really want to delete this trail? This action cannot be undone.",
"describe-your-trail": "Describe your trail",
"description": "Description",
"difficult": "Difficult",
"difficulty": "Difficulty",
"directions": "Directions",
"display-as": "Display as",
"distance": "Distance",
"download-gpx": "Download GPX",
"easy": "Easy",
"edit": "Edit",
"edit-entry": "Edit Entry",
"edit-list": "Edit List",
@@ -61,6 +64,7 @@
"make-thumbnail": "Make thumbnail",
"map": "Map",
"metric": "Metric",
"moderate": "Moderate",
"name": "Name",
"near": "Near",
"new-list": "New List",

View File

@@ -1,13 +1,13 @@
import { env } from '$env/dynamic/public'
import { env } from '$env/dynamic/private'
import { MeiliSearch } from 'meilisearch'
import { pb } from './pocketbase'
export function createInstance() {
export function createInstance() {
if (pb.authStore.model) {
return new MeiliSearch({ host: env.PUBLIC_MEILISEARCH_URL, apiKey: pb.authStore.model.token })
return new MeiliSearch({ host: env.MEILI_URL, apiKey: pb.authStore.model.token })
}
return new MeiliSearch({ host: env.PUBLIC_MEILISEARCH_URL, apiKey: env.PUBLIC_MEILISEARCH_API_TOKEN })
return new MeiliSearch({ host: env.MEILI_URL, apiKey: env.MEILI_API_TOKEN })
}
export function regenerateInstance() {

View File

@@ -11,6 +11,7 @@ class Trail {
distance?: number;
elevation_gain?: number;
duration?: number;
difficulty?: "easy"|"moderate"|"difficult"
lat?: number;
lon?: number;
thumbnail: number;
@@ -38,6 +39,7 @@ class Trail {
distance?: number,
elevation_gain?: number,
duration?: number,
difficulty?: "easy"|"moderate"|"difficult",
lat?:number,
lon?: number,
thumbnail?: number,
@@ -59,6 +61,7 @@ class Trail {
this.distance = params?.distance;
this.elevation_gain = params?.elevation_gain;
this.duration = params?.duration;
this.difficulty = params?.difficulty ?? "easy";
this.lat = params?.lat;
this.lon = params?.lon;
this.thumbnail = params?.thumbnail ?? 0;
@@ -93,6 +96,7 @@ const trailSchema = object<Trail>({
interface TrailFilter {
q: string,
category: string[],
difficulty: ("easy" | "moderate" | "difficult")[]
near: {
lat?: number,
lon?: number,

View File

@@ -36,6 +36,8 @@ export async function trails_index(data: { perPage: number, random?: boolean, f:
export async function trails_search_filter(filter: TrailFilter, page: number = 1, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
let filterText: string = `distance >= ${filter.distanceMin} AND distance <= ${filter.distanceMax} AND elevation_gain >= ${filter.elevationGainMin} AND elevation_gain <= ${filter.elevationGainMax}`;
filterText += ` AND difficulty IN [${filter.difficulty.join(",")}]`
if (filter.category.length > 0) {
filterText += ` AND category IN [${filter.category.join(",")}]`;
}
@@ -45,12 +47,20 @@ export async function trails_search_filter(filter: TrailFilter, page: number = 1
if (filter.near.lat && filter.near.lon) {
filterText += ` AND _geoRadius(${filter.near.lat}, ${filter.near.lon}, ${filter.near.radius})`
}
if (filter.near.lat && filter.near.lon) {
filterText += ` AND _geoRadius(${filter.near.lat}, ${filter.near.lon}, ${filter.near.radius})`
}
let r = await f("/api/v1/search/trails", {
method: "POST",
body: JSON.stringify({ q: filter.q, options: { filter: filterText, hitsPerPage: 20, page: page } }),
});
const result = await r.json();
if (!r.ok) {
throw new ClientResponseError(result)
}
const trailIds = result.hits.map((h: Record<string, any>) => h.id);
if (trailIds.length == 0) {

View File

@@ -1,4 +1,3 @@
import { regenerateInstance } from "$lib/meilisearch";
import { pb } from "$lib/pocketbase";
import { ClientResponseError } from "pocketbase";
import { writable, type Writable } from "svelte/store";
@@ -38,7 +37,6 @@ export async function login(user: User) {
if (r.ok) {
pb.authStore.loadFromCookie(document.cookie)
regenerateInstance()
} else {
throw new ClientResponseError(await r.json())
}
@@ -47,7 +45,6 @@ export async function login(user: User) {
export async function logout() {
pb.authStore.clear();
regenerateInstance()
}
export async function users_update(id: string, user: User | { [K in keyof User]?: User[K] } | FormData) {

View File

@@ -6,6 +6,7 @@ export const load: Load = async ({ params, fetch }) => {
const filter: TrailFilter = {
q: "",
category: [],
difficulty: ["easy", "moderate", "difficult"],
near: {
radius: 2000,
},

View File

@@ -452,6 +452,16 @@
label={$_("describe-your-trail")}
bind:value={$form.description}
></Textarea>
<Select
name="difficulty"
label={$_("difficulty")}
bind:value={$form.difficulty}
items={[
{ text: $_('easy'), value: "easy" },
{ text: $_('moderate'), value: "moderate" },
{ text: $_('difficult'), value: "difficult" },
]}
></Select>
{#if $form.expand.category}
<Select
name="category"

View File

@@ -7,6 +7,7 @@ export const load: ServerLoad = async ({ params, locals, url, fetch }) => {
const filter: TrailFilter = {
q: "",
category: [],
difficulty: ["easy", "moderate", "difficult"],
near: {
radius: 2000,
},