1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-08-02 19:55:18 +02:00

fix: update RegionCard component to handle undefined visited state and refactor global search API to return structured results

This commit is contained in:
Sean Morley 2025-01-18 12:57:56 -05:00
parent d60945d5b7
commit f10e171a8e
4 changed files with 129 additions and 224 deletions

View file

@ -7,7 +7,7 @@
import { t } from 'svelte-i18n';
export let region: Region;
export let visited: boolean;
export let visited: boolean | undefined;
function goToCity() {
console.log(region);
@ -64,12 +64,12 @@
</div>
<div class="card-actions justify-end">
<!-- <button class="btn btn-info" on:click={moreInfo}>More Info</button> -->
{#if !visited}
{#if !visited && visited !== undefined}
<button class="btn btn-primary" on:click={markVisited}
>{$t('adventures.mark_visited')}</button
>
{/if}
{#if visited}
{#if visited && visited !== undefined}
<button class="btn btn-warning" on:click={removeVisit}>{$t('adventures.remove')}</button>
{/if}
{#if region.num_cities > 0}

View file

@ -8,7 +8,6 @@ const serverEndpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
export const load = (async (event) => {
const query = event.url.searchParams.get('query');
const property = event.url.searchParams.get('property') || 'all';
if (!query) {
return { data: [] };
@ -16,15 +15,12 @@ export const load = (async (event) => {
let sessionId = event.cookies.get('sessionid');
let res = await fetch(
`${serverEndpoint}/api/adventures/search/?query=${query}&property=${property}`,
{
headers: {
'Content-Type': 'application/json',
Cookie: `sessionid=${sessionId}`
}
let res = await fetch(`${serverEndpoint}/api/search/?query=${query}`, {
headers: {
'Content-Type': 'application/json',
Cookie: `sessionid=${sessionId}`
}
);
});
if (!res.ok) {
console.error('Failed to fetch search data');
@ -32,27 +28,16 @@ export const load = (async (event) => {
return { error: error.error };
}
let adventures: Adventure[] = await res.json();
let osmRes = await fetch(`https://nominatim.openstreetmap.org/search?q=${query}&format=jsonv2`, {
headers: {
'User-Agent': `AdventureLog / ${appVersion} `
}
});
if (!osmRes.ok) {
console.error('Failed to fetch OSM data');
let error = await res.json();
return { error: error.error };
}
let osmData = (await osmRes.json()) as OpenStreetMapPlace[];
let data = await res.json();
return {
props: {
adventures,
query,
osmData
}
adventures: data.adventures,
collections: data.collections,
users: data.users,
countries: data.countries,
regions: data.regions,
cities: data.cities,
visited_cities: data.visited_cities,
visited_regions: data.visited_regions
};
}) satisfies PageServerLoad;

View file

@ -1,184 +1,102 @@
<script lang="ts">
import AdventureCard from '$lib/components/AdventureCard.svelte';
import NotFound from '$lib/components/NotFound.svelte';
import type { Adventure, OpenStreetMapPlace } from '$lib/types';
import { onMount } from 'svelte';
import type { PageData } from './$types';
import { goto } from '$app/navigation';
import AdventureModal from '$lib/components/AdventureModal.svelte';
import { t } from 'svelte-i18n';
import RegionCard from '$lib/components/RegionCard.svelte';
import CityCard from '$lib/components/CityCard.svelte';
import CountryCard from '$lib/components/CountryCard.svelte';
import CollectionCard from '$lib/components/CollectionCard.svelte';
import UserCard from '$lib/components/UserCard.svelte';
export let data: PageData;
function deleteAdventure(event: CustomEvent<string>) {
myAdventures = myAdventures.filter((adventure) => adventure.id !== event.detail);
}
let adventures = data.adventures;
let collections = data.collections;
let users = data.users;
let countries = data.countries;
let regions = data.regions;
let cities = data.cities;
let osmResults: OpenStreetMapPlace[] = [];
let myAdventures: Adventure[] = [];
let publicAdventures: Adventure[] = [];
let visited_regions: { region: any }[] = data.visited_regions;
let visited_cities: { city: any }[] = data.visited_cities;
let query: string | null = '';
let property: string = 'all';
// on chage of property, console log the property
function filterByProperty() {
let url = new URL(window.location.href);
url.searchParams.set('property', property);
goto(url.toString(), { invalidateAll: true });
}
onMount(() => {
const urlParams = new URLSearchParams(window.location.search);
query = urlParams.get('query');
});
console.log(data);
$: {
if (data.props) {
myAdventures = data.props.adventures;
publicAdventures = data.props.adventures;
if (data.user?.uuid != null) {
myAdventures = myAdventures.filter((adventure) => adventure.user_id === data.user?.uuid);
} else {
myAdventures = [];
}
publicAdventures = publicAdventures.filter(
(adventure) => adventure.user_id !== data.user?.uuid
);
if (data.props.osmData) {
osmResults = data.props.osmData;
}
}
}
let adventureToEdit: Adventure;
let isAdventureModalOpen: boolean = false;
function editAdventure(event: CustomEvent<Adventure>) {
adventureToEdit = event.detail;
isAdventureModalOpen = true;
}
function saveEdit(event: CustomEvent<Adventure>) {
console.log(event.detail);
myAdventures = myAdventures.map((adventure) => {
if (adventure.id === event.detail.id) {
return event.detail;
}
return adventure;
});
isAdventureModalOpen = false;
console.log(myAdventures);
}
</script>
{#if isAdventureModalOpen}
<AdventureModal
{adventureToEdit}
on:close={() => (isAdventureModalOpen = false)}
on:save={filterByProperty}
/>
{/if}
<h1 class="text-4xl font-bold text-center m-4">Search{query ? `: ${query}` : ''}</h1>
{#if myAdventures.length === 0 && osmResults.length === 0}
<NotFound error={data.error} />
{/if}
{#if myAdventures.length !== 0}
<h2 class="text-center font-bold text-2xl mb-4">{$t('search.adventurelog_results')}</h2>
<div class="flex items-center justify-center mt-2 mb-2">
<div class="join">
<input
class="join-item btn"
type="radio"
name="filter"
aria-label={$t('adventures.all')}
id="all"
checked
on:change={() => (property = 'all')}
/>
<input
class="join-item btn"
type="radio"
name="filter"
aria-label={$t('adventures.name')}
id="name"
on:change={() => (property = 'name')}
/>
<input
class="join-item btn"
type="radio"
name="filter"
aria-label={$t('adventures.location')}
id="location"
on:change={() => (property = 'location')}
/>
<input
class="join-item btn"
type="radio"
name="filter"
aria-label={$t('adventures.description')}
id="description"
on:change={() => (property = 'description')}
/>
<input
class="join-item btn"
type="radio"
name="filter"
aria-label={$t('adventures.tags')}
id="activity_types"
on:change={() => (property = 'activity_types')}
/>
</div>
<button class="btn btn-primary ml-2" type="button" on:click={filterByProperty}
>{$t('adventures.filter')}</button
>
{#if adventures.length > 0}
<h2 class="text-3xl font-bold text-center m-4">Adventures</h2>
<div class="flex flex-wrap gap-4 mr-4 ml-4 justify-center content-center">
{#each adventures as adventure}
<AdventureCard {adventure} user={data.user} />
{/each}
</div>
{/if}
{#if myAdventures.length > 0}
<h2 class="text-center font-bold text-2xl mb-4">{$t('adventures.my_adventures')}</h2>
<div class="flex flex-wrap gap-4 mr-4 justify-center content-center">
{#each myAdventures as adventure}
<AdventureCard
user={data.user}
{adventure}
on:delete={deleteAdventure}
on:edit={editAdventure}
{#if collections.length > 0}
<h2 class="text-3xl font-bold text-center m-4">Collections</h2>
<div class="flex flex-wrap gap-4 mr-4 ml-4 justify-center content-center">
{#each collections as collection}
<CollectionCard {collection} type={''} />
{/each}
</div>
{/if}
{#if countries.length > 0}
<h2 class="text-3xl font-bold text-center m-4">Countries</h2>
<div class="flex flex-wrap gap-4 mr-4 ml-4 justify-center content-center">
{#each countries as country}
<CountryCard {country} />
{/each}
</div>
{/if}
{#if regions.length > 0}
<h2 class="text-3xl font-bold text-center m-4">Regions</h2>
<div class="flex flex-wrap gap-4 mr-4 ml-4 justify-center content-center">
{#each regions as region}
<RegionCard
{region}
visited={visited_regions.some((visitedRegion) => visitedRegion.region === region.id)}
/>
{/each}
</div>
{/if}
{#if publicAdventures.length > 0}
<h2 class="text-center font-bold text-2xl mb-4">{$t('search.public_adventures')}</h2>
<div class="flex flex-wrap gap-4 mr-4 justify-center content-center">
{#each publicAdventures as adventure}
<AdventureCard user={null} {adventure} on:delete={deleteAdventure} on:edit={editAdventure} />
{#if cities.length > 0}
<h2 class="text-3xl font-bold text-center m-4">Cities</h2>
<div class="flex flex-wrap gap-4 mr-4 ml-4 justify-center content-center">
{#each cities as city}
<CityCard
{city}
visited={visited_cities.some((visitedCity) => visitedCity.city === city.id)}
/>
{/each}
</div>
{/if}
{#if myAdventures.length > 0 && osmResults.length > 0 && publicAdventures.length > 0}
<div class="divider"></div>
{/if}
{#if osmResults.length > 0}
<h2 class="text-center font-bold mt-2 text-2xl mb-4">{$t('search.online_results')}</h2>
<div class="flex flex-wrap gap-4 mr-4 justify-center content-center">
{#each osmResults as result}
<div class="bg-base-300 rounded-lg shadow-md p-4 w-96 mb-2">
<h2 class="text-xl font-bold">{result.display_name}</h2>
<p>{result.type}</p>
<p>{result.lat}, {result.lon}</p>
</div>
{#if users.length > 0}
<h2 class="text-3xl font-bold text-center m-4">Users</h2>
<div class="flex flex-wrap gap-4 mr-4 ml-4 justify-center content-center">
{#each users as user}
<UserCard {user} />
{/each}
</div>
{/if}
{#if adventures.length === 0 && regions.length === 0 && cities.length === 0 && countries.length === 0 && collections.length === 0 && users.length === 0}
<p class="text-center text-lg m-4">
{$t('adventures.no_results')}
</p>
{/if}
<svelte:head>
<title>Search{query ? `: ${query}` : ''}</title>
<meta name="description" content="Search your adventures." />