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
All checks were successful
Build and Push Image / docker-build-and-push (push) Successful in 2m22s
This commit is contained in:
@@ -474,6 +474,7 @@ export interface OperatorTourDayPlan {
|
||||
contact_number?: string | null;
|
||||
// Packing/todo fields
|
||||
items_json?: string | null;
|
||||
is_optional?: number | null;
|
||||
}
|
||||
|
||||
export function listPlansForOperatorTour(tourId: number): OperatorTourDayPlan[] {
|
||||
@@ -525,7 +526,8 @@ export function createOperatorTourDayPlan(
|
||||
end_time?: string | null;
|
||||
end_timezone?: string | null;
|
||||
},
|
||||
checklistItemsJson?: string | null
|
||||
checklistItemsJson?: string | null,
|
||||
isOptional?: boolean
|
||||
): OperatorTourDayPlan {
|
||||
const maxPos = db.get<{ pos: number }>(
|
||||
'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 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> = [
|
||||
dayId,
|
||||
type,
|
||||
title.trim(),
|
||||
notes?.trim() || null,
|
||||
position
|
||||
position,
|
||||
isOptional ? 1 : 0
|
||||
];
|
||||
if (type === 'lodging' && l) {
|
||||
cols += ', chain, address_line1, address_line2, city_name, country, country_code, postal_code';
|
||||
|
||||
@@ -23,6 +23,7 @@ interface GAdventureComponent {
|
||||
end_location?: { id?: string; href?: string; name?: string };
|
||||
accommodation_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 {
|
||||
@@ -30,6 +31,9 @@ interface GAdventureItineraryDay {
|
||||
label?: string;
|
||||
summary?: string;
|
||||
components?: GAdventureComponent[];
|
||||
optional_activities?: Array<{
|
||||
activity_dossier?: { id?: string; href?: string; name?: string };
|
||||
}>;
|
||||
}
|
||||
|
||||
// Accommodation dossier detail response (from following accommodation_dossier href)
|
||||
@@ -160,11 +164,30 @@ async function dayPlansFromComponents(components: GAdventureComponent[]): Promis
|
||||
if (details) lodgingFields = lodgingFieldsFromDossier(details);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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 = {
|
||||
name: 'G Adventures',
|
||||
|
||||
@@ -203,7 +226,9 @@ export const GAdventuresProvider: TourProvider = {
|
||||
const days: TourDay[] = [];
|
||||
|
||||
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({
|
||||
dayNumber: d.day,
|
||||
title: d.label ?? '',
|
||||
|
||||
@@ -31,9 +31,10 @@ export interface TourDayPlanTransportFields {
|
||||
|
||||
/** A transport or lodging plan attached to a day (from provider components) */
|
||||
export interface TourDayPlan {
|
||||
type: 'transport' | 'lodging';
|
||||
type: 'transport' | 'lodging' | 'activity';
|
||||
title: string;
|
||||
notes?: string | null;
|
||||
isOptional?: boolean;
|
||||
/** When type is 'lodging', structured address/location from provider */
|
||||
lodgingFields?: TourDayPlanLodgingFields;
|
||||
/** When type is 'transport', structured movement details from provider */
|
||||
|
||||
@@ -507,7 +507,8 @@ export function runMigrations(db: Database): void {
|
||||
website TEXT,
|
||||
address TEXT,
|
||||
contact_number TEXT,
|
||||
items_json TEXT
|
||||
items_json TEXT,
|
||||
is_optional INTEGER NOT NULL DEFAULT 0
|
||||
)
|
||||
`);
|
||||
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,
|
||||
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,
|
||||
start_location, end_location
|
||||
start_location, end_location, is_optional
|
||||
)
|
||||
SELECT
|
||||
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,
|
||||
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
|
||||
`);
|
||||
db.run('DROP TABLE operator_tour_day_plans');
|
||||
@@ -555,7 +556,8 @@ export function runMigrations(db: Database): void {
|
||||
'website TEXT',
|
||||
'address TEXT',
|
||||
'contact_number TEXT',
|
||||
'items_json TEXT'
|
||||
'items_json TEXT',
|
||||
'is_optional INTEGER NOT NULL DEFAULT 0'
|
||||
]) {
|
||||
try {
|
||||
db.run(`ALTER TABLE operator_tour_day_plans ADD COLUMN ${col}`);
|
||||
|
||||
@@ -5,6 +5,7 @@ import { createPlan } from './plans.js';
|
||||
import { createOtherTransport } from './other-transports.js';
|
||||
import { createExperience } from './experiences.js';
|
||||
import { createChecklist } from './checklists.js';
|
||||
import { createLodging } from './lodgings.js';
|
||||
import { listDaysForOperatorTour, listPlansForOperatorTour } from './admin/data.js';
|
||||
|
||||
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: {
|
||||
operatorTourId: number;
|
||||
tripId: string;
|
||||
@@ -316,7 +323,13 @@ export function cloneTemplateDaysToTour(input: {
|
||||
}): void {
|
||||
const days = listDaysForOperatorTour(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) {
|
||||
const dayDate = tourStartDate ? addDaysToDate(tourStartDate, day.day_number - 1) : null;
|
||||
const newDay = createTourDay({
|
||||
tripId: input.tripId,
|
||||
userId: input.userId,
|
||||
@@ -331,7 +344,7 @@ export function cloneTemplateDaysToTour(input: {
|
||||
tripId: input.tripId,
|
||||
userId: input.userId,
|
||||
parentId: newDay.plan_id,
|
||||
status: 'idea',
|
||||
status: dp.is_optional ? 'idea' : 'confirmed',
|
||||
title: dp.title,
|
||||
notes: dp.notes ?? undefined,
|
||||
startDate: dp.start_date ?? undefined,
|
||||
@@ -350,7 +363,7 @@ export function cloneTemplateDaysToTour(input: {
|
||||
type: dp.type,
|
||||
name: dp.title,
|
||||
parentId: newDay.plan_id,
|
||||
status: 'idea',
|
||||
status: dp.type === 'activity' ? (dp.is_optional ? 'idea' : 'confirmed') : 'idea',
|
||||
bookingId: dp.booking_id ?? undefined,
|
||||
totalCost: dp.total_cost ?? undefined,
|
||||
description: dp.description ?? dp.notes ?? undefined,
|
||||
@@ -364,6 +377,27 @@ export function cloneTemplateDaysToTour(input: {
|
||||
endTime: dp.end_time ?? 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') {
|
||||
let items: string[] = [];
|
||||
try {
|
||||
|
||||
@@ -101,9 +101,10 @@ export const actions: Actions = {
|
||||
title: string;
|
||||
description: string;
|
||||
dayPlans?: Array<{
|
||||
type: 'transport' | 'lodging';
|
||||
type: 'transport' | 'lodging' | 'activity';
|
||||
title: string;
|
||||
notes?: string | null;
|
||||
isOptional?: boolean;
|
||||
lodgingFields?: {
|
||||
chain?: string | null;
|
||||
address_line1?: string | null;
|
||||
@@ -151,7 +152,10 @@ export const actions: Actions = {
|
||||
plan.title,
|
||||
plan.notes ?? 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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,18 +304,18 @@
|
||||
? { start: tripDates[dayIndex], end: tripDates[dayIndex] }
|
||||
: null
|
||||
: scheduleView === 'week'
|
||||
? weekRanges[weekIndex] ?? null
|
||||
? (weekRanges[weekIndex] ?? null)
|
||||
: null
|
||||
);
|
||||
|
||||
const filteredTimeline = $derived(
|
||||
currentRange
|
||||
? {
|
||||
scheduled: timeline.scheduled.filter(
|
||||
(group) => group.date >= currentRange.start && group.date <= currentRange.end
|
||||
),
|
||||
unscheduled: []
|
||||
}
|
||||
scheduled: timeline.scheduled.filter(
|
||||
(group) => group.date >= currentRange.start && group.date <= currentRange.end
|
||||
),
|
||||
unscheduled: []
|
||||
}
|
||||
: timeline
|
||||
);
|
||||
|
||||
@@ -357,9 +357,7 @@
|
||||
);
|
||||
|
||||
const visibleLodgings = $derived(
|
||||
filteredPlanIds
|
||||
? lodgings.filter((lodging) => filteredPlanIds.has(lodging.plan_id))
|
||||
: lodgings
|
||||
filteredPlanIds ? lodgings.filter((lodging) => filteredPlanIds.has(lodging.plan_id)) : lodgings
|
||||
);
|
||||
|
||||
const visibleActivities = $derived(
|
||||
@@ -868,7 +866,7 @@
|
||||
{#if scheduleView === 'day' && tripDates.length > 1}
|
||||
<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))}
|
||||
disabled={dayIndex === 0}
|
||||
aria-label="Previous day"
|
||||
@@ -879,7 +877,7 @@
|
||||
{#if scheduleView === 'week' && weekRanges.length > 1}
|
||||
<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))}
|
||||
disabled={weekIndex === 0}
|
||||
aria-label="Previous week"
|
||||
@@ -905,7 +903,7 @@
|
||||
{#if scheduleView === 'day' && tripDates.length > 1}
|
||||
<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))}
|
||||
disabled={dayIndex >= tripDates.length - 1}
|
||||
aria-label="Next day"
|
||||
@@ -916,7 +914,7 @@
|
||||
{#if scheduleView === 'week' && weekRanges.length > 1}
|
||||
<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))}
|
||||
disabled={weekIndex >= weekRanges.length - 1}
|
||||
aria-label="Next week"
|
||||
@@ -1270,6 +1268,26 @@
|
||||
onDelete={submitForm}
|
||||
/>
|
||||
</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}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1288,8 +1306,8 @@
|
||||
<div class="h-px flex-1 bg-gray-200"></div>
|
||||
</div>
|
||||
<div class="mt-4 flex flex-col gap-4">
|
||||
{#each filteredTimeline.unscheduled as entry (entry.id)}
|
||||
{#if entry.kind === 'day'}
|
||||
{#each filteredTimeline.unscheduled as entry (entry.id)}
|
||||
{#if entry.kind === 'day'}
|
||||
<div
|
||||
class="rounded-lg border border-gray-200 bg-gray-50 px-4 py-2"
|
||||
style="border-left: 4px solid {entry.tour.highlightColor ?? '#E5E7EB'}"
|
||||
@@ -1597,6 +1615,26 @@
|
||||
onDelete={submitForm}
|
||||
/>
|
||||
</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}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1674,20 +1712,20 @@
|
||||
</div>
|
||||
|
||||
<!-- Transportation section -->
|
||||
{#if
|
||||
visibleFlightBookings.length > 0 ||
|
||||
visiblePrivateVehicles.length > 0 ||
|
||||
visibleOtherTransports.length > 0
|
||||
}
|
||||
{#if visibleFlightBookings.length > 0 || visiblePrivateVehicles.length > 0 || visibleOtherTransports.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 visiblePlans.filter((p) => p.type === 'transport') as plan (plan.id)}
|
||||
{@const flightBooking = visibleFlightBookings.find((b) => b.plan_id === plan.id)}
|
||||
{@const privateVehicle = visiblePrivateVehicles.find((pv) => pv.plan_id === plan.id)}
|
||||
{@const otherTransport = visibleOtherTransports.find((ot) => ot.plan_id === plan.id)}
|
||||
{#each visiblePlans.filter((p) => p.type === 'transport') as plan (plan.id)}
|
||||
{@const flightBooking = visibleFlightBookings.find((b) => b.plan_id === plan.id)}
|
||||
{@const privateVehicle = visiblePrivateVehicles.find(
|
||||
(pv) => pv.plan_id === plan.id
|
||||
)}
|
||||
{@const otherTransport = visibleOtherTransports.find(
|
||||
(ot) => ot.plan_id === plan.id
|
||||
)}
|
||||
{@const packageTourContext = byTypeTourContextByPlanId.get(plan.id)}
|
||||
{#if plan}
|
||||
{@const formId = `remove-plan-${plan.id}`}
|
||||
@@ -1795,8 +1833,8 @@
|
||||
Attractions & Activities
|
||||
</h2>
|
||||
<div class="flex flex-col gap-3">
|
||||
{#each visibleActivities as activity (activity.id)}
|
||||
{@const plan = visiblePlans.find((p) => p.id === activity.plan_id)}
|
||||
{#each visibleActivities as activity (activity.id)}
|
||||
{@const plan = visiblePlans.find((p) => p.id === activity.plan_id)}
|
||||
{#if plan}
|
||||
{@const formId = `remove-plan-${plan.id}`}
|
||||
{@const packageTourContext = byTypeTourContextByPlanId.get(plan.id)}
|
||||
@@ -1871,8 +1909,8 @@
|
||||
Restaurants
|
||||
</h2>
|
||||
<div class="flex flex-col gap-3">
|
||||
{#each visibleRestaurants as restaurant (restaurant.id)}
|
||||
{@const plan = visiblePlans.find((p) => p.id === restaurant.plan_id)}
|
||||
{#each visibleRestaurants as restaurant (restaurant.id)}
|
||||
{@const plan = visiblePlans.find((p) => p.id === restaurant.plan_id)}
|
||||
{#if plan}
|
||||
{@const formId = `remove-plan-${plan.id}`}
|
||||
{@const packageTourContext = byTypeTourContextByPlanId.get(plan.id)}
|
||||
@@ -1947,8 +1985,8 @@
|
||||
Packing Lists
|
||||
</h2>
|
||||
<div class="flex flex-col gap-3">
|
||||
{#each visiblePackingLists as list (list.id)}
|
||||
{@const plan = visiblePlans.find((p) => p.id === list.plan_id)}
|
||||
{#each visiblePackingLists as list (list.id)}
|
||||
{@const plan = visiblePlans.find((p) => p.id === list.plan_id)}
|
||||
{#if plan}
|
||||
{@const formId = `remove-plan-${plan.id}`}
|
||||
{@const submitForm = () => {
|
||||
@@ -1988,8 +2026,8 @@
|
||||
To-dos
|
||||
</h2>
|
||||
<div class="flex flex-col gap-3">
|
||||
{#each visibleTodos as list (list.id)}
|
||||
{@const plan = visiblePlans.find((p) => p.id === list.plan_id)}
|
||||
{#each visibleTodos as list (list.id)}
|
||||
{@const plan = visiblePlans.find((p) => p.id === list.plan_id)}
|
||||
{#if plan}
|
||||
{@const formId = `remove-plan-${plan.id}`}
|
||||
{@const submitForm = () => {
|
||||
@@ -2029,8 +2067,8 @@
|
||||
Lodgings
|
||||
</h2>
|
||||
<div class="flex flex-col gap-3">
|
||||
{#each visibleLodgings as lodging (lodging.id)}
|
||||
{@const plan = visiblePlans.find((p) => p.id === lodging.plan_id)}
|
||||
{#each visibleLodgings as lodging (lodging.id)}
|
||||
{@const plan = visiblePlans.find((p) => p.id === lodging.plan_id)}
|
||||
{#if plan}
|
||||
{@const formId = `remove-plan-${plan.id}`}
|
||||
{@const packageTourContext = byTypeTourContextByPlanId.get(plan.id)}
|
||||
@@ -2105,8 +2143,8 @@
|
||||
Package Tours
|
||||
</h2>
|
||||
<div class="flex flex-col gap-3">
|
||||
{#each visiblePackageTours as tour (tour.id)}
|
||||
{@const plan = visiblePlans.find((p) => p.id === tour.plan_id)}
|
||||
{#each visiblePackageTours as tour (tour.id)}
|
||||
{@const plan = visiblePlans.find((p) => p.id === tour.plan_id)}
|
||||
{#if plan}
|
||||
{@const formId = `remove-plan-${plan.id}`}
|
||||
{@const submitForm = () => {
|
||||
|
||||
Reference in New Issue
Block a user