trip) adding support for lodgings
This commit is contained in:
10
src/routes/(protected)/api/airlines/+server.ts
Normal file
10
src/routes/(protected)/api/airlines/+server.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
import { searchAirlines } from '$lib/server/flights.js';
|
||||
|
||||
export const GET: RequestHandler = ({ url }) => {
|
||||
const q = url.searchParams.get('q') ?? '';
|
||||
const airlines = searchAirlines(q);
|
||||
return json(airlines);
|
||||
};
|
||||
|
||||
10
src/routes/(protected)/api/airports/+server.ts
Normal file
10
src/routes/(protected)/api/airports/+server.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
import { searchAirports } from '$lib/server/flights.js';
|
||||
|
||||
export const GET: RequestHandler = ({ url }) => {
|
||||
const q = url.searchParams.get('q') ?? '';
|
||||
const airports = searchAirports(q);
|
||||
return json(airports);
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ import { searchCities } from '$lib/server/plans.js';
|
||||
|
||||
export const GET: RequestHandler = ({ url }) => {
|
||||
const q = url.searchParams.get('q') ?? '';
|
||||
const cities = searchCities(q);
|
||||
const countryCode = url.searchParams.get('country_code') ?? undefined;
|
||||
const cities = searchCities(q, countryCode);
|
||||
return json(cities);
|
||||
};
|
||||
|
||||
9
src/routes/(protected)/api/countries/+server.ts
Normal file
9
src/routes/(protected)/api/countries/+server.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { json } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
import { getCountries } from '$lib/server/plans.js';
|
||||
|
||||
export const GET: RequestHandler = ({ url }) => {
|
||||
const q = url.searchParams.get('q') ?? undefined;
|
||||
const countries = getCountries(q);
|
||||
return json(countries);
|
||||
};
|
||||
@@ -1,7 +1,14 @@
|
||||
import { error, fail } from '@sveltejs/kit';
|
||||
import { getTripById, updateTrip } from '$lib/server/trips.js';
|
||||
import { createDestination, getPlansForTrip, deletePlan } from '$lib/server/plans.js';
|
||||
import { addTraveller, getTravellersForTrip, getPeopleForUser, removeTravellerFromTrip } from '$lib/server/travellers.js';
|
||||
import {
|
||||
addTraveller,
|
||||
getTravellersForTrip,
|
||||
getPeopleForUser,
|
||||
removeTravellerFromTrip
|
||||
} from '$lib/server/travellers.js';
|
||||
import { createFlight, updateFlight, getFlightBookingsForTrip } from '$lib/server/flights.js';
|
||||
import { createLodging, updateLodging, getLodgingsForTrip } from '$lib/server/lodgings.js';
|
||||
import type { PageServerLoad, Actions } from './$types';
|
||||
|
||||
export const load: PageServerLoad = async (event) => {
|
||||
@@ -15,8 +22,19 @@ export const load: PageServerLoad = async (event) => {
|
||||
const plans = getPlansForTrip(trip.id, userId);
|
||||
const travellers = getTravellersForTrip(trip.id, userId);
|
||||
const people = getPeopleForUser(userId);
|
||||
const flightBookings = getFlightBookingsForTrip(trip.id, userId);
|
||||
const lodgings = getLodgingsForTrip(trip.id, userId);
|
||||
|
||||
return { trip, plans, planCount: plans.length, travellers, travellerCount: travellers.length, people };
|
||||
return {
|
||||
trip,
|
||||
plans,
|
||||
planCount: plans.length,
|
||||
travellers,
|
||||
travellerCount: travellers.length,
|
||||
people,
|
||||
flightBookings,
|
||||
lodgings
|
||||
};
|
||||
};
|
||||
|
||||
export const actions: Actions = {
|
||||
@@ -128,7 +146,9 @@ export const actions: Actions = {
|
||||
removeTravellerFromTrip(trip.id, personId, userId);
|
||||
return { success: true };
|
||||
} catch (err) {
|
||||
return fail(400, { error: err instanceof Error ? err.message : 'Failed to remove traveller' });
|
||||
return fail(400, {
|
||||
error: err instanceof Error ? err.message : 'Failed to remove traveller'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -153,5 +173,350 @@ export const actions: Actions = {
|
||||
} catch (err) {
|
||||
return fail(400, { error: err instanceof Error ? err.message : 'Failed to remove plan' });
|
||||
}
|
||||
},
|
||||
|
||||
addFlight: async (event) => {
|
||||
const session = await event.locals.auth();
|
||||
const userId = session?.user?.id;
|
||||
if (!userId) return fail(401, { error: 'Not authenticated' });
|
||||
|
||||
const trip = getTripById(event.params.id, userId);
|
||||
if (!trip) return fail(404, { error: 'Trip not found' });
|
||||
|
||||
const data = await event.request.formData();
|
||||
const confirmationNumber = (data.get('confirmation_number') as string)?.trim() || undefined;
|
||||
const priceRaw = (data.get('price') as string)?.trim();
|
||||
const price = priceRaw ? parseFloat(priceRaw) : undefined;
|
||||
const currency = (data.get('currency') as string)?.trim() || 'USD';
|
||||
const status = data.get('status') as string as 'idea' | 'tentative' | 'confirmed';
|
||||
const passengerIds = data.getAll('passenger_ids[]') as string[];
|
||||
|
||||
// Parse segments - form data comes as segments[0][field], segments[1][field], etc.
|
||||
const segments: Array<{
|
||||
departureDate: string;
|
||||
airlineId?: number;
|
||||
airlineIata?: string;
|
||||
airlineName?: string;
|
||||
flightNumber: string;
|
||||
route?: {
|
||||
departureAirportId?: number;
|
||||
departureAirportCode?: string;
|
||||
arrivalAirportId?: number;
|
||||
arrivalAirportCode?: string;
|
||||
departureDatetime?: string;
|
||||
arrivalDatetime?: string;
|
||||
};
|
||||
}> = [];
|
||||
|
||||
// Collect all segment indices
|
||||
const segmentIndices = new Set<number>();
|
||||
for (const key of data.keys()) {
|
||||
const match = key.match(/^segments\[(\d+)\]/);
|
||||
if (match) {
|
||||
segmentIndices.add(parseInt(match[1]));
|
||||
}
|
||||
}
|
||||
|
||||
// Build segments array
|
||||
for (const index of Array.from(segmentIndices).sort((a, b) => a - b)) {
|
||||
const departureDate = (data.get(`segments[${index}][departure_date]`) as string)?.trim();
|
||||
const airlineIdRaw = (data.get(`segments[${index}][airline_id]`) as string)?.trim();
|
||||
const airlineIata = (data.get(`segments[${index}][airline_iata]`) as string)?.trim();
|
||||
const airlineName = (data.get(`segments[${index}][airline_name]`) as string)?.trim();
|
||||
const flightNumber = (data.get(`segments[${index}][flight_number]`) as string)?.trim();
|
||||
const departureAirportIdRaw = (
|
||||
data.get(`segments[${index}][departure_airport_id]`) as string
|
||||
)?.trim();
|
||||
const departureAirportCode = (
|
||||
data.get(`segments[${index}][departure_airport_code]`) as string
|
||||
)?.trim();
|
||||
const arrivalAirportIdRaw = (
|
||||
data.get(`segments[${index}][arrival_airport_id]`) as string
|
||||
)?.trim();
|
||||
const arrivalAirportCode = (
|
||||
data.get(`segments[${index}][arrival_airport_code]`) as string
|
||||
)?.trim();
|
||||
const departureDatetime =
|
||||
(data.get(`segments[${index}][departure_datetime]`) as string)?.trim() || undefined;
|
||||
const arrivalDatetime =
|
||||
(data.get(`segments[${index}][arrival_datetime]`) as string)?.trim() || undefined;
|
||||
|
||||
if (!departureDate || !flightNumber) {
|
||||
return fail(400, { error: 'All segments must have a departure date and flight number' });
|
||||
}
|
||||
|
||||
const hasRoute =
|
||||
departureAirportIdRaw ||
|
||||
departureAirportCode ||
|
||||
arrivalAirportIdRaw ||
|
||||
arrivalAirportCode ||
|
||||
departureDatetime ||
|
||||
arrivalDatetime;
|
||||
|
||||
segments.push({
|
||||
departureDate,
|
||||
airlineId: airlineIdRaw ? parseInt(airlineIdRaw) : undefined,
|
||||
airlineIata: airlineIata || undefined,
|
||||
airlineName: airlineName || undefined,
|
||||
flightNumber,
|
||||
route: hasRoute
|
||||
? {
|
||||
departureAirportId: departureAirportIdRaw
|
||||
? parseInt(departureAirportIdRaw)
|
||||
: undefined,
|
||||
departureAirportCode: departureAirportCode || undefined,
|
||||
arrivalAirportId: arrivalAirportIdRaw ? parseInt(arrivalAirportIdRaw) : undefined,
|
||||
arrivalAirportCode: arrivalAirportCode || undefined,
|
||||
departureDatetime,
|
||||
arrivalDatetime
|
||||
}
|
||||
: undefined
|
||||
});
|
||||
}
|
||||
|
||||
if (segments.length === 0) {
|
||||
return fail(400, { error: 'At least one flight segment is required' });
|
||||
}
|
||||
|
||||
try {
|
||||
createFlight({
|
||||
tripId: trip.id,
|
||||
userId,
|
||||
confirmationNumber,
|
||||
price,
|
||||
currency,
|
||||
status: ['idea', 'tentative', 'confirmed'].includes(status) ? status : 'idea',
|
||||
segments,
|
||||
passengerIds: passengerIds.length > 0 ? passengerIds : undefined
|
||||
});
|
||||
return { success: true };
|
||||
} catch (err) {
|
||||
return fail(400, { error: err instanceof Error ? err.message : 'Failed to create flight' });
|
||||
}
|
||||
},
|
||||
|
||||
editFlight: async (event) => {
|
||||
const session = await event.locals.auth();
|
||||
const userId = session?.user?.id;
|
||||
if (!userId) return fail(401, { error: 'Not authenticated' });
|
||||
|
||||
const trip = getTripById(event.params.id, userId);
|
||||
if (!trip) return fail(404, { error: 'Trip not found' });
|
||||
|
||||
const data = await event.request.formData();
|
||||
const bookingId = (data.get('booking_id') as string)?.trim();
|
||||
if (!bookingId) return fail(400, { error: 'Booking ID is required' });
|
||||
|
||||
const confirmationNumber = (data.get('confirmation_number') as string)?.trim() || undefined;
|
||||
const priceRaw = (data.get('price') as string)?.trim();
|
||||
const price = priceRaw ? parseFloat(priceRaw) : undefined;
|
||||
const currency = (data.get('currency') as string)?.trim() || 'USD';
|
||||
const status = data.get('status') as string as 'idea' | 'tentative' | 'confirmed';
|
||||
const passengerIds = data.getAll('passenger_ids[]') as string[];
|
||||
|
||||
const segments: Array<{
|
||||
departureDate: string;
|
||||
airlineId?: number;
|
||||
airlineIata?: string;
|
||||
airlineName?: string;
|
||||
flightNumber: string;
|
||||
route?: {
|
||||
departureAirportId?: number;
|
||||
departureAirportCode?: string;
|
||||
arrivalAirportId?: number;
|
||||
arrivalAirportCode?: string;
|
||||
departureDatetime?: string;
|
||||
arrivalDatetime?: string;
|
||||
};
|
||||
}> = [];
|
||||
|
||||
const segmentIndices = new Set<number>();
|
||||
for (const key of data.keys()) {
|
||||
const match = key.match(/^segments\[(\d+)\]/);
|
||||
if (match) segmentIndices.add(parseInt(match[1]));
|
||||
}
|
||||
|
||||
for (const index of Array.from(segmentIndices).sort((a, b) => a - b)) {
|
||||
const departureDate = (data.get(`segments[${index}][departure_date]`) as string)?.trim();
|
||||
const airlineIdRaw = (data.get(`segments[${index}][airline_id]`) as string)?.trim();
|
||||
const airlineIata = (data.get(`segments[${index}][airline_iata]`) as string)?.trim();
|
||||
const airlineName = (data.get(`segments[${index}][airline_name]`) as string)?.trim();
|
||||
const flightNumber = (data.get(`segments[${index}][flight_number]`) as string)?.trim();
|
||||
const departureAirportIdRaw = (
|
||||
data.get(`segments[${index}][departure_airport_id]`) as string
|
||||
)?.trim();
|
||||
const departureAirportCode = (
|
||||
data.get(`segments[${index}][departure_airport_code]`) as string
|
||||
)?.trim();
|
||||
const arrivalAirportIdRaw = (
|
||||
data.get(`segments[${index}][arrival_airport_id]`) as string
|
||||
)?.trim();
|
||||
const arrivalAirportCode = (
|
||||
data.get(`segments[${index}][arrival_airport_code]`) as string
|
||||
)?.trim();
|
||||
const departureDatetime =
|
||||
(data.get(`segments[${index}][departure_datetime]`) as string)?.trim() || undefined;
|
||||
const arrivalDatetime =
|
||||
(data.get(`segments[${index}][arrival_datetime]`) as string)?.trim() || undefined;
|
||||
|
||||
if (!departureDate || !flightNumber) {
|
||||
return fail(400, { error: 'All segments must have a departure date and flight number' });
|
||||
}
|
||||
|
||||
const hasRoute =
|
||||
departureAirportIdRaw ||
|
||||
departureAirportCode ||
|
||||
arrivalAirportIdRaw ||
|
||||
arrivalAirportCode ||
|
||||
departureDatetime ||
|
||||
arrivalDatetime;
|
||||
|
||||
segments.push({
|
||||
departureDate,
|
||||
airlineId: airlineIdRaw ? parseInt(airlineIdRaw) : undefined,
|
||||
airlineIata: airlineIata || undefined,
|
||||
airlineName: airlineName || undefined,
|
||||
flightNumber,
|
||||
route: hasRoute
|
||||
? {
|
||||
departureAirportId: departureAirportIdRaw
|
||||
? parseInt(departureAirportIdRaw)
|
||||
: undefined,
|
||||
departureAirportCode: departureAirportCode || undefined,
|
||||
arrivalAirportId: arrivalAirportIdRaw ? parseInt(arrivalAirportIdRaw) : undefined,
|
||||
arrivalAirportCode: arrivalAirportCode || undefined,
|
||||
departureDatetime,
|
||||
arrivalDatetime
|
||||
}
|
||||
: undefined
|
||||
});
|
||||
}
|
||||
|
||||
if (segments.length === 0) {
|
||||
return fail(400, { error: 'At least one flight segment is required' });
|
||||
}
|
||||
|
||||
try {
|
||||
updateFlight({
|
||||
bookingId,
|
||||
userId,
|
||||
confirmationNumber,
|
||||
price,
|
||||
currency,
|
||||
status: ['idea', 'tentative', 'confirmed'].includes(status) ? status : 'idea',
|
||||
segments,
|
||||
passengerIds: passengerIds.length > 0 ? passengerIds : undefined
|
||||
});
|
||||
return { success: true };
|
||||
} catch (err) {
|
||||
return fail(400, { error: err instanceof Error ? err.message : 'Failed to update flight' });
|
||||
}
|
||||
},
|
||||
|
||||
addLodging: async (event) => {
|
||||
const session = await event.locals.auth();
|
||||
const userId = session?.user?.id;
|
||||
if (!userId) return fail(401, { error: 'Not authenticated' });
|
||||
|
||||
const trip = getTripById(event.params.id, userId);
|
||||
if (!trip) return fail(404, { error: 'Trip not found' });
|
||||
|
||||
const data = await event.request.formData();
|
||||
const name = (data.get('name') as string)?.trim();
|
||||
if (!name) return fail(400, { error: 'Lodging name is required' });
|
||||
|
||||
const str = (key: string) => (data.get(key) as string)?.trim() || undefined;
|
||||
const num = (key: string) => {
|
||||
const v = str(key);
|
||||
return v ? parseFloat(v) : undefined;
|
||||
};
|
||||
const status = data.get('status') as string as 'idea' | 'tentative' | 'confirmed';
|
||||
const guestIds = data.getAll('guest_ids[]') as string[];
|
||||
|
||||
try {
|
||||
createLodging({
|
||||
tripId: trip.id,
|
||||
userId,
|
||||
status: ['idea', 'tentative', 'confirmed'].includes(status) ? status : 'idea',
|
||||
name,
|
||||
chain: str('chain'),
|
||||
checkInDate: str('check_in_date'),
|
||||
checkInTime: str('check_in_time'),
|
||||
checkInTimezone: str('check_in_timezone'),
|
||||
checkOutDate: str('check_out_date'),
|
||||
checkOutTime: str('check_out_time'),
|
||||
checkOutTimezone: str('check_out_timezone'),
|
||||
addressLine1: str('address_line1'),
|
||||
addressLine2: str('address_line2'),
|
||||
cityName: str('city_name'),
|
||||
country: str('country'),
|
||||
countryCode: str('country_code'),
|
||||
postalCode: str('postal_code'),
|
||||
confirmationNumber: str('confirmation_number'),
|
||||
website: str('website'),
|
||||
phone: str('phone'),
|
||||
price: num('price'),
|
||||
currency: str('currency') ?? 'USD',
|
||||
guestIds: guestIds.length > 0 ? guestIds : undefined
|
||||
});
|
||||
return { success: true };
|
||||
} catch (err) {
|
||||
return fail(400, { error: err instanceof Error ? err.message : 'Failed to create lodging' });
|
||||
}
|
||||
},
|
||||
|
||||
editLodging: async (event) => {
|
||||
const session = await event.locals.auth();
|
||||
const userId = session?.user?.id;
|
||||
if (!userId) return fail(401, { error: 'Not authenticated' });
|
||||
|
||||
const trip = getTripById(event.params.id, userId);
|
||||
if (!trip) return fail(404, { error: 'Trip not found' });
|
||||
|
||||
const data = await event.request.formData();
|
||||
const lodgingId = (data.get('lodging_id') as string)?.trim();
|
||||
if (!lodgingId) return fail(400, { error: 'Lodging ID is required' });
|
||||
|
||||
const name = (data.get('name') as string)?.trim();
|
||||
if (!name) return fail(400, { error: 'Lodging name is required' });
|
||||
|
||||
const str = (key: string) => (data.get(key) as string)?.trim() || undefined;
|
||||
const num = (key: string) => {
|
||||
const v = str(key);
|
||||
return v ? parseFloat(v) : undefined;
|
||||
};
|
||||
const status = data.get('status') as string as 'idea' | 'tentative' | 'confirmed';
|
||||
const guestIds = data.getAll('guest_ids[]') as string[];
|
||||
|
||||
try {
|
||||
updateLodging({
|
||||
lodgingId,
|
||||
userId,
|
||||
status: ['idea', 'tentative', 'confirmed'].includes(status) ? status : 'idea',
|
||||
name,
|
||||
chain: str('chain'),
|
||||
checkInDate: str('check_in_date'),
|
||||
checkInTime: str('check_in_time'),
|
||||
checkInTimezone: str('check_in_timezone'),
|
||||
checkOutDate: str('check_out_date'),
|
||||
checkOutTime: str('check_out_time'),
|
||||
checkOutTimezone: str('check_out_timezone'),
|
||||
addressLine1: str('address_line1'),
|
||||
addressLine2: str('address_line2'),
|
||||
cityName: str('city_name'),
|
||||
country: str('country'),
|
||||
countryCode: str('country_code'),
|
||||
postalCode: str('postal_code'),
|
||||
confirmationNumber: str('confirmation_number'),
|
||||
website: str('website'),
|
||||
phone: str('phone'),
|
||||
price: num('price'),
|
||||
currency: str('currency') ?? 'USD',
|
||||
guestIds: guestIds.length > 0 ? guestIds : undefined
|
||||
});
|
||||
return { success: true };
|
||||
} catch (err) {
|
||||
return fail(400, { error: err instanceof Error ? err.message : 'Failed to update lodging' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,7 +3,13 @@
|
||||
import TripWelcome from '$lib/components/TripWelcome.svelte';
|
||||
import AddDestinationModal from '$lib/components/AddDestinationModal.svelte';
|
||||
import AddTravellerModal from '$lib/components/AddTravellerModal.svelte';
|
||||
import AddFlightModal from '$lib/components/AddFlightModal.svelte';
|
||||
import EditFlightModal from '$lib/components/EditFlightModal.svelte';
|
||||
import AddLodgingModal from '$lib/components/AddLodgingModal.svelte';
|
||||
import EditLodgingModal from '$lib/components/EditLodgingModal.svelte';
|
||||
import PlanCard from '$lib/components/PlanCard.svelte';
|
||||
import FlightCard from '$lib/components/FlightCard.svelte';
|
||||
import LodgingCard from '$lib/components/LodgingCard.svelte';
|
||||
import TravellerChip from '$lib/components/TravellerChip.svelte';
|
||||
|
||||
let { data, form } = $props();
|
||||
@@ -14,10 +20,19 @@
|
||||
let travellerCount = $derived(data.travellerCount ?? 0);
|
||||
let people = $derived(data.people ?? []);
|
||||
let tripTravellerIds = $derived(travellers.map((t) => t.id));
|
||||
let flightBookings = $derived(data.flightBookings ?? []);
|
||||
let lodgings = $derived(data.lodgings ?? []);
|
||||
let editing = $state(false);
|
||||
let showAddDestination = $state(false);
|
||||
let showAddTraveller = $state(false);
|
||||
let showAddFlight = $state(false);
|
||||
let showAddLodging = $state(false);
|
||||
let showAddMenu = $state(false);
|
||||
let editingFlight = $state<(typeof flightBookings)[0] | null>(null);
|
||||
let editingFlightPlan = $derived(
|
||||
editingFlight ? (plans.find((p) => p.id === editingFlight!.plan_id) ?? null) : null
|
||||
);
|
||||
let editingLodging = $state<(typeof lodgings)[0] | null>(null);
|
||||
|
||||
const menuItems = [
|
||||
{
|
||||
@@ -39,6 +54,7 @@
|
||||
label: 'Transportation',
|
||||
icon: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#F97316" stroke-width="1.5"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-3.99-3.99A19.79 19.79 0 0 1 4.1 6.18 2 2 0 0 1 6.08 4h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L10.09 11a16 16 0 0 0 5.91 5.91l1.27-1.27a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 24 18z"/></svg>`,
|
||||
onclick: () => {
|
||||
showAddFlight = true;
|
||||
showAddMenu = false;
|
||||
}
|
||||
},
|
||||
@@ -46,6 +62,7 @@
|
||||
label: 'Lodgings',
|
||||
icon: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#A855F7" stroke-width="1.5"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>`,
|
||||
onclick: () => {
|
||||
showAddLodging = true;
|
||||
showAddMenu = false;
|
||||
}
|
||||
},
|
||||
@@ -100,6 +117,33 @@
|
||||
{people}
|
||||
{tripTravellerIds}
|
||||
/>
|
||||
<AddFlightModal
|
||||
open={showAddFlight}
|
||||
onclose={() => (showAddFlight = false)}
|
||||
{people}
|
||||
{tripTravellerIds}
|
||||
/>
|
||||
<EditFlightModal
|
||||
open={!!editingFlight}
|
||||
flightBooking={editingFlight}
|
||||
planStatus={editingFlightPlan?.status}
|
||||
onclose={() => (editingFlight = null)}
|
||||
{people}
|
||||
{tripTravellerIds}
|
||||
/>
|
||||
<AddLodgingModal
|
||||
open={showAddLodging}
|
||||
onclose={() => (showAddLodging = false)}
|
||||
{people}
|
||||
{tripTravellerIds}
|
||||
/>
|
||||
<EditLodgingModal
|
||||
open={!!editingLodging}
|
||||
lodging={editingLodging}
|
||||
onclose={() => (editingLodging = null)}
|
||||
{people}
|
||||
{tripTravellerIds}
|
||||
/>
|
||||
|
||||
<div class="mx-auto max-w-2xl">
|
||||
<!-- Header -->
|
||||
@@ -389,6 +433,8 @@
|
||||
tripName={trip.name}
|
||||
onAddDestination={() => (showAddDestination = true)}
|
||||
onAddTraveller={() => (showAddTraveller = true)}
|
||||
onAddFlight={() => (showAddFlight = true)}
|
||||
onAddLodging={() => (showAddLodging = true)}
|
||||
/>
|
||||
{:else}
|
||||
<!-- Plans list -->
|
||||
@@ -420,6 +466,86 @@
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Flights section -->
|
||||
{#if flightBookings.length > 0}
|
||||
<div class="mt-8">
|
||||
<h2 class="mb-3 text-sm font-semibold tracking-wider text-gray-400 uppercase">
|
||||
Transportation
|
||||
</h2>
|
||||
<div class="flex flex-col gap-3">
|
||||
{#each flightBookings as flightBooking (flightBooking.id)}
|
||||
{@const plan = plans.find((p) => p.id === flightBooking.plan_id)}
|
||||
{#if plan}
|
||||
{@const formId = `remove-plan-${plan.id}`}
|
||||
{@const submitForm = () => {
|
||||
const form = document.getElementById(formId) as HTMLFormElement;
|
||||
form?.requestSubmit();
|
||||
}}
|
||||
<form
|
||||
id={formId}
|
||||
method="POST"
|
||||
action="?/removePlan"
|
||||
use:enhance={() => {
|
||||
return ({ update }) => {
|
||||
update();
|
||||
};
|
||||
}}
|
||||
class="contents"
|
||||
>
|
||||
<input type="hidden" name="plan_id" value={plan.id} />
|
||||
<FlightCard
|
||||
{plan}
|
||||
{flightBooking}
|
||||
onEdit={() => (editingFlight = flightBooking)}
|
||||
onDelete={submitForm}
|
||||
/>
|
||||
</form>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Lodgings section -->
|
||||
{#if lodgings.length > 0}
|
||||
<div class="mt-8">
|
||||
<h2 class="mb-3 text-sm font-semibold tracking-wider text-gray-400 uppercase">
|
||||
Lodgings
|
||||
</h2>
|
||||
<div class="flex flex-col gap-3">
|
||||
{#each lodgings as lodging (lodging.id)}
|
||||
{@const plan = plans.find((p) => p.id === lodging.plan_id)}
|
||||
{#if plan}
|
||||
{@const formId = `remove-plan-${plan.id}`}
|
||||
{@const submitForm = () => {
|
||||
const form = document.getElementById(formId) as HTMLFormElement;
|
||||
form?.requestSubmit();
|
||||
}}
|
||||
<form
|
||||
id={formId}
|
||||
method="POST"
|
||||
action="?/removePlan"
|
||||
use:enhance={() => {
|
||||
return ({ update }) => {
|
||||
update();
|
||||
};
|
||||
}}
|
||||
class="contents"
|
||||
>
|
||||
<input type="hidden" name="plan_id" value={plan.id} />
|
||||
<LodgingCard
|
||||
{plan}
|
||||
{lodging}
|
||||
onEdit={() => (editingLodging = lodging)}
|
||||
onDelete={submitForm}
|
||||
/>
|
||||
</form>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user