adds svelte API

This commit is contained in:
Christian Beutel
2024-03-09 16:46:02 +01:00
parent 33c3b273ab
commit 1b493f576e
114 changed files with 1574 additions and 2528 deletions

View File

@@ -2,7 +2,7 @@ import { categories_index } from "$lib/stores/category_store";
import { trails_index } from "$lib/stores/trail_store";
import type { ServerLoad } from "@sveltejs/kit";
export const load: ServerLoad = async ({ params, locals }) => {
await trails_index({perPage: 20, random: true})
export const load: ServerLoad = async ({ params, locals, fetch }) => {
await trails_index({perPage: 20, random: true, f: fetch})
await categories_index()
};

View File

@@ -0,0 +1,13 @@
import { pb } from "$lib/pocketbase";
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').authWithPassword(data.email ?? data.username!, data.password);
return json(r);
} catch (e: any) {
throw error(e.status, e);
}
}

View File

@@ -0,0 +1,28 @@
import type { List } from '$lib/models/list';
import { pb } from '$lib/pocketbase';
import { error, json, type RequestEvent } from '@sveltejs/kit';
export async function GET(event: RequestEvent) {
const sort = event.url.searchParams.get('sort') ?? ""
try {
const r: List[] = await pb.collection('lists').getFullList<List>({
expand: "trails",
sort: sort,
})
return json(r)
} catch (e: any) {
throw error(e.status, e);
}
}
export async function PUT(event: RequestEvent) {
const data = await event.request.json();
try {
const r = await pb.collection('lists').create<List>(data)
return json(r);
} catch (e: any) {
throw error(e.status, e)
}
}

View File

@@ -0,0 +1,23 @@
import { pb } from "$lib/pocketbase";
import { error, json, type RequestEvent } from "@sveltejs/kit";
import type { List } from "postcss/lib/list";
export async function POST(event: RequestEvent) {
const data = await event.request.json()
try {
const r = await pb.collection('lists').update<List>(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('lists').delete(event.params.id as string)
return json(r);
} catch (e: any) {
throw error(e.status, e)
}
}

View File

@@ -0,0 +1,13 @@
import { pb } from "$lib/pocketbase";
import { error, json, type RequestEvent } from "@sveltejs/kit";
import type { List } from "postcss/lib/list";
export async function POST(event: RequestEvent) {
const data = await event.request.formData()
try {
const r = await pb.collection("lists").update<List>(event.params.id as string, data,);
return json(r);
} catch (e: any) {
throw error(e.status, e)
}
}

View File

@@ -0,0 +1,14 @@
import type { SummitLog } from '$lib/models/summit_log';
import { pb } from '$lib/pocketbase';
import { error, json, type RequestEvent } from '@sveltejs/kit';
export async function PUT(event: RequestEvent) {
const data = await event.request.json();
try {
const r = await pb.collection('summit_logs').create<SummitLog>(data)
return json(r);
} catch (e: any) {
throw error(e.status, e)
}
}

View File

@@ -0,0 +1,23 @@
import type { SummitLog } from "$lib/models/summit_log";
import { pb } from "$lib/pocketbase";
import { error, json, type RequestEvent } from "@sveltejs/kit";
export async function POST(event: RequestEvent) {
const data = await event.request.json()
try {
const r = await await pb.collection("summit_logs").update<SummitLog>(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('summit_logs').delete(event.params.id as string)
return json(r);
} catch (e: any) {
throw error(e.status, e)
}
}

View File

@@ -0,0 +1,30 @@
import type { Trail } from '$lib/models/trail';
import { pb } from '$lib/pocketbase';
import { error, json, type RequestEvent } from '@sveltejs/kit';
export async function GET(event: RequestEvent) {
const page = event.url.searchParams.get("page") ?? "0";
const perPage = event.url.searchParams.get("per-page") ?? "5";
const expand = event.url.searchParams.get("expand") ?? ""
const sort = event.url.searchParams.get("sort") ?? ""
const filter = event.url.searchParams.get("filter") ?? "";
try {
const r = await pb.collection('trails')
.getList<Trail>(parseInt(page), parseInt(perPage), { expand: expand ?? "", sort: sort ?? "", filter: filter ?? "" })
return json(r)
} catch (e: any) {
throw error(e.status, e);
}
}
export async function PUT(event: RequestEvent) {
const data = await event.request.json();
try {
const r = await pb.collection('trails').create<Trail>(data)
return json(r);
} catch (e: any) {
throw error(e.status, e)
}
}

View File

@@ -0,0 +1,35 @@
import type { Trail } from "$lib/models/trail";
import { pb } from "$lib/pocketbase";
import { error, json, type RequestEvent } from "@sveltejs/kit";
export async function GET(event: RequestEvent) {
const expand = event.url.searchParams.get("expand") ?? ""
try {
const r = await pb.collection('trails')
.getOne<Trail>(event.params.id as string, { expand: expand ?? "" })
return json(r)
} catch (e: any) {
throw error(e.status, e);
}
}
export async function POST(event: RequestEvent) {
const data = await event.request.json()
try {
const r = await pb.collection("trails").update<Trail>(event.params.id as string, data, { expand: "category,waypoints,summit_logs" });
return json(r);
} catch (e: any) {
throw error(e.status, e)
}
}
export async function DELETE(event: RequestEvent) {
try {
const r = await pb.collection('trails').delete(event.params.id as string)
return json(r);
} catch (e: any) {
throw error(e.status, e)
}
}

View File

@@ -0,0 +1,13 @@
import type { Trail } from "$lib/models/trail";
import { pb } from "$lib/pocketbase";
import { error, json, type RequestEvent } from "@sveltejs/kit";
export async function POST(event: RequestEvent) {
const data = await event.request.formData()
try {
const r = await pb.collection("trails").update<Trail>(event.params.id as string, data,);
return json(r);
} catch (e: any) {
throw error(e.status, e)
}
}

View File

@@ -0,0 +1,33 @@
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('lists').update<User>(data.id, data.user)
return json(r);
} catch (e: any) {
throw error(e.status, e)
}
}
export async function PUT(event: RequestEvent) {
const data = await event.request.json()
try {
const r = await pb.collection('users').create<User>(data)
return json(r);
} catch (e: any) {
throw error(e.status, e)
}
}
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,14 @@
import type { Waypoint } from '$lib/models/waypoint';
import { pb } from '$lib/pocketbase';
import { error, json, type RequestEvent } from '@sveltejs/kit';
export async function PUT(event: RequestEvent) {
const data = await event.request.json();
try {
const r = await pb.collection('waypoints').create<Waypoint>(data)
return json(r);
} catch (e: any) {
throw error(e.status, e)
}
}

