diff --git a/src/lib/components/PlaceCard.svelte b/src/lib/components/PlaceCard.svelte index 450762f..064bfbc 100644 --- a/src/lib/components/PlaceCard.svelte +++ b/src/lib/components/PlaceCard.svelte @@ -83,9 +83,9 @@ {#if fullPlace.desc}

{fullPlace.desc}

{/if} -
+
{/if} @@ -236,7 +236,7 @@ font-size: 0.9rem; } - .edit-time { + /* .edit-time { background-color: var(--gray-200); border: none; padding: 0.5rem 1rem; @@ -249,5 +249,5 @@ .edit-time:hover { opacity: 0.75; - } + } */ \ No newline at end of file diff --git a/src/lib/components/WorldMap.svelte b/src/lib/components/WorldMap.svelte index fdfbc50..7a764be 100644 --- a/src/lib/components/WorldMap.svelte +++ b/src/lib/components/WorldMap.svelte @@ -4,38 +4,91 @@ import { feature } from 'topojson-client'; import { Colors } from '../constants/Colors'; import '../../app.css'; + import { ref, get } from 'firebase/database'; + import { db } from '../../firebase'; let mapContainer: HTMLDivElement; + interface TripLocation { + name: string; + location: { + lat: number; + lng: number; + }; + } + + async function getPastTripLocations(): Promise { + try { + const tripsRef = ref(db, 'trips'); + const snapshot = await get(tripsRef); + + if (!snapshot.exists()) return []; + + // Get today's date at midnight for comparison + const today = new Date(); + today.setHours(0, 0, 0, 0); + + // Create a Set to store unique locations + const uniqueLocations = new Map(); + + // Filter past trips and extract unique destinations + Object.values(snapshot.val()).forEach((trip: any) => { + const endDate = new Date(trip.endDate); + if (endDate < today && trip.destination?.location) { + const locationKey = `${trip.destination.location.lat},${trip.destination.location.lng}`; + if (!uniqueLocations.has(locationKey)) { + uniqueLocations.set(locationKey, { + name: trip.destination.name, + location: trip.destination.location + }); + } + } + }); + + return Array.from(uniqueLocations.values()); + } catch (error) { + console.error('Error fetching past trips:', error); + return []; + } + } + + let cleanup: (() => void) | undefined; + onMount(() => { - const width = mapContainer.clientWidth; - const height = mapContainer.clientHeight; + let mounted = true; - // Create SVG - const svg = d3.select(mapContainer) - .append('svg') - .attr('width', '100%') - .attr('height', '100%') - .attr('viewBox', `0 0 ${width} ${height}`) // make a coordinate from (0, 0) to (width, height) - .attr('preserveAspectRatio', 'xMidYMid meet') as d3.Selection; // center the map + async function initMap() { + if (!mounted) return; - // Add a group for all map elements that will be transformed - const g = svg.append('g'); + const width = mapContainer.clientWidth; + const height = mapContainer.clientHeight; - // Create projection - const projection = d3.geoMercator() - .scale(width / (2 * Math.PI)) - .translate([width / 2, height / 1.6]); // position the map, horizontally centered but is slighty upward + // Create SVG + const svg = d3.select(mapContainer) + .append('svg') + .attr('width', '100%') + .attr('height', '100%') + .attr('viewBox', `0 0 ${width} ${height}`) + .attr('preserveAspectRatio', 'xMidYMid meet') as d3.Selection; - const path = d3.geoPath().projection(projection); + // Add a group for all map elements that will be transformed + const g = svg.append('g'); - // Tokyo coordinates [longitude, latitude] - const tokyo: [number, number] = [139.6917, 35.6895]; + // Create projection + const projection = d3.geoMercator() + .scale(width / (2 * Math.PI)) + .translate([width / 2, height / 1.6]); // position the map, horizontally centered but is slighty upward + + const path = d3.geoPath().projection(projection); - const initMap = async () => { try { + // Get past trip locations + const pastLocations = await getPastTripLocations(); + if (!mounted) return; + // Load world map data const response = await fetch('https://unpkg.com/world-atlas@2/countries-110m.json'); + if (!mounted) return; const world = await response.json(); // Convert TopoJSON to GeoJSON @@ -53,13 +106,16 @@ .attr('stroke', Colors.gray.light50) .attr('stroke-width', '0.5'); - // Add Tokyo marker - g.append('circle') - .attr('cx', projection(tokyo)![0]) - .attr('cy', projection(tokyo)![1]) + // Add markers for past trip locations + g.selectAll('circle') + .data(pastLocations) + .enter() + .append('circle') + .attr('cx', d => projection([d.location.lng, d.location.lat])![0]) + .attr('cy', d => projection([d.location.lng, d.location.lat])![1]) .attr('r', 5) .attr('class', 'marker') - .attr('fill', Colors.planner.med400); + .attr('fill', Colors.planner.med400) // Add zoom behavior const zoom = d3.zoom() @@ -70,16 +126,20 @@ svg.call(zoom) .call(zoom.transform, d3.zoomIdentity); + } catch (error) { - console.error('Error loading map:', error); + console.error('Error initializing map:', error); } - }; + } initMap(); - return () => { + cleanup = () => { + mounted = false; d3.select(mapContainer).selectAll('*').remove(); }; + + return cleanup; }); diff --git a/src/routes/itinerary/[tid]/+page.svelte b/src/routes/itinerary/[tid]/+page.svelte index 8e0d9f0..caf38b8 100644 --- a/src/routes/itinerary/[tid]/+page.svelte +++ b/src/routes/itinerary/[tid]/+page.svelte @@ -259,7 +259,7 @@ tripDates = dates; // initialize expanded states for dates - expandedDates = Object.fromEntries(dates.map(date => [date, false])); + expandedDates = Object.fromEntries(dates.map(date => [date, true])); // initialize placesToVisit from database or empty array placesToVisit = tripData.placesToVisit || [];