1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-07-21 13:59:36 +02:00

refactor: Improve adventure card UI and collection functionality

This commit is contained in:
Sean Morley 2024-08-01 11:01:27 -04:00
parent b2ceda819c
commit c5cbea1c20
3 changed files with 249 additions and 63 deletions

View file

@ -226,7 +226,7 @@
> >
{/if} {/if}
<!-- remove from adventure --> <!-- remove from adventure -->
{#if (adventure.collection && adventure.type == 'visited') || adventure.type == 'planned'} {#if adventure.collection && (adventure.type == 'visited' || adventure.type == 'planned')}
<button class="btn btn-neutral mb-2" on:click={removeFromCollection} <button class="btn btn-neutral mb-2" on:click={removeFromCollection}
><LinkVariantRemove class="w-6 h-6" />Remove from Collection</button ><LinkVariantRemove class="w-6 h-6" />Remove from Collection</button
> >

View file

@ -1,6 +1,6 @@
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL']; const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
import type { Adventure } from '$lib/types'; import type { Adventure, Collection } from '$lib/types';
const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000'; const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
export const load = (async (event) => { export const load = (async (event) => {
@ -19,10 +19,21 @@ export const load = (async (event) => {
}; };
} else { } else {
let adventure = (await request.json()) as Adventure; let adventure = (await request.json()) as Adventure;
let collection: Collection | null = null;
if (adventure.collection) {
let res2 = await fetch(`${endpoint}/api/collections/${adventure.collection}/`, {
headers: {
Cookie: `${event.cookies.get('auth')}`
}
});
collection = await res2.json();
}
return { return {
props: { props: {
adventure adventure,
collection
} }
}; };
} }

View file

@ -19,8 +19,10 @@
import type { PageData } from './$types'; import type { PageData } from './$types';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import Lost from '$lib/assets/undraw_lost.svg'; import Lost from '$lib/assets/undraw_lost.svg';
import { DefaultMarker, MapLibre, Popup } from 'svelte-maplibre';
export let data: PageData; export let data: PageData;
console.log(data);
let adventure: Adventure; let adventure: Adventure;
@ -62,66 +64,239 @@
<span class="loading loading-spinner w-24 h-24"></span> <span class="loading loading-spinner w-24 h-24"></span>
</div> </div>
{/if} {/if}
{#if adventure} {#if adventure}
{#if adventure.name} <div class="flex flex-col min-h-dvh">
<h1 class="text-center font-extrabold text-4xl mb-2">{adventure.name}</h1> <main class="flex-1">
{/if} <div class="max-w-5xl mx-auto p-4 md:p-6 lg:p-8">
{#if adventure.location} <div class="grid gap-8">
<p class="text-center text-2xl"> {#if adventure.image}
<iconify-icon icon="mdi:map-marker" class="text-xl -mb-0.5" <div>
></iconify-icon>{adventure.location} <img
</p> src={adventure.image}
{/if} alt={adventure.name}
{#if adventure.date} width="1200"
<p class="text-center text-lg mt-4 pl-16 pr-16"> height="600"
Visited on: {adventure.date} class="w-full h-auto object-cover rounded-lg"
</p> style="aspect-ratio: 1200 / 600; object-fit: cover;"
{/if} />
{#if adventure.rating !== undefined && adventure.rating !== null} </div>
<div class="flex justify-center items-center"> {/if}
<div class="rating" aria-readonly="true"> <div class="grid gap-4">
{#each Array.from({ length: 5 }, (_, i) => i + 1) as star} <div class="flex items-center justify-between">
<input <div>
type="radio" <h1 class="text-4xl mt-2 font-bold">{adventure.name}</h1>
name="rating-1" </div>
class="mask mask-star"
checked={star <= adventure.rating} <div class="flex items-center gap-1">
disabled {#if adventure.rating !== undefined && adventure.rating !== null}
/> <div class="flex justify-center items-center">
{/each} <div class="rating" aria-readonly="true">
</div> {#each Array.from({ length: 5 }, (_, i) => i + 1) as star}
</div> <input
{/if} type="radio"
{#if adventure.description} name="rating-1"
<p class="text-center text-lg mt-4 pl-16 pr-16">{adventure.description}</p> class="mask mask-star"
{/if} checked={star <= adventure.rating}
{#if adventure.link} disabled
<div class="flex justify-center items-center mt-4"> />
<a href={adventure.link} target="_blank" rel="noopener noreferrer" class="btn btn-primary"> {/each}
Visit Website </div>
</a> </div>
</div> {/if}
{/if} </div>
{#if adventure.activity_types && adventure.activity_types.length > 0} </div>
<div class="flex justify-center items-center mt-4"> <div class="grid gap-2">
<p class="text-center text-lg">Activities:&nbsp</p> <div class="flex items-center gap-2">
<ul class="flex flex-wrap"> <svg
{#each adventure.activity_types as activity} xmlns="http://www.w3.org/2000/svg"
<div class="badge badge-primary mr-1 text-md font-semibold pb-2 pt-1 mb-1"> width="24"
{activity} height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="w-5 h-5 text-muted-foreground"
>
<rect width="18" height="11" x="3" y="11" rx="2" ry="2"></rect>
<path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
</svg>
<span class="text-sm text-muted-foreground"
>{adventure.is_public ? 'Public' : 'Private'}</span
>
</div>
{#if adventure.date}
<div class="flex items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="w-5 h-5 text-muted-foreground"
>
<path d="M8 2v4"></path>
<path d="M16 2v4"></path>
<rect width="18" height="18" x="3" y="4" rx="2"></rect>
<path d="M3 10h18"></path>
</svg>
<span class="text-sm text-muted-foreground"
>{new Date(adventure.date).toLocaleDateString()}</span
>
</div>
{/if}
{#if adventure.location}
<div class="flex items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="w-5 h-5 text-muted-foreground"
>
<path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z"></path>
<circle cx="12" cy="10" r="3"></circle>
</svg>
<span class="text-sm text-muted-foreground">{adventure.location}</span>
</div>
{/if}
{#if adventure.activity_types}
<div class="flex items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="w-5 h-5 text-muted-foreground"
>
<path
d="M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2"
></path>
</svg>
<span class="text-sm text-muted-foreground"
>{adventure.activity_types.join(', ')}</span
>
</div>
{/if}
{#if adventure.link}
<div class="flex items-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="w-5 h-5 text-muted-foreground"
>
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
</svg>
<a
href={adventure.link}
class="text-sm text-muted-foreground hover:underline"
target="_blank"
>
{adventure.link}
</a>
</div>
{/if}
</div>
{#if adventure.description}
<div class="grid gap-2">
<p class="text-sm text-muted-foreground">
{adventure.description}
</p>
</div>
{/if}
</div> </div>
{/each} </div>
</ul> <div
</div> data-orientation="horizontal"
{/if} role="none"
{#if adventure.image} class="shrink-0 bg-border h-[1px] w-full my-8"
<div class="flex content-center justify-center"> ></div>
<!-- svelte-ignore a11y-img-redundant-alt --> <div class="grid gap-8">
<img <div>
src={adventure.image} <h2 class="text-2xl font-bold mt-4">Trip Details</h2>
alt="Adventure Image" <div class="grid gap-4 mt-4">
class="w-1/2 mt-4 align-middle rounded-lg shadow-lg" <div class="grid md:grid-cols-2 gap-4">
/> <div>
</div> <p class="text-sm text-muted-foreground">Trip Type</p>
{/if} <p class="text-base font-medium">
{adventure.type[0].toLocaleUpperCase() + adventure.type.slice(1)}
</p>
</div>
{#if data.props.collection}
<div>
<p class="text-sm text-muted-foreground">Collection</p>
<p class="text-base font-medium">{data.props.collection.name}</p>
</div>
{/if}
</div>
{#if adventure.longitude && adventure.latitude}
<div class="grid md:grid-cols-2 gap-4">
<div>
<p class="text-sm text-muted-foreground">Latitude</p>
<p class="text-base font-medium">{adventure.latitude}° N</p>
</div>
<div>
<p class="text-sm text-muted-foreground">Longitude</p>
<p class="text-base font-medium">{adventure.longitude}° W</p>
</div>
</div>
<MapLibre
style="https://basemaps.cartocdn.com/gl/positron-gl-style/style.json"
class="flex items-center self-center justify-center aspect-[9/16] max-h-[70vh] sm:aspect-video sm:max-h-full w-10/12"
standardControls
center={{ lng: adventure.longitude, lat: adventure.latitude }}
zoom={12}
>
<!-- MapEvents gives you access to map events even from other components inside the map,
where you might not have access to the top-level `MapLibre` component. In this case
it would also work to just use on:click on the MapLibre component itself. -->
<!-- <MapEvents on:click={addMarker} /> -->
<DefaultMarker lngLat={{ lng: adventure.longitude, lat: adventure.latitude }}>
<Popup openOn="click" offset={[0, -10]}>
<div class="text-lg text-black font-bold">{adventure.name}</div>
<p class="font-semibold text-black text-md">
{adventure.type.charAt(0).toUpperCase() + adventure.type.slice(1)}
</p>
<p>
{adventure.date
? new Date(adventure.date).toLocaleDateString('en-US', {
timeZone: 'UTC'
})
: ''}
</p>
</Popup>
</DefaultMarker>
</MapLibre>
{/if}
</div>
</div>
</div>
</div>
</main>
</div>
{/if} {/if}