View File

@@ -0,0 +1,23 @@
import type { Waypoint } from "$lib/models/waypoint";
import { pb } from "$lib/pocketbase";
import { error, json, type RequestEvent } from "@sveltejs/kit";
export async function POST(event: RequestEvent){
const data = await event.request.json()
try {
const r = await await pb.collection("waypoints").update<Waypoint>(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('waypoints').delete(event.params.id as string)
return json(r);
} catch (e: any) {
throw error(e.status, e)
}
}

View File

@@ -30,16 +30,16 @@
}
async function saveList(
e: CustomEvent<{ list: List; formData: FormData }>,
e: CustomEvent<{ list: List; avatar?: File }>,
) {
const result = e.detail;
if (result.list.id) {
await lists_update(result.list, result.formData);
await lists_index();
await lists_update(result.list, result.avatar);
} else {
await lists_create(result.formData);
await lists_index();
await lists_create(result.list, result.avatar);
}
await lists_index();
}
async function handleDropdownClick(

View File

@@ -2,7 +2,7 @@ import type { TrailFilter } from "$lib/models/trail";
import { lists_index } from "$lib/stores/list_store";
import type { Load } from "@sveltejs/kit";
export const load: Load = async ({ params }) => {
export const load: Load = async ({ params, fetch }) => {
const filter: TrailFilter = {
q: "",
category: [],
@@ -16,7 +16,7 @@ export const load: Load = async ({ params }) => {
sort: "created",
sortOrder: "+",
};
await lists_index();
await lists_index(undefined, fetch);
return {filter};
};

View File

@@ -205,7 +205,7 @@
<li class="flex items-center gap-4 cursor-pointer text-black">
<div class="shrink-0"><img class="h-14 w-14 object-cover rounded-xl" src="${getFileURL(
trail,
trail.thumbnail,
trail.photos[trail.thumbnail],
)}" alt="">
</div>
<div>
@@ -263,7 +263,7 @@
extraClasses="w-full"
on:update={(e) => search(e.detail)}
on:click={(e) => handleSearchClick(e.detail)}
placeholder="{$_("search-for-trails-places")}..."
placeholder="{$_('search-for-trails-places')}..."
items={searchDropdownItems}
></Search>
<button

View File

@@ -37,7 +37,7 @@
onMount(() => {
lightboxDataSource = $trail.photos.map((p) => ({
src: p,
src: getFileURL($trail, p),
}));
lightbox = new PhotoSwipeLightbox({
dataSource: lightboxDataSource,
@@ -83,7 +83,7 @@
<section class="relative h-80">
<img
class="w-full h-80 object-cover"
src={getFileURL($trail, $trail.thumbnail)}
src={getFileURL($trail, $trail.photos[$trail.thumbnail])}
alt=""
/>
<div
@@ -152,14 +152,14 @@
</ul>
{/if}
{#if activeTab == 2}
<div id="photo-gallery" class="space-y-4">
<div id="photo-gallery" class="space-y-4 mx-4">
{#each $trail.photos ?? [] as photo, i}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<img
class="rounded-xl cursor-pointer hover:scale-105 transition-transform"
on:click={() => openGallery(i)}
src={photo}
src={getFileURL($trail, photo)}
alt=""
/>
{/each}

View File

@@ -1,10 +1,11 @@
import { trails, trails_show } from "$lib/stores/trail_store";
import { lists_index } from "$lib/stores/list_store";
import { trails_show } from "$lib/stores/trail_store";
import { error, type ServerLoad } from "@sveltejs/kit";
import { ClientResponseError } from "pocketbase";
export const load: ServerLoad = async ({ params, locals }) => {
export const load: ServerLoad = async ({ params, locals, fetch }) => {
try {
await trails_show(params.id!, true)
await trails_show(params.id!, true, fetch)
} catch (e) {
if (e instanceof ClientResponseError && e.status == 404) {
error(404, {
@@ -13,4 +14,5 @@ export const load: ServerLoad = async ({ params, locals }) => {
}
}
await lists_index(undefined, fetch);
};

View File

@@ -22,6 +22,7 @@
trails_update,
} from "$lib/stores/trail_store";
import { waypoint } from "$lib/stores/waypoint_store";
import { getFileURL } from "$lib/util/file_util";
import {
formatDistance,
formatElevation,
@@ -50,6 +51,11 @@
let loading = false;
const photoFiles: File[] = [];
let photoPreviews: string[] = [];
let gpxFile: File | null = null;
onMount(async () => {
L = (await import("leaflet")).default;
await import("leaflet-gpx");
@@ -86,14 +92,21 @@
"trail-form",
) as HTMLFormElement;
const formData = new FormData(htmlForm);
if (!formData.get("public")) {
submittedTrail.public = false;
}
submittedTrail.photos = submittedTrail.photos.filter(
(p) => !p.startsWith("data:image/svg+xml;base64"),
);
if (!submittedTrail.id) {
const createdTrail = await trails_create(
submittedTrail,
formData,
photoFiles,
gpxFile,
);
$form.id = createdTrail.id;
} else {
await trails_update($trail, submittedTrail, formData);
await trails_update($trail, submittedTrail, photoFiles, gpxFile);
}
show_toast({
@@ -199,8 +212,9 @@
return;
}
$form.gpx = selectedFile?.name;
gpxFile = selectedFile;
$form.expand.waypoints = [];
$form.waypoints = [];
var reader = new FileReader();
@@ -235,6 +249,7 @@
} else if (e.detail.value === "delete") {
currentWaypoint.marker?.remove();
$form.expand.waypoints.splice(index, 1);
$form.waypoints.splice(index, 1);
$form.expand.waypoints = $form.expand.waypoints;
}
}
@@ -278,17 +293,14 @@
}
for (const file of files) {
$form._photoFiles.push(file);
photoFiles.push(file);
(function (file) {
var reader = new FileReader();
reader.onload = function (e) {
if (e.target?.result) {
if ($form.photos.length == 0) {
$form.thumbnail = e.target.result as string;
}
$form.photos = [
...$form.photos,
photoPreviews = [
...photoPreviews,
e.target.result as string,
];
}
@@ -299,20 +311,23 @@
}
function makePhotoThumbnail(index: number) {
$form.thumbnail = $form.photos[index];
$form.thumbnail = index;
}
function handlePhotoDelete(index: number) {
const photoToDelete = $form.photos[index];
let reassignThumbnail: boolean = false;
if ($form.thumbnail == photoToDelete) {
reassignThumbnail = true;
if ($form.thumbnail == index) {
$form.thumbnail = 0;
console.log("huere");
}
$form.photos.splice(index, 1);
$form._photoFiles.splice(index, 1);
$form.photos = $form.photos;
if (reassignThumbnail) {
$form.thumbnail = $form.photos[0];
if (index >= $form.photos.length) {
const adjustedIndex = index - $form.photos.length;
photoFiles.splice(adjustedIndex, 1);
photoPreviews.splice(adjustedIndex, 1);
} else {
$form.photos.splice(index, 1);
$form.photos = $form.photos;
}
}
@@ -350,6 +365,7 @@
openSummitLogModal();
} else if (e.detail.value === "delete") {
$form.expand.summit_logs.splice(index, 1);
$form.summit_logs.splice(index, 1);
$form.expand.summit_logs = $form.expand.summit_logs;
}
}
@@ -383,7 +399,7 @@
<h3 class="text-xl font-semibold">{$_("basic-info")}</h3>
<div class="flex gap-4 justify-around">
<div class="flex flex-col items-center">
<span>{$_('distance')}</span>
<span>{$_("distance")}</span>
<span class="font-medium">{formatDistance($form.distance)}</span
>
<input type="hidden" name="distance" value={$form.distance} />
@@ -430,7 +446,7 @@
<Select
name="category"
label={$_("category")}
bind:value={$form.expand.category.id}
bind:value={$form.category}
items={$categories.map((c) => ({ text: c.name, value: c.id }))}
></Select>
{/if}
@@ -472,19 +488,19 @@
style="display: none;"
on:change={handlePhotoSelection}
/>
{#each $form.photos ?? [] as photo, i}
{#each ($form.photos ?? []).concat(photoPreviews) as photo, i}
<div class="shrink-0 grow-0 basis-auto m-2">
<PhotoCard
src={photo}
src={i >= $form.photos.length ? photo : getFileURL($form, photo)}
on:delete={() => handlePhotoDelete(i)}
isThumbnail={$form.thumbnail === photo}
isThumbnail={$form.thumbnail === i}
on:thumbnail={() => makePhotoThumbnail(i)}
></PhotoCard>
</div>
{/each}
</div>
<hr class="border-separator" />
<h3 class="text-xl font-semibold">{$_('summit-book')}</h3>
<h3 class="text-xl font-semibold">{$_("summit-book")}</h3>
<ul>
{#each $form.expand.summit_logs ?? [] as log, i}
<li>

View File

@@ -3,7 +3,7 @@ import { categories_index } from "$lib/stores/category_store";
import { trails_show } from "$lib/stores/trail_store";
import { error, type Load } from "@sveltejs/kit";
export const load: Load = async ({ params }) => {
export const load: Load = async ({ params, fetch }) => {
if (!params.id) {
return error(400, "Bad Request")
}
@@ -11,9 +11,9 @@ export const load: Load = async ({ params }) => {
let trail: Trail;
if (params.id === "new") {
trail = new Trail("", {category: categories[0]});
trail = new Trail("", { category: categories[0] });
} else {
trail = await trails_show(params.id, true);
trail = await trails_show(params.id, true, fetch);
}
return { trail: trail }

View File

@@ -84,7 +84,7 @@
}
lightboxDataSource = $trail.photos.map((p) => ({
src: p,
src: getFileURL($trail, p),
}));
lightbox = new PhotoSwipeLightbox({
dataSource: lightboxDataSource,
@@ -134,7 +134,7 @@
<section class="relative h-80">
<img
class="w-full h-80"
src={getFileURL($trail, $trail.thumbnail)}
src={getFileURL($trail, $trail.photos[$trail.thumbnail])}
alt=""
/>
<div
@@ -225,7 +225,7 @@
<img
class="rounded-xl cursor-pointer hover:scale-105 transition-transform"
on:click={() => openGallery(i)}
src={photo}
src={getFileURL($trail, photo)}
alt=""
/>
{/each}

View File

@@ -3,9 +3,9 @@ import { trails_show } from "$lib/stores/trail_store";
import { error, type Load } from "@sveltejs/kit";
import { ClientResponseError } from "pocketbase";
export const load: Load = async ({ params }) => {
export const load: Load = async ({ params, fetch }) => {
try {
await trails_show(params.id!, true)
await trails_show(params.id!, true, fetch)
} catch (e) {
if (e instanceof ClientResponseError && e.status == 404) {
error(404, {
@@ -13,5 +13,6 @@ export const load: Load = async ({ params }) => {
});
}
} await lists_index();
}
await lists_index(undefined, fetch);
};

View File

@@ -3,7 +3,7 @@ import { categories_index } from "$lib/stores/category_store";
import { trails_search_filter } from "$lib/stores/trail_store";
import type { ServerLoad } from "@sveltejs/kit";
export const load: ServerLoad = async ({ params, locals, url }) => {
export const load: ServerLoad = async ({ params, locals, url, fetch }) => {
const filter: TrailFilter = {
q: "",
category: [],
@@ -21,7 +21,7 @@ export const load: ServerLoad = async ({ params, locals, url }) => {
if (paramCategory) {
filter.category.push(paramCategory);
}
await trails_search_filter(filter);
await trails_search_filter(filter, fetch);
await categories_index()
return {filter};