Import package tour activities and align day-indexed lodging scheduling
All checks were successful
Build and Push Image / docker-build-and-push (push) Successful in 2m22s

This commit is contained in:
2026-02-24 21:37:22 -05:00
parent 1b4720be17
commit 4af96244ba
7 changed files with 156 additions and 49 deletions

View File

@@ -474,6 +474,7 @@ export interface OperatorTourDayPlan {
contact_number?: string | null; contact_number?: string | null;
// Packing/todo fields // Packing/todo fields
items_json?: string | null; items_json?: string | null;
is_optional?: number | null;
} }
export function listPlansForOperatorTour(tourId: number): OperatorTourDayPlan[] { export function listPlansForOperatorTour(tourId: number): OperatorTourDayPlan[] {
@@ -525,7 +526,8 @@ export function createOperatorTourDayPlan(
end_time?: string | null; end_time?: string | null;
end_timezone?: string | null; end_timezone?: string | null;
}, },
checklistItemsJson?: string | null checklistItemsJson?: string | null,
isOptional?: boolean
): OperatorTourDayPlan { ): OperatorTourDayPlan {
const maxPos = db.get<{ pos: number }>( const maxPos = db.get<{ pos: number }>(
'SELECT COALESCE(MAX(position), -1) + 1 as pos FROM operator_tour_day_plans WHERE operator_tour_day_id = ?', 'SELECT COALESCE(MAX(position), -1) + 1 as pos FROM operator_tour_day_plans WHERE operator_tour_day_id = ?',
@@ -535,13 +537,14 @@ export function createOperatorTourDayPlan(
const l = lodgingFields; const l = lodgingFields;
const t = transportFields; const t = transportFields;
let cols = 'operator_tour_day_id, type, title, notes, position'; let cols = 'operator_tour_day_id, type, title, notes, position, is_optional';
let vals: Array<string | number | null> = [ let vals: Array<string | number | null> = [
dayId, dayId,
type, type,
title.trim(), title.trim(),
notes?.trim() || null, notes?.trim() || null,
position position,
isOptional ? 1 : 0
]; ];
if (type === 'lodging' && l) { if (type === 'lodging' && l) {
cols += ', chain, address_line1, address_line2, city_name, country, country_code, postal_code'; cols += ', chain, address_line1, address_line2, city_name, country, country_code, postal_code';

View File

@@ -23,6 +23,7 @@ interface GAdventureComponent {
end_location?: { id?: string; href?: string; name?: string }; end_location?: { id?: string; href?: string; name?: string };
accommodation_dossier?: { id: string; href: string; name: string }; accommodation_dossier?: { id: string; href: string; name: string };
transport_dossier?: { id: string; href: string; name: string }; transport_dossier?: { id: string; href: string; name: string };
activity_dossier?: { id?: string; href?: string; name?: string };
} }
interface GAdventureItineraryDay { interface GAdventureItineraryDay {
@@ -30,6 +31,9 @@ interface GAdventureItineraryDay {
label?: string; label?: string;
summary?: string; summary?: string;
components?: GAdventureComponent[]; components?: GAdventureComponent[];
optional_activities?: Array<{
activity_dossier?: { id?: string; href?: string; name?: string };
}>;
} }
// Accommodation dossier detail response (from following accommodation_dossier href) // Accommodation dossier detail response (from following accommodation_dossier href)
@@ -160,11 +164,30 @@ async function dayPlansFromComponents(components: GAdventureComponent[]): Promis
if (details) lodgingFields = lodgingFieldsFromDossier(details); if (details) lodgingFields = lodgingFieldsFromDossier(details);
} }
plans.push({ type: 'lodging', title, notes: notes ?? undefined, lodgingFields }); plans.push({ type: 'lodging', title, notes: notes ?? undefined, lodgingFields });
} else if (type === 'ACTIVITY') {
const title = c.activity_dossier?.name?.trim() || c.summary?.trim() || 'Activity';
const notes = c.summary?.trim() || null;
plans.push({ type: 'activity', title, notes: notes ?? undefined, isOptional: false });
} }
} }
return plans; return plans;
} }
function optionalActivityPlansFromDay(day: GAdventureItineraryDay): TourDayPlan[] {
const plans: TourDayPlan[] = [];
for (const opt of day.optional_activities ?? []) {
const title = opt.activity_dossier?.name?.trim();
if (!title) continue;
plans.push({
type: 'activity',
title,
notes: 'Optional activity',
isOptional: true
});
}
return plans;
}
export const GAdventuresProvider: TourProvider = { export const GAdventuresProvider: TourProvider = {
name: 'G Adventures', name: 'G Adventures',
@@ -203,7 +226,9 @@ export const GAdventuresProvider: TourProvider = {
const days: TourDay[] = []; const days: TourDay[] = [];
for (const d of rawDays) { for (const d of rawDays) {
const dayPlans = await dayPlansFromComponents(d.components ?? []); const includedDayPlans = await dayPlansFromComponents(d.components ?? []);
const optionalActivityPlans = optionalActivityPlansFromDay(d);
const dayPlans = [...includedDayPlans, ...optionalActivityPlans];
days.push({ days.push({
dayNumber: d.day, dayNumber: d.day,
title: d.label ?? '', title: d.label ?? '',

View File

@@ -31,9 +31,10 @@ export interface TourDayPlanTransportFields {
/** A transport or lodging plan attached to a day (from provider components) */ /** A transport or lodging plan attached to a day (from provider components) */
export interface TourDayPlan { export interface TourDayPlan {
type: 'transport' | 'lodging'; type: 'transport' | 'lodging' | 'activity';
title: string; title: string;
notes?: string | null; notes?: string | null;
isOptional?: boolean;
/** When type is 'lodging', structured address/location from provider */ /** When type is 'lodging', structured address/location from provider */
lodgingFields?: TourDayPlanLodgingFields; lodgingFields?: TourDayPlanLodgingFields;
/** When type is 'transport', structured movement details from provider */ /** When type is 'transport', structured movement details from provider */

View File

@@ -507,7 +507,8 @@ export function runMigrations(db: Database): void {
website TEXT, website TEXT,
address TEXT, address TEXT,
contact_number TEXT, contact_number TEXT,
items_json TEXT items_json TEXT,
is_optional INTEGER NOT NULL DEFAULT 0
) )
`); `);
db.run(` db.run(`
@@ -515,13 +516,13 @@ export function runMigrations(db: Database): void {
id, operator_tour_day_id, type, title, notes, position, created_at, updated_at, id, operator_tour_day_id, type, title, notes, position, created_at, updated_at,
chain, address_line1, address_line2, city_name, country, country_code, postal_code, chain, address_line1, address_line2, city_name, country, country_code, postal_code,
transport_kind, start_date, start_time, start_timezone, end_date, end_time, end_timezone, transport_kind, start_date, start_time, start_timezone, end_date, end_time, end_timezone,
start_location, end_location start_location, end_location, is_optional
) )
SELECT SELECT
id, operator_tour_day_id, type, title, notes, position, created_at, updated_at, id, operator_tour_day_id, type, title, notes, position, created_at, updated_at,
chain, address_line1, address_line2, city_name, country, country_code, postal_code, chain, address_line1, address_line2, city_name, country, country_code, postal_code,
transport_kind, start_date, start_time, start_timezone, end_date, end_time, end_timezone, transport_kind, start_date, start_time, start_timezone, end_date, end_time, end_timezone,
NULL as start_location, NULL as end_location NULL as start_location, NULL as end_location, 0 as is_optional
FROM operator_tour_day_plans FROM operator_tour_day_plans
`); `);
db.run('DROP TABLE operator_tour_day_plans'); db.run('DROP TABLE operator_tour_day_plans');
@@ -555,7 +556,8 @@ export function runMigrations(db: Database): void {
'website TEXT', 'website TEXT',
'address TEXT', 'address TEXT',
'contact_number TEXT', 'contact_number TEXT',
'items_json TEXT' 'items_json TEXT',
'is_optional INTEGER NOT NULL DEFAULT 0'
]) { ]) {
try { try {
db.run(`ALTER TABLE operator_tour_day_plans ADD COLUMN ${col}`); db.run(`ALTER TABLE operator_tour_day_plans ADD COLUMN ${col}`);

View File

@@ -5,6 +5,7 @@ import { createPlan } from './plans.js';
import { createOtherTransport } from './other-transports.js'; import { createOtherTransport } from './other-transports.js';
import { createExperience } from './experiences.js'; import { createExperience } from './experiences.js';
import { createChecklist } from './checklists.js'; import { createChecklist } from './checklists.js';
import { createLodging } from './lodgings.js';
import { listDaysForOperatorTour, listPlansForOperatorTour } from './admin/data.js'; import { listDaysForOperatorTour, listPlansForOperatorTour } from './admin/data.js';
export interface PackageTour { export interface PackageTour {
@@ -308,6 +309,12 @@ export function updateTourDay(input: {
]); ]);
} }
function addDaysToDate(date: string, offset: number): string {
const [year, month, day] = date.split('-').map(Number);
const ts = Date.UTC(year, month - 1, day + offset);
return new Date(ts).toISOString().slice(0, 10);
}
export function cloneTemplateDaysToTour(input: { export function cloneTemplateDaysToTour(input: {
operatorTourId: number; operatorTourId: number;
tripId: string; tripId: string;
@@ -316,7 +323,13 @@ export function cloneTemplateDaysToTour(input: {
}): void { }): void {
const days = listDaysForOperatorTour(input.operatorTourId); const days = listDaysForOperatorTour(input.operatorTourId);
const allDayPlans = listPlansForOperatorTour(input.operatorTourId); const allDayPlans = listPlansForOperatorTour(input.operatorTourId);
const tourStartDate =
db.get<{ start_date: string | null }>(
'SELECT start_date FROM package_tours WHERE plan_id = ?',
[input.tourPlanId]
)?.start_date ?? null;
for (const day of days) { for (const day of days) {
const dayDate = tourStartDate ? addDaysToDate(tourStartDate, day.day_number - 1) : null;
const newDay = createTourDay({ const newDay = createTourDay({
tripId: input.tripId, tripId: input.tripId,
userId: input.userId, userId: input.userId,
@@ -331,7 +344,7 @@ export function cloneTemplateDaysToTour(input: {
tripId: input.tripId, tripId: input.tripId,
userId: input.userId, userId: input.userId,
parentId: newDay.plan_id, parentId: newDay.plan_id,
status: 'idea', status: dp.is_optional ? 'idea' : 'confirmed',
title: dp.title, title: dp.title,
notes: dp.notes ?? undefined, notes: dp.notes ?? undefined,
startDate: dp.start_date ?? undefined, startDate: dp.start_date ?? undefined,
@@ -350,7 +363,7 @@ export function cloneTemplateDaysToTour(input: {
type: dp.type, type: dp.type,
name: dp.title, name: dp.title,
parentId: newDay.plan_id, parentId: newDay.plan_id,
status: 'idea', status: dp.type === 'activity' ? (dp.is_optional ? 'idea' : 'confirmed') : 'idea',
bookingId: dp.booking_id ?? undefined, bookingId: dp.booking_id ?? undefined,
totalCost: dp.total_cost ?? undefined, totalCost: dp.total_cost ?? undefined,
description: dp.description ?? dp.notes ?? undefined, description: dp.description ?? dp.notes ?? undefined,
@@ -364,6 +377,27 @@ export function cloneTemplateDaysToTour(input: {
endTime: dp.end_time ?? undefined, endTime: dp.end_time ?? undefined,
endTimezone: dp.end_timezone ?? undefined endTimezone: dp.end_timezone ?? undefined
}); });
} else if (dp.type === 'lodging') {
createLodging({
tripId: input.tripId,
userId: input.userId,
parentId: newDay.plan_id,
status: dp.is_optional ? 'idea' : 'confirmed',
name: dp.title,
chain: dp.chain ?? undefined,
checkInDate: dp.start_date ?? dayDate ?? undefined,
checkInTime: dp.start_time ?? undefined,
checkInTimezone: dp.start_timezone ?? undefined,
checkOutDate: dp.end_date ?? dayDate ?? undefined,
checkOutTime: dp.end_time ?? undefined,
checkOutTimezone: dp.end_timezone ?? undefined,
addressLine1: dp.address_line1 ?? undefined,
addressLine2: dp.address_line2 ?? undefined,
cityName: dp.city_name ?? undefined,
country: dp.country ?? undefined,
countryCode: dp.country_code ?? undefined,
postalCode: dp.postal_code ?? undefined
});
} else if (dp.type === 'packing' || dp.type === 'todo') { } else if (dp.type === 'packing' || dp.type === 'todo') {
let items: string[] = []; let items: string[] = [];
try { try {

View File

@@ -101,9 +101,10 @@ export const actions: Actions = {
title: string; title: string;
description: string; description: string;
dayPlans?: Array<{ dayPlans?: Array<{
type: 'transport' | 'lodging'; type: 'transport' | 'lodging' | 'activity';
title: string; title: string;
notes?: string | null; notes?: string | null;
isOptional?: boolean;
lodgingFields?: { lodgingFields?: {
chain?: string | null; chain?: string | null;
address_line1?: string | null; address_line1?: string | null;
@@ -151,7 +152,10 @@ export const actions: Actions = {
plan.title, plan.title,
plan.notes ?? undefined, plan.notes ?? undefined,
plan.type === 'lodging' && plan.lodgingFields ? plan.lodgingFields : undefined, plan.type === 'lodging' && plan.lodgingFields ? plan.lodgingFields : undefined,
plan.type === 'transport' && plan.transportFields ? plan.transportFields : undefined plan.type === 'transport' && plan.transportFields ? plan.transportFields : undefined,
undefined,
undefined,
plan.isOptional ?? false
); );
} }
} }

View File

@@ -304,18 +304,18 @@
? { start: tripDates[dayIndex], end: tripDates[dayIndex] } ? { start: tripDates[dayIndex], end: tripDates[dayIndex] }
: null : null
: scheduleView === 'week' : scheduleView === 'week'
? weekRanges[weekIndex] ?? null ? (weekRanges[weekIndex] ?? null)
: null : null
); );
const filteredTimeline = $derived( const filteredTimeline = $derived(
currentRange currentRange
? { ? {
scheduled: timeline.scheduled.filter( scheduled: timeline.scheduled.filter(
(group) => group.date >= currentRange.start && group.date <= currentRange.end (group) => group.date >= currentRange.start && group.date <= currentRange.end
), ),
unscheduled: [] unscheduled: []
} }
: timeline : timeline
); );
@@ -357,9 +357,7 @@
); );
const visibleLodgings = $derived( const visibleLodgings = $derived(
filteredPlanIds filteredPlanIds ? lodgings.filter((lodging) => filteredPlanIds.has(lodging.plan_id)) : lodgings
? lodgings.filter((lodging) => filteredPlanIds.has(lodging.plan_id))
: lodgings
); );
const visibleActivities = $derived( const visibleActivities = $derived(
@@ -868,7 +866,7 @@
{#if scheduleView === 'day' && tripDates.length > 1} {#if scheduleView === 'day' && tripDates.length > 1}
<button <button
type="button" type="button"
class="absolute left-3 top-1/2 -translate-y-1/2 rounded-md border border-gray-200 px-2 py-1 text-xs text-gray-500 hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-40" class="absolute top-1/2 left-3 -translate-y-1/2 rounded-md border border-gray-200 px-2 py-1 text-xs text-gray-500 hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-40"
onclick={() => (dayIndex = Math.max(0, dayIndex - 1))} onclick={() => (dayIndex = Math.max(0, dayIndex - 1))}
disabled={dayIndex === 0} disabled={dayIndex === 0}
aria-label="Previous day" aria-label="Previous day"
@@ -879,7 +877,7 @@
{#if scheduleView === 'week' && weekRanges.length > 1} {#if scheduleView === 'week' && weekRanges.length > 1}
<button <button
type="button" type="button"
class="absolute left-3 top-1/2 -translate-y-1/2 rounded-md border border-gray-200 px-2 py-1 text-xs text-gray-500 hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-40" class="absolute top-1/2 left-3 -translate-y-1/2 rounded-md border border-gray-200 px-2 py-1 text-xs text-gray-500 hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-40"
onclick={() => (weekIndex = Math.max(0, weekIndex - 1))} onclick={() => (weekIndex = Math.max(0, weekIndex - 1))}
disabled={weekIndex === 0} disabled={weekIndex === 0}
aria-label="Previous week" aria-label="Previous week"
@@ -905,7 +903,7 @@
{#if scheduleView === 'day' && tripDates.length > 1} {#if scheduleView === 'day' && tripDates.length > 1}
<button <button
type="button" type="button"
class="absolute right-3 top-1/2 -translate-y-1/2 rounded-md border border-gray-200 px-2 py-1 text-xs text-gray-500 hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-40" class="absolute top-1/2 right-3 -translate-y-1/2 rounded-md border border-gray-200 px-2 py-1 text-xs text-gray-500 hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-40"
onclick={() => (dayIndex = Math.min(tripDates.length - 1, dayIndex + 1))} onclick={() => (dayIndex = Math.min(tripDates.length - 1, dayIndex + 1))}
disabled={dayIndex >= tripDates.length - 1} disabled={dayIndex >= tripDates.length - 1}
aria-label="Next day" aria-label="Next day"
@@ -916,7 +914,7 @@
{#if scheduleView === 'week' && weekRanges.length > 1} {#if scheduleView === 'week' && weekRanges.length > 1}
<button <button
type="button" type="button"
class="absolute right-3 top-1/2 -translate-y-1/2 rounded-md border border-gray-200 px-2 py-1 text-xs text-gray-500 hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-40" class="absolute top-1/2 right-3 -translate-y-1/2 rounded-md border border-gray-200 px-2 py-1 text-xs text-gray-500 hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-40"
onclick={() => (weekIndex = Math.min(weekRanges.length - 1, weekIndex + 1))} onclick={() => (weekIndex = Math.min(weekRanges.length - 1, weekIndex + 1))}
disabled={weekIndex >= weekRanges.length - 1} disabled={weekIndex >= weekRanges.length - 1}
aria-label="Next week" aria-label="Next week"
@@ -1270,6 +1268,26 @@
onDelete={submitForm} onDelete={submitForm}
/> />
</form> </form>
{:else}
{@const formId = `remove-plan-timeline-${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} />
<PlanCard {plan} onDelete={submitForm} />
</form>
{/if} {/if}
</div> </div>
</div> </div>
@@ -1288,8 +1306,8 @@
<div class="h-px flex-1 bg-gray-200"></div> <div class="h-px flex-1 bg-gray-200"></div>
</div> </div>
<div class="mt-4 flex flex-col gap-4"> <div class="mt-4 flex flex-col gap-4">
{#each filteredTimeline.unscheduled as entry (entry.id)} {#each filteredTimeline.unscheduled as entry (entry.id)}
{#if entry.kind === 'day'} {#if entry.kind === 'day'}
<div <div
class="rounded-lg border border-gray-200 bg-gray-50 px-4 py-2" class="rounded-lg border border-gray-200 bg-gray-50 px-4 py-2"
style="border-left: 4px solid {entry.tour.highlightColor ?? '#E5E7EB'}" style="border-left: 4px solid {entry.tour.highlightColor ?? '#E5E7EB'}"
@@ -1597,6 +1615,26 @@
onDelete={submitForm} onDelete={submitForm}
/> />
</form> </form>
{:else}
{@const formId = `remove-plan-timeline-${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} />
<PlanCard {plan} onDelete={submitForm} />
</form>
{/if} {/if}
</div> </div>
</div> </div>
@@ -1674,20 +1712,20 @@
</div> </div>
<!-- Transportation section --> <!-- Transportation section -->
{#if {#if visibleFlightBookings.length > 0 || visiblePrivateVehicles.length > 0 || visibleOtherTransports.length > 0}
visibleFlightBookings.length > 0 ||
visiblePrivateVehicles.length > 0 ||
visibleOtherTransports.length > 0
}
<div class="mt-8"> <div class="mt-8">
<h2 class="mb-3 text-sm font-semibold tracking-wider text-gray-400 uppercase"> <h2 class="mb-3 text-sm font-semibold tracking-wider text-gray-400 uppercase">
Transportation Transportation
</h2> </h2>
<div class="flex flex-col gap-3"> <div class="flex flex-col gap-3">
{#each visiblePlans.filter((p) => p.type === 'transport') as plan (plan.id)} {#each visiblePlans.filter((p) => p.type === 'transport') as plan (plan.id)}
{@const flightBooking = visibleFlightBookings.find((b) => b.plan_id === plan.id)} {@const flightBooking = visibleFlightBookings.find((b) => b.plan_id === plan.id)}
{@const privateVehicle = visiblePrivateVehicles.find((pv) => pv.plan_id === plan.id)} {@const privateVehicle = visiblePrivateVehicles.find(
{@const otherTransport = visibleOtherTransports.find((ot) => ot.plan_id === plan.id)} (pv) => pv.plan_id === plan.id
)}
{@const otherTransport = visibleOtherTransports.find(
(ot) => ot.plan_id === plan.id
)}
{@const packageTourContext = byTypeTourContextByPlanId.get(plan.id)} {@const packageTourContext = byTypeTourContextByPlanId.get(plan.id)}
{#if plan} {#if plan}
{@const formId = `remove-plan-${plan.id}`} {@const formId = `remove-plan-${plan.id}`}
@@ -1795,8 +1833,8 @@
Attractions & Activities Attractions & Activities
</h2> </h2>
<div class="flex flex-col gap-3"> <div class="flex flex-col gap-3">
{#each visibleActivities as activity (activity.id)} {#each visibleActivities as activity (activity.id)}
{@const plan = visiblePlans.find((p) => p.id === activity.plan_id)} {@const plan = visiblePlans.find((p) => p.id === activity.plan_id)}
{#if plan} {#if plan}
{@const formId = `remove-plan-${plan.id}`} {@const formId = `remove-plan-${plan.id}`}
{@const packageTourContext = byTypeTourContextByPlanId.get(plan.id)} {@const packageTourContext = byTypeTourContextByPlanId.get(plan.id)}
@@ -1871,8 +1909,8 @@
Restaurants Restaurants
</h2> </h2>
<div class="flex flex-col gap-3"> <div class="flex flex-col gap-3">
{#each visibleRestaurants as restaurant (restaurant.id)} {#each visibleRestaurants as restaurant (restaurant.id)}
{@const plan = visiblePlans.find((p) => p.id === restaurant.plan_id)} {@const plan = visiblePlans.find((p) => p.id === restaurant.plan_id)}
{#if plan} {#if plan}
{@const formId = `remove-plan-${plan.id}`} {@const formId = `remove-plan-${plan.id}`}
{@const packageTourContext = byTypeTourContextByPlanId.get(plan.id)} {@const packageTourContext = byTypeTourContextByPlanId.get(plan.id)}
@@ -1947,8 +1985,8 @@
Packing Lists Packing Lists
</h2> </h2>
<div class="flex flex-col gap-3"> <div class="flex flex-col gap-3">
{#each visiblePackingLists as list (list.id)} {#each visiblePackingLists as list (list.id)}
{@const plan = visiblePlans.find((p) => p.id === list.plan_id)} {@const plan = visiblePlans.find((p) => p.id === list.plan_id)}
{#if plan} {#if plan}
{@const formId = `remove-plan-${plan.id}`} {@const formId = `remove-plan-${plan.id}`}
{@const submitForm = () => { {@const submitForm = () => {
@@ -1988,8 +2026,8 @@
To-dos To-dos
</h2> </h2>
<div class="flex flex-col gap-3"> <div class="flex flex-col gap-3">
{#each visibleTodos as list (list.id)} {#each visibleTodos as list (list.id)}
{@const plan = visiblePlans.find((p) => p.id === list.plan_id)} {@const plan = visiblePlans.find((p) => p.id === list.plan_id)}
{#if plan} {#if plan}
{@const formId = `remove-plan-${plan.id}`} {@const formId = `remove-plan-${plan.id}`}
{@const submitForm = () => { {@const submitForm = () => {
@@ -2029,8 +2067,8 @@
Lodgings Lodgings
</h2> </h2>
<div class="flex flex-col gap-3"> <div class="flex flex-col gap-3">
{#each visibleLodgings as lodging (lodging.id)} {#each visibleLodgings as lodging (lodging.id)}
{@const plan = visiblePlans.find((p) => p.id === lodging.plan_id)} {@const plan = visiblePlans.find((p) => p.id === lodging.plan_id)}
{#if plan} {#if plan}
{@const formId = `remove-plan-${plan.id}`} {@const formId = `remove-plan-${plan.id}`}
{@const packageTourContext = byTypeTourContextByPlanId.get(plan.id)} {@const packageTourContext = byTypeTourContextByPlanId.get(plan.id)}
@@ -2105,8 +2143,8 @@
Package Tours Package Tours
</h2> </h2>
<div class="flex flex-col gap-3"> <div class="flex flex-col gap-3">
{#each visiblePackageTours as tour (tour.id)} {#each visiblePackageTours as tour (tour.id)}
{@const plan = visiblePlans.find((p) => p.id === tour.plan_id)} {@const plan = visiblePlans.find((p) => p.id === tour.plan_id)}
{#if plan} {#if plan}
{@const formId = `remove-plan-${plan.id}`} {@const formId = `remove-plan-${plan.id}`}
{@const submitForm = () => { {@const submitForm = () => {