mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-07-24 15:29:36 +02:00
Refactor map page
This commit is contained in:
parent
e6c5bc9ca8
commit
07263c5697
7 changed files with 127 additions and 182 deletions
|
@ -1162,4 +1162,3 @@ class ReverseGeocodeViewSet(viewsets.ViewSet):
|
|||
new_region_count += 1
|
||||
new_regions[region.id] = region.name
|
||||
return Response({"new_regions": new_region_count, "regions": new_regions})
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ services:
|
|||
- DEBUG=False
|
||||
- FRONTEND_URL='http://localhost:8015' # Used for email generation. This should be the url of the frontend
|
||||
ports:
|
||||
- "8016:80" # User can change this to any outward port without breaking the setup
|
||||
- "8016:80"
|
||||
depends_on:
|
||||
- db
|
||||
volumes:
|
||||
|
|
|
@ -70,7 +70,8 @@
|
|||
images: adventureToEdit?.images || [],
|
||||
user_id: adventureToEdit?.user_id || null,
|
||||
collection: adventureToEdit?.collection || collection?.id || null,
|
||||
visits: adventureToEdit?.visits || []
|
||||
visits: adventureToEdit?.visits || [],
|
||||
is_visited: adventureToEdit?.is_visited || false
|
||||
};
|
||||
|
||||
let markers: Point[] = [];
|
||||
|
|
|
@ -253,6 +253,39 @@ export let ADVENTURE_TYPES = [
|
|||
{ type: 'other', label: 'Other' }
|
||||
];
|
||||
|
||||
// adventure type to icon mapping
|
||||
export let ADVENTURE_TYPE_ICONS = {
|
||||
general: '🌍',
|
||||
outdoor: '🏞️',
|
||||
lodging: '🛌',
|
||||
dining: '🍽️',
|
||||
activity: '🏄',
|
||||
attraction: '🎢',
|
||||
shopping: '🛍️',
|
||||
nightlife: '🌃',
|
||||
event: '🎉',
|
||||
transportation: '🚗',
|
||||
culture: '🎭',
|
||||
water_sports: '🚤',
|
||||
hiking: '🥾',
|
||||
wildlife: '🦒',
|
||||
historical_sites: '🏛️',
|
||||
music_concerts: '🎶',
|
||||
fitness: '🏋️',
|
||||
art_museums: '🎨',
|
||||
festivals: '🎪',
|
||||
spiritual_journeys: '🧘♀️',
|
||||
volunteer_work: '🤝',
|
||||
other: '❓'
|
||||
};
|
||||
|
||||
type AdventureType = keyof typeof ADVENTURE_TYPE_ICONS;
|
||||
|
||||
export function getAdventureTypeLabel(type: AdventureType) {
|
||||
const typeObj = ADVENTURE_TYPE_ICONS[type];
|
||||
return typeObj;
|
||||
}
|
||||
|
||||
export function getRandomBackground() {
|
||||
const randomIndex = Math.floor(Math.random() * randomBackgrounds.backgrounds.length);
|
||||
return randomBackgrounds.backgrounds[randomIndex] as Background;
|
||||
|
|
|
@ -63,6 +63,9 @@ export type VisitedRegion = {
|
|||
id: number;
|
||||
region: number;
|
||||
user_id: number;
|
||||
longitude: number;
|
||||
latitude: number;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type Point = {
|
||||
|
|
|
@ -19,43 +19,21 @@ export const load = (async (event) => {
|
|||
Cookie: `${event.cookies.get('auth')}`
|
||||
}
|
||||
});
|
||||
let visitedRegions = (await visitedRegionsFetch.json()) as VisitedRegion[];
|
||||
|
||||
if (!visitedFetch.ok) {
|
||||
let visitedRegions = (await visitedRegionsFetch.json()) as VisitedRegion[];
|
||||
let adventures = (await visitedFetch.json()) as Adventure[];
|
||||
|
||||
if (!visitedRegionsFetch.ok) {
|
||||
console.error('Failed to fetch visited regions');
|
||||
return redirect(302, '/login');
|
||||
} else if (!visitedFetch.ok) {
|
||||
console.error('Failed to fetch visited adventures');
|
||||
return redirect(302, '/login');
|
||||
} else {
|
||||
let visited: Adventure[] = [];
|
||||
try {
|
||||
let api_result = await visitedFetch.json();
|
||||
visited = api_result as Adventure[];
|
||||
if (!Array.isArray(visited) || visited.length === 0 || !visited) {
|
||||
throw new Error('Visited adventures response is not an array');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error parsing visited adventures:', error);
|
||||
return redirect(302, '/login');
|
||||
}
|
||||
|
||||
// make a long lat array like this { lngLat: [-20, 0], name: 'Adventure 1' },
|
||||
let markers = visited
|
||||
.filter((adventure) => adventure.latitude !== null && adventure.longitude !== null)
|
||||
.map((adventure) => {
|
||||
return {
|
||||
lngLat: [adventure.longitude, adventure.latitude],
|
||||
name: adventure.name,
|
||||
visits: adventure.visits,
|
||||
type: adventure.type,
|
||||
is_visited: adventure.is_visited
|
||||
};
|
||||
});
|
||||
|
||||
console.log('sent');
|
||||
|
||||
return {
|
||||
props: {
|
||||
markers,
|
||||
visitedRegions
|
||||
visitedRegions,
|
||||
adventures
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,91 +1,53 @@
|
|||
<script>
|
||||
// @ts-nocheck
|
||||
|
||||
<script lang="ts">
|
||||
import AdventureModal from '$lib/components/AdventureModal.svelte';
|
||||
import {
|
||||
DefaultMarker,
|
||||
MapEvents,
|
||||
MapLibre,
|
||||
Popup,
|
||||
Marker,
|
||||
GeoJSON,
|
||||
LineLayer,
|
||||
FillLayer,
|
||||
SymbolLayer
|
||||
} from 'svelte-maplibre';
|
||||
import { DefaultMarker, MapEvents, MapLibre, Popup, Marker } from 'svelte-maplibre';
|
||||
import { t } from 'svelte-i18n';
|
||||
import type { Adventure, VisitedRegion } from '$lib/types.js';
|
||||
export let data;
|
||||
|
||||
let clickedName = '';
|
||||
let createModalOpen: boolean = false;
|
||||
let showGeo: boolean = false;
|
||||
|
||||
let visitedRegions: VisitedRegion[] = data.props.visitedRegions;
|
||||
let adventures: Adventure[] = data.props.adventures;
|
||||
|
||||
let filteredAdventures = adventures;
|
||||
|
||||
// Updates the filtered adventures based on the checkboxes
|
||||
$: {
|
||||
filteredAdventures = adventures.filter(
|
||||
(adventure) => (showVisited && adventure.is_visited) || (showPlanned && !adventure.is_visited)
|
||||
);
|
||||
}
|
||||
|
||||
console.log(data);
|
||||
|
||||
let showVisited = true;
|
||||
let showPlanned = true;
|
||||
let showVisited: boolean = true;
|
||||
let showPlanned: boolean = true;
|
||||
|
||||
$: filteredMarkers = markers.filter(
|
||||
(marker) => (showVisited && marker.is_visited) || (showPlanned && !marker.is_visited)
|
||||
);
|
||||
let newMarker: { lngLat: any } | null = null;
|
||||
|
||||
let newMarker = [];
|
||||
let newLongitude: number | null = null;
|
||||
let newLatitude: number | null = null;
|
||||
|
||||
let newLongitude = null;
|
||||
let newLatitude = null;
|
||||
|
||||
function addMarker(e) {
|
||||
newMarker = [];
|
||||
newMarker = [...newMarker, { lngLat: e.detail.lngLat, name: 'Marker 1' }];
|
||||
function addMarker(e: { detail: { lngLat: { lng: any; lat: any } } }) {
|
||||
newMarker = null;
|
||||
newMarker = { lngLat: e.detail.lngLat };
|
||||
newLongitude = e.detail.lngLat.lng;
|
||||
newLatitude = e.detail.lngLat.lat;
|
||||
}
|
||||
|
||||
let markers = [];
|
||||
// let markers = [];
|
||||
|
||||
$: {
|
||||
markers = data.props.markers;
|
||||
}
|
||||
// $: {
|
||||
// markers = data.props.markers;
|
||||
// }
|
||||
|
||||
function createNewAdventure(event) {
|
||||
let newMarker = {
|
||||
lngLat: [event.detail.longitude, event.detail.latitude],
|
||||
name: event.detail.name,
|
||||
type: event.detail.type,
|
||||
visits: event.detail.visits
|
||||
};
|
||||
markers = [...markers, newMarker];
|
||||
clearMarkers();
|
||||
function createNewAdventure(event: CustomEvent) {
|
||||
adventures = [...adventures, event.detail];
|
||||
newMarker = null;
|
||||
createModalOpen = false;
|
||||
}
|
||||
let visitedRegions = data.props.visitedRegions;
|
||||
|
||||
let allRegions = [];
|
||||
|
||||
let visitArray = [];
|
||||
|
||||
// turns in into an array of the visits
|
||||
visitedRegions.forEach((el) => {
|
||||
visitArray.push(el.region);
|
||||
});
|
||||
|
||||
function clearMarkers() {
|
||||
newMarker = [];
|
||||
newLatitude = null;
|
||||
newLongitude = null;
|
||||
}
|
||||
|
||||
// mapped to the checkbox
|
||||
let showGEO = false;
|
||||
$: {
|
||||
if (showGEO && allRegions.length === 0) {
|
||||
(async () => {
|
||||
allRegions = await fetch('/api/visitedregion/').then((res) => res.json());
|
||||
})();
|
||||
} else if (!showGEO) {
|
||||
allRegions = [];
|
||||
}
|
||||
}
|
||||
|
||||
let createModalOpen = false;
|
||||
</script>
|
||||
|
||||
<h1 class="text-center font-bold text-4xl">Adventure Map</h1>
|
||||
|
@ -109,14 +71,14 @@
|
|||
id="show-geo"
|
||||
name="show-geo"
|
||||
class="checkbox"
|
||||
bind:checked={showGEO}
|
||||
on:click={() => (showGeo = !showGeo)}
|
||||
/>
|
||||
<!-- <div class="divider divider-horizontal"></div> -->
|
||||
{#if newMarker.length > 0}
|
||||
<div class="divider divider-horizontal"></div>
|
||||
{#if newMarker}
|
||||
<button type="button" class="btn btn-primary mb-2" on:click={() => (createModalOpen = true)}
|
||||
>Add New Adventure at Marker</button
|
||||
>
|
||||
<button type="button" class="btn btn-neutral mb-2" on:click={clearMarkers}
|
||||
<button type="button" class="btn btn-neutral mb-2" on:click={() => (newMarker = null)}
|
||||
>Clear Marker</button
|
||||
>
|
||||
{:else}
|
||||
|
@ -142,12 +104,13 @@
|
|||
class="relative aspect-[9/16] max-h-[70vh] w-full sm:aspect-video sm:max-h-full"
|
||||
standardControls
|
||||
>
|
||||
{#each filteredMarkers as marker}
|
||||
{#if marker.is_visited}
|
||||
{#each filteredAdventures as adventure}
|
||||
{#if adventure.latitude && adventure.longitude}
|
||||
<Marker
|
||||
lngLat={marker.lngLat}
|
||||
on:click={() => (clickedName = marker.name)}
|
||||
class="grid h-8 w-8 place-items-center rounded-full border border-gray-200 bg-red-300 text-black shadow-md"
|
||||
lngLat={[adventure.longitude, adventure.latitude]}
|
||||
class="grid h-8 w-8 place-items-center rounded-full border border-gray-200 bg-{adventure.is_visited
|
||||
? 'red'
|
||||
: 'blue'}-300 text-black shadow-md"
|
||||
>
|
||||
<svg
|
||||
width="24"
|
||||
|
@ -156,58 +119,26 @@
|
|||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle cx="12" cy="12" r="10" stroke="red" stroke-width="2" fill="red" />
|
||||
<circle
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke={adventure.is_visited ? 'red' : 'blue'}
|
||||
stroke-width="2"
|
||||
fill={adventure.is_visited ? 'red' : 'blue'}
|
||||
/>
|
||||
</svg>
|
||||
<Popup openOn="click" offset={[0, -10]}>
|
||||
<div class="text-lg text-black font-bold">{marker.name}</div>
|
||||
<p class="font-semibold text-black text-md">Visited</p>
|
||||
<div class="text-lg text-black font-bold">{adventure.name}</div>
|
||||
<p class="font-semibold text-black text-md">
|
||||
{$t(`adventures.activities.${marker.type}`)}
|
||||
{adventure.is_visited ? $t('adventures.visited') : $t('adventures.planned')}
|
||||
</p>
|
||||
{#if marker.visits && marker.visits.length > 0}
|
||||
<p class="text-black text-sm">
|
||||
{#each marker.visits as visit}
|
||||
{visit.start_date
|
||||
? new Date(visit.start_date).toLocaleDateString(undefined, {
|
||||
timeZone: 'UTC'
|
||||
})
|
||||
: ''}
|
||||
{visit.end_date && visit.end_date !== '' && visit.end_date !== visit.start_date
|
||||
? ' - ' +
|
||||
new Date(visit.end_date).toLocaleDateString(undefined, {
|
||||
timeZone: 'UTC'
|
||||
})
|
||||
: ''}
|
||||
<br />
|
||||
{/each}
|
||||
</p>
|
||||
{/if}
|
||||
</Popup>
|
||||
</Marker>
|
||||
{:else}
|
||||
<Marker
|
||||
lngLat={marker.lngLat}
|
||||
on:click={() => (clickedName = marker.name)}
|
||||
class="grid h-8 w-8 place-items-center rounded-full border border-gray-200 bg-blue-300 text-black shadow-2xl focus:outline-2 focus:outline-black"
|
||||
>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle cx="12" cy="12" r="10" stroke="blue" stroke-width="2" fill="blue" />
|
||||
</svg>
|
||||
<Popup openOn="click" offset={[0, -10]}>
|
||||
<div class="text-lg text-black font-bold">{marker.name}</div>
|
||||
<p class="font-semibold text-black text-md">Planned</p>
|
||||
<p class="font-semibold text-black text-md">
|
||||
{$t(`adventures.activities.${marker.type}`)}
|
||||
{$t(`adventures.activities.${adventure.type}`)}
|
||||
</p>
|
||||
{#if marker.visits && marker.visits.length > 0}
|
||||
{#if adventure.visits && adventure.visits.length > 0}
|
||||
<p class="text-black text-sm">
|
||||
{#each marker.visits as visit}
|
||||
{#each adventure.visits as visit}
|
||||
{visit.start_date
|
||||
? new Date(visit.start_date).toLocaleDateString(undefined, {
|
||||
timeZone: 'UTC'
|
||||
|
@ -229,31 +160,31 @@
|
|||
{/each}
|
||||
|
||||
<MapEvents on:click={addMarker} />
|
||||
{#each newMarker as marker}
|
||||
<DefaultMarker lngLat={marker.lngLat} />
|
||||
{/each}
|
||||
{#if newMarker}
|
||||
<DefaultMarker lngLat={newMarker.lngLat} />
|
||||
{/if}
|
||||
|
||||
{#each allRegions as { longitude, latitude, name, region }}
|
||||
<Marker
|
||||
lngLat={[longitude, latitude]}
|
||||
on:click={() => (clickedName = name)}
|
||||
class="grid h-8 w-8 place-items-center rounded-full border border-gray-200 bg-green-300 text-black shadow-md"
|
||||
>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{#each visitedRegions as region}
|
||||
{#if showGeo}
|
||||
<Marker
|
||||
lngLat={[region.latitude, region.longitude]}
|
||||
class="grid h-8 w-8 place-items-center rounded-full border border-gray-200 bg-green-300 text-black shadow-md"
|
||||
>
|
||||
<!-- green circle -->
|
||||
<circle cx="12" cy="12" r="10" stroke="green" stroke-width="2" fill="green" />
|
||||
</svg>
|
||||
<Popup openOn="click" offset={[0, -10]}>
|
||||
<div class="text-lg text-black font-bold">{name}</div>
|
||||
<p class="font-semibold text-black text-md">{region}</p>
|
||||
</Popup>
|
||||
</Marker>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle cx="12" cy="12" r="10" stroke="green" stroke-width="2" fill="green" />
|
||||
</svg>
|
||||
<Popup openOn="click" offset={[0, -10]}>
|
||||
<div class="text-lg text-black font-bold">{name}</div>
|
||||
<p class="font-semibold text-black text-md">{region.name}</p>
|
||||
</Popup>
|
||||
</Marker>
|
||||
{/if}
|
||||
{/each}
|
||||
</MapLibre>
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue