adds search api

This commit is contained in:
Christian Beutel
2024-03-10 00:52:45 +01:00
parent 963b589b2d
commit 01e1053569
25 changed files with 271 additions and 87 deletions

View File

@@ -0,0 +1,87 @@
<script lang="ts">
import { range } from "$lib/util/array_util";
import { createEventDispatcher } from "svelte";
export let page: number;
export let totalPages: number;
const maxPagesToRender = 6;
const dispatch = createEventDispatcher();
function update(clickedPage: number) {
if (page !== clickedPage) {
dispatch("pagination", clickedPage);
}
}
$: numberGenerator = () => {
if (totalPages < 4) {
return [];
}
if (totalPages <= maxPagesToRender) {
return range(totalPages - 2, 2);
}
if (page < maxPagesToRender - 2) {
return range(maxPagesToRender - 1, 2);
}
if (page > totalPages - 3) {
return range(totalPages - 2, totalPages - 5);
}
if (page > 2 && page <= totalPages - 3) {
return range(page + Math.max(1, maxPagesToRender - 5), page - 2);
}
return [];
};
</script>
<div class="flex gap-x-4 justify-center">
{#if totalPages > 1}
<button
class:text-gray-500={page == 1}
disabled={page == 1}
on:click={() => update(page - 1)}
><i class="fa fa-caret-left"></i></button
>
<button
class="w-8 aspect-square rounded-lg transition-colors border-input-border hover:bg-menu-item-background-hover"
class:border={page == 1}
on:click={() => update(1)}>1</button
>
{#if totalPages > maxPagesToRender && page >= maxPagesToRender - 2 && page > 2}
<span>...</span>
{:else if totalPages > 2}
<button
class="w-8 aspect-square rounded-lg transition-colors border-input-border hover:bg-menu-item-background-hover"
class:border={page == 2}
on:click={() => update(2)}>2</button
>
{/if}
{#each numberGenerator() as i}
<button
class="w-8 aspect-square rounded-lg transition-colors border-input-border hover:bg-menu-item-background-hover"
class:border={page == i + 1}
on:click={() => update(i + 1)}>{i + 1}</button
>
{/each}
{#if totalPages > maxPagesToRender && page < totalPages - 3}
<span>...</span>
{:else if totalPages > 3}
<button
class="w-8 aspect-square rounded-lg transition-colors border-input-border hover:bg-menu-item-background-hover"
class:border={page == totalPages - 1}
on:click={() => update(totalPages - 1)}>{totalPages - 1}</button
>
{/if}
<button
class="w-8 aspect-square rounded-lg transition-colors border-input-border hover:bg-menu-item-background-hover"
class:border={page == totalPages}
on:click={() => update(totalPages)}>{totalPages}</button
>
<button
class:text-gray-500={page == totalPages}
disabled={page == totalPages}
on:click={() => update(page + 1)}
><i class="fa fa-caret-right"></i></button
>
{/if}
</div>

View File

@@ -90,14 +90,14 @@
show_toast({ show_toast({
type: "success", type: "success",
icon: "check", icon: "check",
text: `Removed trail from "${list.name}"`, text: `${$_("removed-trail-from")} "${list.name}"`,
}); });
} else { } else {
await lists_add_trail(list, trail); await lists_add_trail(list, trail);
show_toast({ show_toast({
type: "success", type: "success",
icon: "check", icon: "check",
text: `Added trail to "${list.name}"`, text: `${$_("added-trail-to")} "${list.name}"`,
}); });
} }
await lists_index(); await lists_index();

View File

@@ -6,13 +6,19 @@
import TrailCard from "./trail_card.svelte"; import TrailCard from "./trail_card.svelte";
import TrailListItem from "./trail_list_item.svelte"; import TrailListItem from "./trail_list_item.svelte";
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import Pagination from "../base/pagination.svelte";
import TextField from "../base/text_field.svelte";
export let filter: TrailFilter; export let filter: TrailFilter;
export let trails: Trail[]; export let trails: Trail[];
export let pagination: { page: number; totalPages: number } = {
page: 1,
totalPages: 1,
};
const displayOptions: SelectItem[] = [ const displayOptions: SelectItem[] = [
{ text: $_('card', {values: {n: 2}}), value: "cards" }, { text: $_("card", { values: { n: 2 } }), value: "cards" },
{ text: $_('list', {values: {n: 1}}), value: "list" }, { text: $_("list", { values: { n: 1 } }), value: "list" },
]; ];
let selectedDisplayOption = displayOptions[0].value; let selectedDisplayOption = displayOptions[0].value;
@@ -20,10 +26,10 @@
let dispatch = createEventDispatcher(); let dispatch = createEventDispatcher();
const sortOptions: SelectItem[] = [ const sortOptions: SelectItem[] = [
{ text: $_('alphabetical'), value: "name" }, { text: $_("alphabetical"), value: "name" },
{ text: $_('creation-date'), value: "created" }, { text: $_("creation-date"), value: "created" },
{ text: $_('distance'), value: "distance" }, { text: $_("distance"), value: "distance" },
{ text: $_('elevation-gain'), value: "elevation_gain" }, { text: $_("elevation-gain"), value: "elevation_gain" },
]; ];
onMount(() => { onMount(() => {
@@ -37,11 +43,11 @@
localStorage.setItem("displayOption", selectedDisplayOption); localStorage.setItem("displayOption", selectedDisplayOption);
} }
async function setSort() { function setSort() {
dispatch("update", filter); dispatch("update", filter);
} }
async function setSortOrder() { function setSortOrder() {
if (filter.sortOrder === "+") { if (filter.sortOrder === "+") {
filter.sortOrder = "-"; filter.sortOrder = "-";
} else { } else {
@@ -52,9 +58,16 @@
</script> </script>
<div class="min-w-0"> <div class="min-w-0">
<div class="flex items-start gap-8 justify-end mx-4"> <div class="flex items-end flex-wrap md:flex-nowrap gap-x-6 gap-y-2 mx-4">
<div> <div class="basis-full order-1 md:order-none mt-6 md:mt-0">
<p class="text-sm text-gray-500 pb-2">{$_('sort')}</p> <Pagination
page={pagination.page}
totalPages={pagination.totalPages}
on:pagination
></Pagination>
</div>
<div class="lg:shrink-0">
<p class="text-sm text-gray-500 pb-2">{$_("sort")}</p>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<Select <Select
bind:value={filter.sort} bind:value={filter.sort}
@@ -70,8 +83,8 @@
> >
</div> </div>
</div> </div>
<div> <div class="lg:shrink-0">
<p class="text-sm text-gray-500 pb-2">{$_('display-as')}</p> <p class="text-sm text-gray-500 pb-2">{$_("display-as")}</p>
<Select <Select
bind:value={selectedDisplayOption} bind:value={selectedDisplayOption}

View File

@@ -4,6 +4,7 @@
"add-entry": "Eintrag hinzufügen", "add-entry": "Eintrag hinzufügen",
"add-to-list": "Zu Liste hinzufügen", "add-to-list": "Zu Liste hinzufügen",
"add-waypoint": "Wegpunkt hinzufügen", "add-waypoint": "Wegpunkt hinzufügen",
"added-trail-to": "Route hinzugefügt zu",
"alphabetical": "Alphabetisch", "alphabetical": "Alphabetisch",
"already-account": "Du hast bereits ein Konto?", "already-account": "Du hast bereits ein Konto?",
"avatar": "Avatar", "avatar": "Avatar",
@@ -40,11 +41,12 @@
"english": "Englisch", "english": "Englisch",
"est-duration": "Gesch. Dauer", "est-duration": "Gesch. Dauer",
"explore": "Erkunden", "explore": "Erkunden",
"explore-some-trails": "Erkunden Sie einige Routen",
"features": "Feature", "features": "Feature",
"german": "Deutsch", "german": "Deutsch",
"hero_section_0_text": "Entdecke spannende Routen, speichere deine Favoriten und erlebe die Schönheit der Natur. Finde dein nächstes Abenteuer!", "hero_section_0_text": "Entdecke spannende Routen, speichere deine Favoriten und erlebe die Schönheit der Natur. Finde dein nächstes Abenteuer!",
"hero_section_1_text": "Hier sind einige Routen, die dir gefallen könnten. Oder du wirfst einen Blick auf die vollständige Liste.", "hero_section_1_text": "Hier sind einige Routen, die dir gefallen könnten. Oder du wirfst einen Blick auf die vollständige Liste.",
"hero_section_2_text": "Wusstest due schon? Du kannst nicht nur deine Routen speichern. Es gibt viele Kategorien für alle deine Abenteuer.", "hero_section_2_text": "Wusstest du schon? Du kannst nicht nur deine Routen speichern. Es gibt viele Kategorien für alle deine Abenteuer.",
"icon": "Icon", "icon": "Icon",
"imperial": "Amerikanisch", "imperial": "Amerikanisch",
"language": "Sprache", "language": "Sprache",
@@ -74,6 +76,7 @@
"public": "Öffentlich", "public": "Öffentlich",
"radius": "Radius", "radius": "Radius",
"register": "Registrieren", "register": "Registrieren",
"removed-trail-from": "Route entfernt aus",
"save": "Speichern", "save": "Speichern",
"save-trail": "Route speichern", "save-trail": "Route speichern",
"search-cities": "Städte suchen", "search-cities": "Städte suchen",
@@ -88,6 +91,7 @@
"summit-book": "Gipfelbuch", "summit-book": "Gipfelbuch",
"text": "Text", "text": "Text",
"trail": "{n, plural, =1 {Route} other {Routen}}", "trail": "{n, plural, =1 {Route} other {Routen}}",
"trails-for-you": "Routen für dich",
"units": "Einheiten", "units": "Einheiten",
"upload-gpx": "GPX hochladen", "upload-gpx": "GPX hochladen",
"username": "Nutzername", "username": "Nutzername",

View File

@@ -4,6 +4,7 @@
"add-entry": "Add Entry", "add-entry": "Add Entry",
"add-to-list": "Add to list", "add-to-list": "Add to list",
"add-waypoint": "Add Waypoint", "add-waypoint": "Add Waypoint",
"added-trail-to": "Added trail to",
"alphabetical": "Alphabetical", "alphabetical": "Alphabetical",
"already-account": "Already have an account?", "already-account": "Already have an account?",
"avatar": "Avatar", "avatar": "Avatar",
@@ -40,6 +41,7 @@
"english": "English", "english": "English",
"est-duration": "Est. duration", "est-duration": "Est. duration",
"explore": "Explore", "explore": "Explore",
"explore-some-trails": "Explore some trails",
"features": "Features", "features": "Features",
"german": "German", "german": "German",
"hero_section_0_text": "Explore exciting trails, save your favorites, and experience the beauty of nature. Find your next adventure!", "hero_section_0_text": "Explore exciting trails, save your favorites, and experience the beauty of nature. Find your next adventure!",
@@ -74,6 +76,7 @@
"public": "Public", "public": "Public",
"radius": "Radius", "radius": "Radius",
"register": "Register", "register": "Register",
"removed-trail-from": "Removed trail from",
"save": "Save", "save": "Save",
"save-trail": "Save Trail", "save-trail": "Save Trail",
"search-cities": "Search cities", "search-cities": "Search cities",
@@ -88,6 +91,7 @@
"summit-book": "Summit Book", "summit-book": "Summit Book",
"text": "Text", "text": "Text",
"trail": "{n, plural, =1 {Trail} other {Trails}}", "trail": "{n, plural, =1 {Trail} other {Trails}}",
"trails-for-you": "Trails for you",
"units": "Units", "units": "Units",
"upload-gpx": "Upload GPX", "upload-gpx": "Upload GPX",
"username": "Username", "username": "Username",

View File

@@ -1,11 +1,20 @@
import { pb } from "$lib/pocketbase"; import { pb } from "$lib/pocketbase";
import type { Category } from "$lib/models/category"; import type { Category } from "$lib/models/category";
import { writable, type Writable } from "svelte/store"; import { writable, type Writable } from "svelte/store";
import { ClientResponseError } from "pocketbase";
export const categories: Writable<Category[]> = writable([]) export const categories: Writable<Category[]> = writable([])
export async function categories_index() { export async function categories_index(f: Function = fetch) {
const response: Category[] = (await pb.collection('categories').getFullList<Category>()) const r = await f('/api/v1/category', {
method: 'GET',
})
if (!r.ok) {
throw new ClientResponseError(await r.json())
}
const response = await r.json();
categories.set(response); categories.set(response);

View File

@@ -4,19 +4,20 @@ import type { Waypoint } from "$lib/models/waypoint";
import { pb } from "$lib/pocketbase"; import { pb } from "$lib/pocketbase";
import { getFileURL } from "$lib/util/file_util"; import { getFileURL } from "$lib/util/file_util";
import { util } from "$lib/vendor/svelte-form-lib/util"; import { util } from "$lib/vendor/svelte-form-lib/util";
import type { LatLng } from "leaflet";
import { ClientResponseError } from "pocketbase";
import { get, writable, type Writable } from "svelte/store"; import { get, writable, type Writable } from "svelte/store";
import { summit_logs_create, summit_logs_delete, summit_logs_update } from "./summit_log_store"; import { summit_logs_create, summit_logs_delete, summit_logs_update } from "./summit_log_store";
import { waypoints_create, waypoints_delete, waypoints_update } from "./waypoint_store"; import { waypoints_create, waypoints_delete, waypoints_update } from "./waypoint_store";
import type { LatLng } from "leaflet";
import { ClientResponseError } from "pocketbase";
export const trails: Writable<Trail[]> = writable([]) export const trails: Writable<Trail[]> = writable([])
export const trail: Writable<Trail> = writable(new Trail("")); export const trail: Writable<Trail> = writable(new Trail(""));
export const editTrail: Writable<Trail> = writable(new Trail("")); export const editTrail: Writable<Trail> = writable(new Trail(""));
export async function trails_index(data: { perPage: number, random?: boolean, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> } = { perPage: 5, random: false, f: fetch }) { export async function trails_index(data: { perPage: number, random?: boolean, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> } = { perPage: 30, random: false, f: fetch }) {
const r = await data.f('/api/v1/trail?' + new URLSearchParams({ const r = await data.f('/api/v1/trail?' + new URLSearchParams({
"per-page": data.perPage.toString(),
expand: "category,waypoints,summit_logs", expand: "category,waypoints,summit_logs",
sort: data.random ? "@random" : "" sort: data.random ? "@random" : ""
}), { }), {
@@ -32,7 +33,7 @@ export async function trails_index(data: { perPage: number, random?: boolean, f:
} }
} }
export async function trails_search_filter(filter: TrailFilter, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) { 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}`; let filterText: string = `distance >= ${filter.distanceMin} AND distance <= ${filter.distanceMax} AND elevation_gain >= ${filter.elevationGainMin} AND elevation_gain <= ${filter.elevationGainMax}`;
if (filter.category.length > 0) { if (filter.category.length > 0) {
@@ -46,7 +47,7 @@ export async function trails_search_filter(filter: TrailFilter, f: (url: Request
} }
let r = await f("/api/v1/search/trails", { let r = await f("/api/v1/search/trails", {
method: "POST", method: "POST",
body: JSON.stringify({ q: filter.q, options: { filter: filterText } }), body: JSON.stringify({ q: filter.q, options: { filter: filterText, hitsPerPage: 20, page: page } }),
}); });
const result = await r.json(); const result = await r.json();
@@ -68,7 +69,7 @@ export async function trails_search_filter(filter: TrailFilter, f: (url: Request
if (r.ok) { if (r.ok) {
trails.set(response.items); trails.set(response.items);
return response.items; return result;
} else { } else {
throw new ClientResponseError(response) throw new ClientResponseError(response)
} }
@@ -120,7 +121,6 @@ export async function trails_search_bounding_box(northEast: LatLng, southWest: L
const response = await r.json() const response = await r.json()
if (r.ok) { if (r.ok) {
for (const trail of response.items) { for (const trail of response.items) {
const gpxData: string = await fetchGPX(trail); const gpxData: string = await fetchGPX(trail);
trail.expand.gpx_data = gpxData; trail.expand.gpx_data = gpxData;
@@ -150,7 +150,7 @@ export async function trails_show(id: string, loadGPX?: boolean, f: (url: Reques
} }
if (loadGPX) { if (loadGPX) {
const gpxData: string = await fetchGPX(response); const gpxData: string = await fetchGPX(response, f);
response.expand.gpx_data = gpxData; response.expand.gpx_data = gpxData;
} }
@@ -317,12 +317,12 @@ export async function trails_delete(trail: Trail) {
} }
} }
async function fetchGPX(trail: Trail) { async function fetchGPX(trail: Trail, f: (url: RequestInfo | URL, config?: RequestInit) => Promise<Response> = fetch) {
if (!trail.gpx) { if (!trail.gpx) {
return ""; return "";
} }
const gpxUrl = getFileURL(trail, trail.gpx); const gpxUrl = getFileURL(trail, trail.gpx);
const response: Response = await fetch(gpxUrl); const response: Response = await f(gpxUrl);
const gpxData = await response.text(); const gpxData = await response.text();
return gpxData return gpxData

View File

@@ -51,9 +51,9 @@ export async function logout() {
} }
export async function users_update(id: string, user: User | { [K in keyof User]?: User[K] } | FormData) { export async function users_update(id: string, user: User | { [K in keyof User]?: User[K] } | FormData) {
const r = await fetch('/api/v1/user', { const r = await fetch('/api/v1/user/' + id, {
method: 'POST', method: 'POST',
body: JSON.stringify({ id: id, user: user }) body: JSON.stringify(user)
}) })
if (r.ok) { if (r.ok) {
@@ -65,9 +65,8 @@ export async function users_update(id: string, user: User | { [K in keyof User]?
} }
export async function users_delete(user: User) { export async function users_delete(user: User) {
const r = await fetch('/api/v1/user', { const r = await fetch('/api/v1/user/' + user.id, {
method: 'DELETE', method: 'DELETE',
body: JSON.stringify({ id: user.id })
}) })
if (!r.ok) { if (!r.ok) {

View File

@@ -0,0 +1,3 @@
export function range(to: number, from: number = 0): ReadonlyArray<number> {
return [...Array(to - from).keys()].map(i => i + from);
}

View File

@@ -1,9 +1,11 @@
import { pb } from "$lib/pocketbase"; import { pb } from "$lib/pocketbase";
import type { FileOptions } from "pocketbase"; import type { FileOptions } from "pocketbase";
export function getFileURL(record: { [key: string]: any; }, filename?: string, options?: FileOptions) { export function getFileURL(record: { [key: string]: any; }, filename?: string) {
if(!filename) { if (!filename) {
return ""; return "";
} }
return pb.files.getUrl(record, filename, options);
// return pb.files.getUrl(record, filename);
return `/api/v1/files/${record.collectionId}/${record.id}/${filename}`
} }

View File

@@ -3,12 +3,12 @@
import Toast from "$lib/components/base/toast.svelte"; import Toast from "$lib/components/base/toast.svelte";
import Footer from "$lib/components/footer.svelte"; import Footer from "$lib/components/footer.svelte";
import NavBar from "$lib/components/nav_bar.svelte"; import NavBar from "$lib/components/nav_bar.svelte";
import { currentUser } from "$lib/stores/user_store";
import { isRouteProtected } from "$lib/util/authorization_util"; import { isRouteProtected } from "$lib/util/authorization_util";
import "@fortawesome/fontawesome-free/css/all.min.css"; import "@fortawesome/fontawesome-free/css/all.min.css";
import "../css/app.css"; import "../css/app.css";
import "../css/components.css"; import "../css/components.css";
import "../css/theme.css"; import "../css/theme.css";
import { currentUser } from "$lib/stores/user_store";
beforeNavigate((n) => { beforeNavigate((n) => {
if (!$currentUser && isRouteProtected(n.to?.url?.pathname ?? "")) { if (!$currentUser && isRouteProtected(n.to?.url?.pathname ?? "")) {

View File

@@ -36,20 +36,26 @@
const response = await r.json(); const response = await r.json();
const trailItems = response.results[0].hits.map((t: Record<string, any>) => ({ const trailItems = response.results[0].hits.map(
text: t.name, (t: Record<string, any>) => ({
description: `Trail | ${t.location}`, text: t.name,
value: t.id, description: `Trail | ${t.location}`,
icon: "route", value: t.id,
})); icon: "route",
const cityItems = response.results[1].hits.map((c: Record<string, any>) => ({ }),
text: c.name, );
description: `City | ${ const cityItems = response.results[1].hits.map(
country_codes[c["country code"] as keyof typeof country_codes] (c: Record<string, any>) => ({
}`, text: c.name,
value: c, description: `City | ${
icon: "city", country_codes[
})); c["country code"] as keyof typeof country_codes
]
}`,
value: c,
icon: "city",
}),
);
searchDropdownItems = [...trailItems, ...cityItems]; searchDropdownItems = [...trailItems, ...cityItems];
} }
@@ -102,15 +108,15 @@
id="trails" id="trails"
class="flex flex-wrap justify-items-center gap-8 py-8 order-1 md:order-none" class="flex flex-wrap justify-items-center gap-8 py-8 order-1 md:order-none"
> >
{#each $trails.splice(0, 5) as trail} {#each { length: Math.min($trails.length, 4) } as _, i}
<a href="/trail/view/{trail.id}"> <a href="/trail/view/{$trails[i].id}">
<TrailCard {trail}></TrailCard></a <TrailCard trail={$trails[i]}></TrailCard></a
> >
{/each} {/each}
</div> </div>
<div class="max-w-md md:mx-auto space-y-8"> <div class="max-w-md md:mx-auto space-y-8">
<h2 class="text-4xl md:text-5xl font-bold"> <h2 class="text-4xl md:text-5xl font-bold">
{$currentUser ? "Trails for you" : "Explore some trails"} {$currentUser ? $_("trails-for-you") : $_("explore-some-trails")}
</h2> </h2>
<h5> <h5>
{$_("hero_section_1_text")} {$_("hero_section_1_text")}
@@ -118,6 +124,7 @@
<a <a
class="inline-block btn-primary btn-large" class="inline-block btn-primary btn-large"
href="/trails" href="/trails"
data-sveltekit-preload-data="off"
role="button">{$_("explore")}</a role="button">{$_("explore")}</a
> >
</div> </div>

View File

@@ -4,5 +4,5 @@ import type { ServerLoad } from "@sveltejs/kit";
export const load: ServerLoad = async ({ params, locals, fetch }) => { export const load: ServerLoad = async ({ params, locals, fetch }) => {
await trails_index({perPage: 20, random: true, f: fetch}) await trails_index({perPage: 20, random: true, f: fetch})
await categories_index() await categories_index(fetch)
}; };

View File

@@ -0,0 +1,13 @@
import type { Category } from "$lib/models/category";
import { pb } from "$lib/pocketbase";
import { error, json, type RequestEvent } from "@sveltejs/kit";
export async function GET(event: RequestEvent) {
try {
const r: Category[] = await pb.collection('categories').getFullList<Category>()
return json(r)
} catch (e: any) {
throw error(e.status, e);
}
}

View File

@@ -0,0 +1,23 @@
import { pb } from "$lib/pocketbase";
import { error, text, type RequestEvent } from "@sveltejs/kit";
export async function GET(event: RequestEvent) {
const parts = [];
parts.push("api");
parts.push("files");
parts.push(encodeURIComponent(event.params.collection as string));
parts.push(encodeURIComponent(event.params.record as string));
parts.push(encodeURIComponent(event.params.file as string));
let fileURL = pb.buildUrl(parts.join("/"));
try {
const r = await event.fetch(fileURL)
return new Response(r.body, {headers: r.headers});
} catch (e: any) {
console.log(e);
throw error(500, e);
}
}

View File

@@ -8,6 +8,8 @@ export async function POST(event: RequestEvent) {
const r = await ms.index(event.params.index as string).search(data.q, data.options); const r = await ms.index(event.params.index as string).search(data.q, data.options);
return json(r); return json(r);
} catch (e: any) { } catch (e: any) {
throw error(e.status, e) console.log(e);
throw error(e.httpStatus, e)
} }
} }

View File

@@ -10,6 +10,6 @@ export async function POST(event: RequestEvent) {
}); });
return json(r); return json(r);
} catch (e: any) { } catch (e: any) {
throw error(e.status, e) throw error(e.httpStatus, e)
} }
} }

View File

@@ -2,16 +2,6 @@ import { pb } from '$lib/pocketbase';
import type { User } from '$lib/stores/user_store'; import type { User } from '$lib/stores/user_store';
import { error, json, type RequestEvent } from '@sveltejs/kit' import { error, json, type RequestEvent } from '@sveltejs/kit'
export async function POST(event: RequestEvent) {
const data = await event.request.json()
try {
const r = await pb.collection('lists').update<User>(data.id, data.user)
return json(r);
} catch (e: any) {
throw error(e.status, e)
}
}
export async function PUT(event: RequestEvent) { export async function PUT(event: RequestEvent) {
const data = await event.request.json() const data = await event.request.json()
try { try {
@@ -22,12 +12,3 @@ export async function PUT(event: RequestEvent) {
} }
} }
export async function DELETE(event: RequestEvent) {
const data = await event.request.json()
try {
const r = await pb.collection('users').delete(data.id)
return json(r);
} catch (e: any) {
throw error(e.status, e)
}
}

View File

@@ -0,0 +1,22 @@
import { pb } from '$lib/pocketbase';
import type { User } from '$lib/stores/user_store';
import { error, json, type RequestEvent } from '@sveltejs/kit'
export async function POST(event: RequestEvent) {
const data = await event.request.json()
try {
const r = await pb.collection('users').update<User>(event.params.id as string, data)
return json(r);
} catch (e: any) {
throw error(e.status, e)
}
}
export async function DELETE(event: RequestEvent) {
try {
const r = await pb.collection('users').delete(event.params.id as string)
return json(r);
} catch (e: any) {
throw error(e.status, e)
}
}

View File

@@ -85,7 +85,7 @@
<h5 class="text-xl font-semibold">{$_("create-new-list")}</h5> <h5 class="text-xl font-semibold">{$_("create-new-list")}</h5>
</button> </button>
<hr class="border-separator my-2" /> <hr class="border-separator my-2" />
<div class="max-h-[512px] md:max-h-none overflow-y-auto"> <div>
{#each $lists as item, i} {#each $lists as item, i}
<li on:click={() => list.set(item)} role="presentation"> <li on:click={() => list.set(item)} role="presentation">
<ListCard <ListCard

View File

@@ -2,8 +2,8 @@ import { categories_index } from "$lib/stores/category_store";
import { trails } from "$lib/stores/trail_store"; import { trails } from "$lib/stores/trail_store";
import type { ServerLoad } from "@sveltejs/kit"; import type { ServerLoad } from "@sveltejs/kit";
export const load: ServerLoad = async ({ params, locals }) => { export const load: ServerLoad = async ({ params, locals, fetch }) => {
await categories_index() await categories_index(fetch)
trails.set([]) trails.set([])
}; };

View File

@@ -75,7 +75,7 @@
<svelte:head> <svelte:head>
<title>{$trail.name} | {$_("map")} | wanderer</title> <title>{$trail.name} | {$_("map")} | wanderer</title>
</svelte:head> </svelte:head>
<main class="grid grid-cols-1 md:grid-cols-[452px_1fr] gap-x-1 gap-y-4"> <main class="grid grid-cols-1 md:grid-cols-[458px_1fr] gap-x-1 gap-y-4">
<div <div
id="trail-details" id="trail-details"
class="md:overflow-y-auto md:overflow-x-hidden flex flex-col items-stretch gap-4 rounded-xl" class="md:overflow-y-auto md:overflow-x-hidden flex flex-col items-stretch gap-4 rounded-xl"

View File

@@ -7,7 +7,7 @@ export const load: Load = async ({ params, fetch }) => {
if (!params.id) { if (!params.id) {
return error(400, "Bad Request") return error(400, "Bad Request")
} }
const categories = await categories_index() const categories = await categories_index(fetch)
let trail: Trail; let trail: Trail;
if (params.id === "new") { if (params.id === "new") {

View File

@@ -4,13 +4,17 @@
import TrailList from "$lib/components/trail/trail_list.svelte"; import TrailList from "$lib/components/trail/trail_list.svelte";
import type { TrailFilter } from "$lib/models/trail"; import type { TrailFilter } from "$lib/models/trail";
import { categories } from "$lib/stores/category_store"; import { categories } from "$lib/stores/category_store";
import { trails, trails_search_filter } from "$lib/stores/trail_store"; import {
trails,
trails_search_filter,
} from "$lib/stores/trail_store";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
let filterExpanded: boolean = true; let filterExpanded: boolean = true;
const filter: TrailFilter = $page.data.filter; const filter: TrailFilter = $page.data.filter;
const pagination: {page: number, totalPages: number} = $page.data.pagination
onMount(() => { onMount(() => {
if (window.innerWidth < 768) { if (window.innerWidth < 768) {
@@ -19,7 +23,14 @@
}); });
async function handleFilterUpdate() { async function handleFilterUpdate() {
await trails_search_filter(filter); const response= await trails_search_filter(filter, 1);
pagination.page = response.page
pagination.totalPages = response.totalPages;
}
async function paginate(page: number) {
pagination.page = page;
const response = await trails_search_filter(filter, page);
} }
</script> </script>
@@ -39,6 +50,8 @@
<TrailList <TrailList
{filter} {filter}
trails={$trails} trails={$trails}
on:update={async () => await trails_search_filter(filter)} pagination={pagination}
on:update={() => handleFilterUpdate()}
on:pagination={(e) => paginate(e.detail)}
></TrailList> ></TrailList>
</main> </main>

View File

@@ -21,8 +21,10 @@ export const load: ServerLoad = async ({ params, locals, url, fetch }) => {
if (paramCategory) { if (paramCategory) {
filter.category.push(paramCategory); filter.category.push(paramCategory);
} }
await trails_search_filter(filter, fetch); const response = await trails_search_filter(filter, 1, fetch);
await categories_index() await categories_index(fetch)
return {filter}; return {
filter: filter, pagination: { page: response.page, totalPages: response.totalPages }
};
}; };