connect to database
This commit is contained in:
parent
143ac1d2ea
commit
9def1973a7
|
@ -63,15 +63,19 @@
|
|||
if (place && place.name) {
|
||||
// If the place has photos, get the URL for the first photo
|
||||
if (place.photos && place.photos.length > 0) {
|
||||
try {
|
||||
const photoOptions = {
|
||||
maxWidth: 400,
|
||||
maxHeight: 300
|
||||
};
|
||||
// Get the photo URL
|
||||
console.log(place.photos[0]);
|
||||
const photoUrl = place.photos[0].getUrl(photoOptions);
|
||||
place.photoUrl = photoUrl;
|
||||
console.log(place.photoUrl);
|
||||
place.photoUrl = place.photos[0].getUrl(photoOptions);
|
||||
} catch (error) {
|
||||
console.error('Error getting photo URL:', error);
|
||||
place.photoUrl = '/placeholder.jpeg';
|
||||
}
|
||||
}
|
||||
else {
|
||||
place.photoUrl = '/placeholder.jpeg';
|
||||
}
|
||||
|
||||
selectedPlace = place;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
const newPlace = {
|
||||
name: place.name || 'Unknown Place',
|
||||
desc: place.formatted_address || '',
|
||||
image: (place as any).photoUrl || 'placeholder.jpeg',
|
||||
image: (place as any).photoUrl || '/placeholder.jpeg',
|
||||
time: 'Add Time'
|
||||
};
|
||||
|
||||
|
|
|
@ -3,16 +3,18 @@
|
|||
import { goto } from '$app/navigation';
|
||||
import { onMount } from 'svelte';
|
||||
import { Loader } from '@googlemaps/js-api-loader';
|
||||
import { ref, child, get, set, onValue, push } from 'firebase/database';
|
||||
import { db } from '../../firebase';
|
||||
import Button from './Button.svelte';
|
||||
|
||||
export let showPopup = false;
|
||||
export let fromPage = 'home';
|
||||
|
||||
let destination = "";
|
||||
let selectedPlace: any;
|
||||
let lastSelectedPlaceName = "";
|
||||
let startDate = "";
|
||||
let endDate = "";
|
||||
let friends: string[] = [];
|
||||
let tripmates: string[] = [];
|
||||
let currentEmail = "";
|
||||
let destinationError = false;
|
||||
let startDateError = false;
|
||||
|
@ -74,13 +76,14 @@
|
|||
autocomplete = new google.maps.places.Autocomplete(input, {
|
||||
types: ['(regions)']
|
||||
});
|
||||
autocomplete.setFields(['name', 'formatted_address']);
|
||||
autocomplete.setFields(['name', 'formatted_address', 'photos', 'place_id', 'geometry']);
|
||||
|
||||
autocomplete.addListener('place_changed', () => {
|
||||
if (!autocomplete) return;
|
||||
const place = autocomplete.getPlace();
|
||||
if (place.name) {
|
||||
destination = place.name;
|
||||
selectedPlace = place;
|
||||
lastSelectedPlaceName = input.value.trim();
|
||||
destinationError = false;
|
||||
}
|
||||
|
@ -100,7 +103,7 @@
|
|||
destinationError = false;
|
||||
destination = "";
|
||||
}
|
||||
}, 200);
|
||||
}, 400);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -114,15 +117,15 @@
|
|||
event.preventDefault();
|
||||
const email = currentEmail.trim();
|
||||
|
||||
if (email && isValidEmail(email) && !friends.includes(email)) {
|
||||
friends = [...friends, email];
|
||||
if (email && isValidEmail(email) && !tripmates.includes(email)) {
|
||||
tripmates = [...tripmates, email];
|
||||
currentEmail = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeEmail(emailToRemove: string) {
|
||||
friends = friends.filter(email => email !== emailToRemove);
|
||||
tripmates = tripmates.filter(email => email !== emailToRemove);
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
|
@ -130,7 +133,7 @@
|
|||
destination = "";
|
||||
startDate = "";
|
||||
endDate = "";
|
||||
friends = [];
|
||||
tripmates = [];
|
||||
currentEmail = "";
|
||||
destinationError = false;
|
||||
startDateError = false;
|
||||
|
@ -142,7 +145,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
function handleStart() {
|
||||
async function handleStart() {
|
||||
destinationError = !destination;
|
||||
startDateError = !startDate;
|
||||
endDateError = !endDate;
|
||||
|
@ -162,12 +165,41 @@
|
|||
}
|
||||
|
||||
if (destinationError || startDateError || endDateError) {
|
||||
// alert('Please fill in all required fields: Destination, Start Date, End Date');
|
||||
return;
|
||||
}
|
||||
else {
|
||||
goto(`/itinerary?from=${fromPage}`);
|
||||
const tid = crypto.randomUUID();
|
||||
|
||||
// Extract required place details
|
||||
const placeDetails = {
|
||||
name: selectedPlace.name,
|
||||
formatted_address: selectedPlace.formatted_address,
|
||||
photo: selectedPlace.photos?.[0]?.getUrl(),
|
||||
location: {
|
||||
lat: selectedPlace.geometry.location.lat(),
|
||||
lng: selectedPlace.geometry.location.lng()
|
||||
}
|
||||
};
|
||||
|
||||
const tripData = {
|
||||
tid,
|
||||
destination: placeDetails,
|
||||
startDate,
|
||||
endDate,
|
||||
tripmates,
|
||||
created_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
try {
|
||||
// Create a new reference for this specific trip using its ID
|
||||
const tripRef = ref(db, `trips/${tid}`);
|
||||
await set(tripRef, tripData);
|
||||
console.log(`Trip saved to db with ID: ${tid}`);
|
||||
goto(`/itinerary/${tid}`);
|
||||
handleCancel();
|
||||
} catch (error) {
|
||||
console.error('Error saving trip:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,7 +214,7 @@
|
|||
<h1>Start a New Plan</h1>
|
||||
|
||||
<div class="input-form">
|
||||
<label for="destination-input">Destination</label>
|
||||
<label for="destination">Destination</label>
|
||||
<div bind:this={destinationInput} class="destination-wrapper" id="destination"></div>
|
||||
{#if destinationError}
|
||||
<p class="error-message">Please enter your destination</p>
|
||||
|
@ -229,7 +261,7 @@
|
|||
</span>
|
||||
</label>
|
||||
<div class="email-input-container">
|
||||
{#each friends as email}
|
||||
{#each tripmates as email}
|
||||
<div class="email-tag">
|
||||
<span>{email}</span>
|
||||
<button class="remove-email" onclick={() => removeEmail(email)}>×</button>
|
||||
|
@ -240,7 +272,7 @@
|
|||
id="trip-friends"
|
||||
bind:value={currentEmail}
|
||||
onkeydown={handleEmailInput}
|
||||
placeholder={friends.length ? "" : "Enter email addresses"}
|
||||
placeholder={tripmates.length ? "" : "Enter email addresses"}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
const defaultPlace: Omit<Place, 'desc'> = {
|
||||
name: 'PlaceName',
|
||||
image: 'placeholder.jpeg',
|
||||
image: '/placeholder.jpeg',
|
||||
time: 'Add Time'
|
||||
};
|
||||
|
||||
|
|
|
@ -1,26 +1,39 @@
|
|||
<script>
|
||||
export let image = '';
|
||||
export let marginLeft = '0px';
|
||||
export let zIndex = '0';
|
||||
<script lang="ts">
|
||||
export let friends = 1;
|
||||
export let images: string[] = [];
|
||||
</script>
|
||||
|
||||
<div
|
||||
<div class="profile-pictures">
|
||||
{#each Array(friends) as _, i}
|
||||
<div
|
||||
class="profile-picture"
|
||||
style="margin-left: {marginLeft}; z-index: {zIndex}"
|
||||
>
|
||||
{#if image}
|
||||
<img class="profile-img" src={image} alt="" />
|
||||
style="z-index: {friends - i}; margin-left: {i === 0 ? '0' : '-20px'}"
|
||||
>
|
||||
{#if images[i]}
|
||||
<img class="profile-img" src={images[i]} alt="Profile" />
|
||||
{:else}
|
||||
<img class="profile-img" src='profile-pic.png' alt="" />
|
||||
<div class="default-avatar">
|
||||
<i class="fa-solid fa-user"></i>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.profile-pictures {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.profile-picture {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
border-radius: 50%;
|
||||
border: 2px solid white;
|
||||
background-color: var(--gray-100);
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.profile-img {
|
||||
|
@ -29,4 +42,14 @@
|
|||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.default-avatar {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--gray-400);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
</style>
|
|
@ -6,7 +6,7 @@
|
|||
</script>
|
||||
|
||||
<div class="trip-card">
|
||||
<div class="image" style="background-image: url({image})">
|
||||
<div class="image" style="background-image: url({image || ''})">
|
||||
<!-- Image placeholder if no image provided -->
|
||||
{#if !image}
|
||||
<div class="placeholder">
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
<BottomBar onClick={() => goto('/trips')} />
|
||||
|
||||
<NewTripPopup bind:showPopup={showNewTripPopup} fromPage="home" />
|
||||
<NewTripPopup bind:showPopup={showNewTripPopup} />
|
||||
</main>
|
||||
|
||||
<style>
|
||||
|
|
|
@ -147,8 +147,7 @@
|
|||
</div>
|
||||
|
||||
<div class="tripmates">
|
||||
<ProfilePicture zIndex=1/>
|
||||
<ProfilePicture marginLeft=-15px zIndex=0/>
|
||||
<ProfilePicture friends={2} />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
|
420
src/routes/itinerary/[tid]/+page.svelte
Normal file
420
src/routes/itinerary/[tid]/+page.svelte
Normal file
|
@ -0,0 +1,420 @@
|
|||
<script lang="ts">
|
||||
import '../../../app.css';
|
||||
import { goto } from '$app/navigation';
|
||||
import { slide } from 'svelte/transition';
|
||||
import { quintOut } from 'svelte/easing';
|
||||
import { page } from '$app/state';
|
||||
import { onMount } from 'svelte';
|
||||
import { Loader } from '@googlemaps/js-api-loader';
|
||||
import { browser } from '$app/environment';
|
||||
import { ref, onValue, update } from 'firebase/database';
|
||||
import { db } from '../../../firebase';
|
||||
import ProfilePicture from '$lib/components/ProfilePicture.svelte';
|
||||
import BottomBar from '$lib/components/BottomBar.svelte';
|
||||
import Button from '$lib/components/Button.svelte';
|
||||
import ItineraryDate from '$lib/components/ItineraryDate.svelte';
|
||||
import AddPlaces from '$lib/components/AddPlaces.svelte';
|
||||
import PlaceCard from '$lib/components/PlaceCard.svelte';
|
||||
|
||||
let tripData: any = null;
|
||||
let tripDates: string[] = [];
|
||||
let places: string[] = [];
|
||||
let tid: string;
|
||||
const place_placeholder = { name: 'Somewhere'}
|
||||
const places_placeholder = Array(3).fill(place_placeholder);
|
||||
|
||||
const GOOGLE_PLACES_API_KEY = import.meta.env.VITE_GOOGLE_PLACES_API_KEY;
|
||||
let mapContainer: HTMLDivElement;
|
||||
let expandedDates: Record<string, boolean> = {};
|
||||
let map: google.maps.Map | null = null;
|
||||
|
||||
onMount(async () => {
|
||||
if (!browser) return;
|
||||
|
||||
if (!GOOGLE_PLACES_API_KEY) {
|
||||
console.error('Google Places API key is missing');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the trip ID from the URL
|
||||
tid = page.params.tid;
|
||||
|
||||
const loader = new Loader({
|
||||
apiKey: GOOGLE_PLACES_API_KEY,
|
||||
version: "weekly",
|
||||
libraries: ["places"],
|
||||
language: 'en'
|
||||
});
|
||||
|
||||
try {
|
||||
const { Map } = await loader.importLibrary("maps");
|
||||
|
||||
// Fetch trip data and initialize map when data is ready
|
||||
const tripRef = ref(db, `trips/${tid}`);
|
||||
|
||||
onValue(tripRef, (snapshot) => {
|
||||
tripData = snapshot.val();
|
||||
if (tripData) {
|
||||
// Generate array of dates between start and end date
|
||||
const start = new Date(tripData.startDate);
|
||||
const end = new Date(tripData.endDate);
|
||||
const dates = [];
|
||||
for (let date = new Date(start); date <= end; date.setDate(date.getDate() + 1)) {
|
||||
dates.push(date.toLocaleDateString('en-GB', { day: '2-digit', month: '2-digit', year: 'numeric' }));
|
||||
}
|
||||
tripDates = dates;
|
||||
|
||||
// Initialize expanded states for dates
|
||||
expandedDates = Object.fromEntries(dates.map(date => [date, false]));
|
||||
|
||||
// Initialize placesToVisit from database or empty array
|
||||
placesToVisit = tripData.placesToVisit || [];
|
||||
|
||||
// Initialize or update the map
|
||||
if (mapContainer && tripData.destination?.location) {
|
||||
if (!map) {
|
||||
map = new Map(mapContainer, {
|
||||
center: tripData.destination.location,
|
||||
zoom: 8,
|
||||
});
|
||||
} else {
|
||||
map.setCenter(tripData.destination.location);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error loading Google Maps:', error);
|
||||
}
|
||||
});
|
||||
|
||||
let expandedSections = {
|
||||
explore: true,
|
||||
places_to_visit: true,
|
||||
itinerary: true
|
||||
};
|
||||
|
||||
let recommendedPlaces = [
|
||||
{ name: "Place name" },
|
||||
{ name: "Place name" },
|
||||
{ name: "Place name" }
|
||||
];
|
||||
|
||||
let placesToVisit: any[] = [];
|
||||
|
||||
async function handleDeletePlace(index: number) {
|
||||
const newPlacesToVisit = placesToVisit.filter((_, i) => i !== index);
|
||||
|
||||
try {
|
||||
// Update the database
|
||||
await update(ref(db, `trips/${tid}`), {
|
||||
placesToVisit: newPlacesToVisit
|
||||
});
|
||||
|
||||
// Update local state
|
||||
placesToVisit = newPlacesToVisit;
|
||||
} catch (error) {
|
||||
console.error('Error deleting place:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSection(section: keyof typeof expandedSections) {
|
||||
expandedSections[section] = !expandedSections[section];
|
||||
}
|
||||
|
||||
function handleBack() {
|
||||
goto('/trips');
|
||||
}
|
||||
|
||||
function handlePastTrip() {
|
||||
console.log(`see past trips to ${tripData?.destination?.name}`)
|
||||
}
|
||||
|
||||
async function handlePlaceSelected(place: google.maps.places.PlaceResult) {
|
||||
const newPlace = {
|
||||
name: place.name || 'Unknown Place',
|
||||
desc: place.formatted_address || '',
|
||||
image: (place as any).photoUrl || '/placeholder.jpeg'
|
||||
};
|
||||
|
||||
const updatedPlaces = [...placesToVisit, newPlace];
|
||||
|
||||
try {
|
||||
// Update the database
|
||||
await update(ref(db, `trips/${tid}`), {
|
||||
placesToVisit: updatedPlaces
|
||||
});
|
||||
|
||||
// Update local state
|
||||
placesToVisit = updatedPlaces;
|
||||
} catch (error) {
|
||||
console.error('Error adding place:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
console.log('cancel update');
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
console.log('save update');
|
||||
}
|
||||
|
||||
function handleRecommendPlaces() {
|
||||
console.log(`will give recommendation using OpenAI`);
|
||||
}
|
||||
|
||||
function handleTurnIntoItinerary() {
|
||||
console.log(`please turn this into itinerary`);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<div class="plan-section">
|
||||
<header>
|
||||
<div class="back-btn-wrapper">
|
||||
<button class="back-btn" onclick={handleBack} aria-label="Back">
|
||||
<i class="fa-solid fa-chevron-left"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="trip-info">
|
||||
<h1>Trip to {tripData?.destination?.name || 'Loading...'}</h1>
|
||||
<p class="date">{tripData?.startDate ? new Date(tripData.startDate).toLocaleDateString('en-GB', { day: '2-digit', month: '2-digit', year: 'numeric' }) : ''} - {tripData?.endDate ? new Date(tripData.endDate).toLocaleDateString('en-GB', { day: '2-digit', month: '2-digit', year: 'numeric' }) : ''}</p>
|
||||
</div>
|
||||
|
||||
<div class="tripmates">
|
||||
<ProfilePicture friends={tripData?.tripmates?.length || 0} />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="content">
|
||||
<section class="places-section">
|
||||
<button class="section-header" onclick={() => toggleSection('places_to_visit')}>
|
||||
<div class="section-text">
|
||||
<i class="fa-solid fa-chevron-right arrow-icon" class:rotated={expandedSections.places_to_visit}></i>
|
||||
<h2>Places to Visit</h2>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{#if expandedSections.places_to_visit}
|
||||
<div
|
||||
class="section-content places"
|
||||
transition:slide={{ duration: 400, easing: quintOut }}
|
||||
>
|
||||
<div class="added-places">
|
||||
{#each placesToVisit as place, i}
|
||||
<PlaceCard
|
||||
variant="simple"
|
||||
place={place}
|
||||
onDelete={() => handleDeletePlace(i)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<AddPlaces
|
||||
onPlaceSelected={handlePlaceSelected}
|
||||
countryRestriction="tw"
|
||||
/>
|
||||
|
||||
<div class="places-buttons">
|
||||
<Button text="Recommend Places" type="blue" onClick={handleRecommendPlaces} />
|
||||
<Button text="Turn into Itinerary" type="blue" onClick={handleTurnIntoItinerary} />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<section class="itinerary-section">
|
||||
<button class="section-header" onclick={() => toggleSection('itinerary')}>
|
||||
<div class="section-text">
|
||||
<i class="fa-solid fa-chevron-right arrow-icon" class:rotated={expandedSections.itinerary}></i>
|
||||
<h2>Itinerary</h2>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{#if expandedSections.itinerary}
|
||||
<div
|
||||
class="section-content"
|
||||
transition:slide={{ duration: 400, easing: quintOut }}
|
||||
>
|
||||
{#each tripDates as date}
|
||||
<ItineraryDate
|
||||
{date}
|
||||
isExpanded={expandedDates[date]}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<div class="button-group">
|
||||
<Button text="Cancel" type="gray" onClick={handleCancel}/>
|
||||
<!-- later edit this so button turns blue only when there is changes to the plan -->
|
||||
<Button text="Save" type="blue" onClick={handleSave} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="map-section">
|
||||
<div class="map-container" bind:this={mapContainer}></div>
|
||||
<BottomBar title="Past Trips" desc="Click to view all past trips to {tripData?.destination?.name}" onClick={handlePastTrip} />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
main {
|
||||
height: 100vh;
|
||||
display: grid;
|
||||
grid-template-columns: 50% 50%;
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
.plan-section {
|
||||
height: 100vh;
|
||||
box-sizing: border-box;
|
||||
padding: 0.5rem 0rem 0rem 0rem;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.map-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background-color: #84D7EB;
|
||||
}
|
||||
|
||||
.map-container {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 0 2rem 1.5rem 1rem;
|
||||
border-bottom: 1px solid var(--gray-100);
|
||||
}
|
||||
|
||||
.back-btn-wrapper {
|
||||
align-self: flex-start;
|
||||
margin-top: 1.75rem;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1.2rem;
|
||||
cursor: pointer;
|
||||
padding: 0.5rem;
|
||||
color: var(--gray-400);
|
||||
border-radius: 50%;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.back-btn:hover {
|
||||
background-color: var(--gray-100);
|
||||
}
|
||||
|
||||
.trip-info {
|
||||
flex: 1;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.trip-info h1 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.date {
|
||||
color: var(--gray-400);
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.tripmates {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 1rem 1.5rem 0 1.5rem;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
width: 100%;
|
||||
background: none;
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--gray-100);
|
||||
box-sizing: border-box;
|
||||
padding: 0.75rem 0.5rem;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.section-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
transition: transform 0.3s ease;
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
.rotated {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.section-text h2 {
|
||||
margin: 0;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.section-content {
|
||||
padding-left: 1.5rem;
|
||||
padding-top: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 1rem;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.section-content.places{
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.places-buttons {
|
||||
margin-top: 0.5rem;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
position: sticky;
|
||||
flex-shrink: 0;
|
||||
background-color: var(--white);
|
||||
padding: 1.5rem 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-top: auto;
|
||||
}
|
||||
</style>
|
|
@ -4,29 +4,65 @@
|
|||
import Button from '$lib/components/Button.svelte';
|
||||
import NewTripPopup from '$lib/components/NewTripPopup.svelte';
|
||||
import Nav from '$lib/components/Nav.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { ref, onValue } from 'firebase/database';
|
||||
import { db } from '../../firebase';
|
||||
|
||||
interface Trip {
|
||||
destination: string;
|
||||
tid: string;
|
||||
destination: {
|
||||
name: string;
|
||||
photo: string;
|
||||
formatted_address: string;
|
||||
location: {
|
||||
lat: number;
|
||||
lng: number;
|
||||
}
|
||||
};
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
imageUrl: string;
|
||||
tripmates: string[];
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
let activeTab = "Ongoing Trips";
|
||||
let showNewTripPopup = false;
|
||||
let contentContainer: HTMLElement;
|
||||
|
||||
// Sample data, replace with actual data later
|
||||
const sample_trip = {
|
||||
destination: "Taiwan",
|
||||
startDate: "04.27.2025",
|
||||
endDate: "04.30.2025",
|
||||
imageUrl: ""
|
||||
}
|
||||
let ongoingTrips = Array(3).fill(sample_trip);
|
||||
let ongoingTrips: Trip[] = [];
|
||||
let pastTrips: Trip[] = [];
|
||||
|
||||
// let pastTrips: Trip[] = [];
|
||||
let pastTrips = Array(14).fill(sample_trip);
|
||||
onMount(() => {
|
||||
// Reference to the trips node
|
||||
const tripsRef = ref(db, 'trips');
|
||||
|
||||
// Listen for changes in the trips data
|
||||
onValue(tripsRef, (snapshot) => {
|
||||
const trips: Trip[] = [];
|
||||
snapshot.forEach((childSnapshot) => {
|
||||
trips.push({
|
||||
tid: childSnapshot.key,
|
||||
...childSnapshot.val()
|
||||
});
|
||||
});
|
||||
|
||||
console.log(trips);
|
||||
// Get today's date at midnight for comparison
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
// Filter trips based on end date
|
||||
ongoingTrips = trips.filter(trip => {
|
||||
const endDate = new Date(trip.endDate);
|
||||
return endDate >= today;
|
||||
}).sort((a, b) => new Date(a.startDate).getTime() - new Date(b.startDate).getTime());
|
||||
|
||||
pastTrips = trips.filter(trip => {
|
||||
const endDate = new Date(trip.endDate);
|
||||
return endDate < today;
|
||||
}).sort((a, b) => new Date(b.endDate).getTime() - new Date(a.endDate).getTime()); // Sort past trips by most recent first
|
||||
});
|
||||
});
|
||||
|
||||
function handleNewTrip() {
|
||||
showNewTripPopup = true;
|
||||
|
@ -71,7 +107,12 @@
|
|||
{:else}
|
||||
<div class="trips-grid">
|
||||
{#each ongoingTrips as trip}
|
||||
<TripCard {...trip} />
|
||||
<TripCard
|
||||
destination={trip.destination.name}
|
||||
startDate={new Date(trip.startDate).toLocaleDateString('en-GB', { day: '2-digit', month: '2-digit', year: 'numeric' })}
|
||||
endDate={new Date(trip.endDate).toLocaleDateString('en-GB', { day: '2-digit', month: '2-digit', year: 'numeric' })}
|
||||
image={trip.destination.photo}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -83,7 +124,12 @@
|
|||
{:else}
|
||||
<div class="trips-grid">
|
||||
{#each pastTrips as trip}
|
||||
<TripCard {...trip} />
|
||||
<TripCard
|
||||
destination={trip.destination.name}
|
||||
startDate={new Date(trip.startDate).toLocaleDateString('en-GB', { day: '2-digit', month: '2-digit', year: 'numeric' })}
|
||||
endDate={new Date(trip.endDate).toLocaleDateString('en-GB', { day: '2-digit', month: '2-digit', year: 'numeric' })}
|
||||
image={trip.destination.photo}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -95,7 +141,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<NewTripPopup bind:showPopup={showNewTripPopup} fromPage="trips" />
|
||||
<NewTripPopup bind:showPopup={showNewTripPopup} />
|
||||
</main>
|
||||
|
||||
<style>
|
||||
|
|
Loading…
Reference in New Issue
Block a user