Improve G Adventures transport import details and location mapping
All checks were successful
Build and Push Image / docker-build-and-push (push) Successful in 2m23s
All checks were successful
Build and Push Image / docker-build-and-push (push) Successful in 2m23s
This commit is contained in:
@@ -26,6 +26,8 @@
|
|||||||
let status = $state<'idea' | 'tentative' | 'confirmed'>('idea');
|
let status = $state<'idea' | 'tentative' | 'confirmed'>('idea');
|
||||||
let title = $state('');
|
let title = $state('');
|
||||||
let notes = $state('');
|
let notes = $state('');
|
||||||
|
let startLocation = $state('');
|
||||||
|
let endLocation = $state('');
|
||||||
let startDate = $state('');
|
let startDate = $state('');
|
||||||
let startTime = $state('');
|
let startTime = $state('');
|
||||||
let startTimezone = $state('');
|
let startTimezone = $state('');
|
||||||
@@ -37,6 +39,8 @@
|
|||||||
status = 'idea';
|
status = 'idea';
|
||||||
title = '';
|
title = '';
|
||||||
notes = '';
|
notes = '';
|
||||||
|
startLocation = '';
|
||||||
|
endLocation = '';
|
||||||
startDate = '';
|
startDate = '';
|
||||||
startTime = '';
|
startTime = '';
|
||||||
startTimezone = '';
|
startTimezone = '';
|
||||||
@@ -108,6 +112,35 @@
|
|||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
|
<div class="flex flex-col gap-1.5">
|
||||||
|
<label for="other_start_location" class="text-sm font-medium text-gray-700">
|
||||||
|
Start location
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="other_start_location"
|
||||||
|
name="start_location"
|
||||||
|
type="text"
|
||||||
|
bind:value={startLocation}
|
||||||
|
placeholder="e.g. Mariscal Sucre International Airport"
|
||||||
|
class="rounded-md border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-1.5">
|
||||||
|
<label for="other_end_location" class="text-sm font-medium text-gray-700">
|
||||||
|
End location
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="other_end_location"
|
||||||
|
name="end_location"
|
||||||
|
type="text"
|
||||||
|
bind:value={endLocation}
|
||||||
|
placeholder="e.g. Quito"
|
||||||
|
class="rounded-md border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-3">
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-3">
|
||||||
<div class="flex flex-col gap-1.5">
|
<div class="flex flex-col gap-1.5">
|
||||||
<label for="other_start_date" class="text-sm font-medium text-gray-700">Start date</label>
|
<label for="other_start_date" class="text-sm font-medium text-gray-700">Start date</label>
|
||||||
|
|||||||
@@ -40,6 +40,8 @@
|
|||||||
let status = $state<'idea' | 'tentative' | 'confirmed'>(planStatus);
|
let status = $state<'idea' | 'tentative' | 'confirmed'>(planStatus);
|
||||||
let title = $state(planTitle ?? '');
|
let title = $state(planTitle ?? '');
|
||||||
let notes = $state(planNotes ?? '');
|
let notes = $state(planNotes ?? '');
|
||||||
|
let startLocation = $state(otherTransport.start_location ?? '');
|
||||||
|
let endLocation = $state(otherTransport.end_location ?? '');
|
||||||
let startDate = $state(otherTransport.start_date ?? '');
|
let startDate = $state(otherTransport.start_date ?? '');
|
||||||
let startTime = $state(otherTransport.start_time ?? '');
|
let startTime = $state(otherTransport.start_time ?? '');
|
||||||
let startTimezone = $state(otherTransport.start_timezone ?? '');
|
let startTimezone = $state(otherTransport.start_timezone ?? '');
|
||||||
@@ -88,6 +90,33 @@
|
|||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
|
<div class="flex flex-col gap-1.5">
|
||||||
|
<label for="edit_other_start_location" class="text-sm font-medium text-gray-700">
|
||||||
|
Start location
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="edit_other_start_location"
|
||||||
|
name="start_location"
|
||||||
|
type="text"
|
||||||
|
bind:value={startLocation}
|
||||||
|
class="rounded-md border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-1.5">
|
||||||
|
<label for="edit_other_end_location" class="text-sm font-medium text-gray-700">
|
||||||
|
End location
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="edit_other_end_location"
|
||||||
|
name="end_location"
|
||||||
|
type="text"
|
||||||
|
bind:value={endLocation}
|
||||||
|
class="rounded-md border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-3">
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-3">
|
||||||
<div class="flex flex-col gap-1.5">
|
<div class="flex flex-col gap-1.5">
|
||||||
<label for="edit_other_start_date" class="text-sm font-medium text-gray-700"
|
<label for="edit_other_start_date" class="text-sm font-medium text-gray-700"
|
||||||
|
|||||||
@@ -68,6 +68,12 @@
|
|||||||
{#if plan.notes}
|
{#if plan.notes}
|
||||||
<p class="mt-0.5 text-sm text-gray-500">{plan.notes}</p>
|
<p class="mt-0.5 text-sm text-gray-500">{plan.notes}</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if otherTransport.start_location || otherTransport.end_location}
|
||||||
|
<p class="mt-1 text-sm text-gray-700">
|
||||||
|
{otherTransport.start_location || 'Unknown start'} -> {otherTransport.end_location ||
|
||||||
|
'Unknown end'}
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex shrink-0 items-center gap-1">
|
<div class="flex shrink-0 items-center gap-1">
|
||||||
{#if onEdit}
|
{#if onEdit}
|
||||||
|
|||||||
@@ -463,6 +463,8 @@ export interface OperatorTourDayPlan {
|
|||||||
end_date?: string | null;
|
end_date?: string | null;
|
||||||
end_time?: string | null;
|
end_time?: string | null;
|
||||||
end_timezone?: string | null;
|
end_timezone?: string | null;
|
||||||
|
start_location?: string | null;
|
||||||
|
end_location?: string | null;
|
||||||
// Activity/restaurant fields
|
// Activity/restaurant fields
|
||||||
booking_id?: string | null;
|
booking_id?: string | null;
|
||||||
total_cost?: number | null;
|
total_cost?: number | null;
|
||||||
@@ -506,6 +508,8 @@ export function createOperatorTourDayPlan(
|
|||||||
end_date?: string | null;
|
end_date?: string | null;
|
||||||
end_time?: string | null;
|
end_time?: string | null;
|
||||||
end_timezone?: string | null;
|
end_timezone?: string | null;
|
||||||
|
start_location?: string | null;
|
||||||
|
end_location?: string | null;
|
||||||
},
|
},
|
||||||
experienceFields?: {
|
experienceFields?: {
|
||||||
booking_id?: string | null;
|
booking_id?: string | null;
|
||||||
@@ -554,7 +558,7 @@ export function createOperatorTourDayPlan(
|
|||||||
}
|
}
|
||||||
if (type === 'transport') {
|
if (type === 'transport') {
|
||||||
cols +=
|
cols +=
|
||||||
', 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';
|
||||||
vals = [
|
vals = [
|
||||||
...vals,
|
...vals,
|
||||||
t?.transport_kind?.trim() || 'other',
|
t?.transport_kind?.trim() || 'other',
|
||||||
@@ -563,7 +567,9 @@ export function createOperatorTourDayPlan(
|
|||||||
t?.start_timezone?.trim() || null,
|
t?.start_timezone?.trim() || null,
|
||||||
t?.end_date?.trim() || null,
|
t?.end_date?.trim() || null,
|
||||||
t?.end_time?.trim() || null,
|
t?.end_time?.trim() || null,
|
||||||
t?.end_timezone?.trim() || null
|
t?.end_timezone?.trim() || null,
|
||||||
|
t?.start_location?.trim() || null,
|
||||||
|
t?.end_location?.trim() || null
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if (type === 'activity' || type === 'restaurant') {
|
if (type === 'activity' || type === 'restaurant') {
|
||||||
@@ -616,6 +622,8 @@ export function updateOperatorTourDayPlan(
|
|||||||
end_date?: string | null;
|
end_date?: string | null;
|
||||||
end_time?: string | null;
|
end_time?: string | null;
|
||||||
end_timezone?: string | null;
|
end_timezone?: string | null;
|
||||||
|
start_location?: string | null;
|
||||||
|
end_location?: string | null;
|
||||||
booking_id?: string | null;
|
booking_id?: string | null;
|
||||||
total_cost?: number | null;
|
total_cost?: number | null;
|
||||||
description?: string | null;
|
description?: string | null;
|
||||||
@@ -649,6 +657,8 @@ export function updateOperatorTourDayPlan(
|
|||||||
optional('end_date', updates.end_date ?? undefined);
|
optional('end_date', updates.end_date ?? undefined);
|
||||||
optional('end_time', updates.end_time ?? undefined);
|
optional('end_time', updates.end_time ?? undefined);
|
||||||
optional('end_timezone', updates.end_timezone ?? undefined);
|
optional('end_timezone', updates.end_timezone ?? undefined);
|
||||||
|
optional('start_location', updates.start_location ?? undefined);
|
||||||
|
optional('end_location', updates.end_location ?? undefined);
|
||||||
optional('booking_id', updates.booking_id ?? undefined);
|
optional('booking_id', updates.booking_id ?? undefined);
|
||||||
optional('total_cost', updates.total_cost ?? undefined);
|
optional('total_cost', updates.total_cost ?? undefined);
|
||||||
optional('description', updates.description ?? undefined);
|
optional('description', updates.description ?? undefined);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type {
|
|||||||
TourDay,
|
TourDay,
|
||||||
TourDayPlan,
|
TourDayPlan,
|
||||||
TourDayPlanLodgingFields,
|
TourDayPlanLodgingFields,
|
||||||
|
TourDayPlanTransportFields,
|
||||||
TourProvider,
|
TourProvider,
|
||||||
TourSearchResult
|
TourSearchResult
|
||||||
} from './types.js';
|
} from './types.js';
|
||||||
@@ -18,6 +19,8 @@ function authHeaders() {
|
|||||||
interface GAdventureComponent {
|
interface GAdventureComponent {
|
||||||
type?: string;
|
type?: string;
|
||||||
summary?: string;
|
summary?: string;
|
||||||
|
start_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 };
|
||||||
}
|
}
|
||||||
@@ -38,11 +41,22 @@ interface GAdventureAccommodationDossier {
|
|||||||
address_line_3?: string;
|
address_line_3?: string;
|
||||||
postal_code?: string;
|
postal_code?: string;
|
||||||
city?: { name?: string };
|
city?: { name?: string };
|
||||||
country?: { name?: string };
|
country?: { id?: string; name?: string };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchAccommodationDetails(href: string): Promise<GAdventureAccommodationDossier | null> {
|
interface GAdventureTransportDossier {
|
||||||
|
name?: string;
|
||||||
|
details?: Array<{
|
||||||
|
body?: string;
|
||||||
|
detail_type?: { code?: string; label?: string };
|
||||||
|
}>;
|
||||||
|
dossier_segment?: { code?: string; label?: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchAccommodationDetails(
|
||||||
|
href: string
|
||||||
|
): Promise<GAdventureAccommodationDossier | null> {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(href, { headers: authHeaders() });
|
const res = await fetch(href, { headers: authHeaders() });
|
||||||
if (!res.ok) return null;
|
if (!res.ok) return null;
|
||||||
@@ -52,7 +66,37 @@ async function fetchAccommodationDetails(href: string): Promise<GAdventureAccomm
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function lodgingFieldsFromDossier(dossier: GAdventureAccommodationDossier): TourDayPlanLodgingFields | undefined {
|
async function fetchTransportDetails(href: string): Promise<GAdventureTransportDossier | null> {
|
||||||
|
try {
|
||||||
|
const res = await fetch(href, { headers: authHeaders() });
|
||||||
|
if (!res.ok) return null;
|
||||||
|
return await res.json();
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function summaryFromTransportDossier(dossier: GAdventureTransportDossier | null): string | null {
|
||||||
|
if (!dossier?.details?.length) return null;
|
||||||
|
const summary = dossier.details.find((d) => d.detail_type?.code === 'COMMON__SUMMARY');
|
||||||
|
const body = summary?.body?.trim();
|
||||||
|
return body || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function transportKindFromSegmentCode(code: string | undefined): string {
|
||||||
|
switch (code) {
|
||||||
|
case 'TRANSPORT__AIR':
|
||||||
|
return 'flight';
|
||||||
|
case 'TRANSPORT__AUTOMOBILE':
|
||||||
|
return 'private_vehicle';
|
||||||
|
default:
|
||||||
|
return 'other';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function lodgingFieldsFromDossier(
|
||||||
|
dossier: GAdventureAccommodationDossier
|
||||||
|
): TourDayPlanLodgingFields | undefined {
|
||||||
const addr = dossier.address;
|
const addr = dossier.address;
|
||||||
if (!addr) return undefined;
|
if (!addr) return undefined;
|
||||||
const address_line1 = addr.address_line_1?.trim() || null;
|
const address_line1 = addr.address_line_1?.trim() || null;
|
||||||
@@ -61,14 +105,7 @@ function lodgingFieldsFromDossier(dossier: GAdventureAccommodationDossier): Tour
|
|||||||
const country = addr.country?.name?.trim() || null;
|
const country = addr.country?.name?.trim() || null;
|
||||||
const country_code = addr.country?.id?.trim() || null;
|
const country_code = addr.country?.id?.trim() || null;
|
||||||
const postal_code = addr.postal_code?.trim() || null;
|
const postal_code = addr.postal_code?.trim() || null;
|
||||||
if (
|
if (!address_line1 && !address_line2 && !city_name && !country && !country_code && !postal_code) {
|
||||||
!address_line1 &&
|
|
||||||
!address_line2 &&
|
|
||||||
!city_name &&
|
|
||||||
!country &&
|
|
||||||
!country_code &&
|
|
||||||
!postal_code
|
|
||||||
) {
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@@ -83,13 +120,34 @@ function lodgingFieldsFromDossier(dossier: GAdventureAccommodationDossier): Tour
|
|||||||
|
|
||||||
async function dayPlansFromComponents(components: GAdventureComponent[]): Promise<TourDayPlan[]> {
|
async function dayPlansFromComponents(components: GAdventureComponent[]): Promise<TourDayPlan[]> {
|
||||||
const plans: TourDayPlan[] = [];
|
const plans: TourDayPlan[] = [];
|
||||||
|
const transportDossierCache = new Map<string, GAdventureTransportDossier | null>();
|
||||||
for (const c of components ?? []) {
|
for (const c of components ?? []) {
|
||||||
const type = c.type?.toUpperCase();
|
const type = c.type?.toUpperCase();
|
||||||
if (type === 'TRANSPORT' && c.transport_dossier) {
|
if (type === 'TRANSPORT' && c.transport_dossier) {
|
||||||
|
let transportDossier: GAdventureTransportDossier | null = null;
|
||||||
|
if (c.transport_dossier.href) {
|
||||||
|
transportDossier = transportDossierCache.get(c.transport_dossier.href) ?? null;
|
||||||
|
if (!transportDossierCache.has(c.transport_dossier.href)) {
|
||||||
|
transportDossier = await fetchTransportDetails(c.transport_dossier.href);
|
||||||
|
transportDossierCache.set(c.transport_dossier.href, transportDossier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const title =
|
||||||
|
transportDossier?.name?.trim() ||
|
||||||
|
c.transport_dossier.name?.trim() ||
|
||||||
|
c.summary?.trim() ||
|
||||||
|
'Transport';
|
||||||
|
const notes = (summaryFromTransportDossier(transportDossier) ?? c.summary?.trim()) || null;
|
||||||
|
const transportFields: TourDayPlanTransportFields = {
|
||||||
|
transport_kind: transportKindFromSegmentCode(transportDossier?.dossier_segment?.code),
|
||||||
|
start_location: c.start_location?.name?.trim() || null,
|
||||||
|
end_location: c.end_location?.name?.trim() || null
|
||||||
|
};
|
||||||
plans.push({
|
plans.push({
|
||||||
type: 'transport',
|
type: 'transport',
|
||||||
title: c.transport_dossier.name?.trim() || c.summary?.trim() || 'Transport',
|
title,
|
||||||
notes: c.summary?.trim() || null
|
notes,
|
||||||
|
transportFields
|
||||||
});
|
});
|
||||||
} else if (type === 'ACCOMMODATION' && c.accommodation_dossier) {
|
} else if (type === 'ACCOMMODATION' && c.accommodation_dossier) {
|
||||||
const dossier = c.accommodation_dossier;
|
const dossier = c.accommodation_dossier;
|
||||||
|
|||||||
@@ -16,6 +16,19 @@ export interface TourDayPlanLodgingFields {
|
|||||||
postal_code?: string | null;
|
postal_code?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Transport-specific fields (from provider transport components) */
|
||||||
|
export interface TourDayPlanTransportFields {
|
||||||
|
transport_kind?: string | null;
|
||||||
|
start_location?: string | null;
|
||||||
|
end_location?: string | null;
|
||||||
|
start_date?: string | null;
|
||||||
|
start_time?: string | null;
|
||||||
|
start_timezone?: string | null;
|
||||||
|
end_date?: string | null;
|
||||||
|
end_time?: string | null;
|
||||||
|
end_timezone?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
/** 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';
|
||||||
@@ -23,6 +36,8 @@ export interface TourDayPlan {
|
|||||||
notes?: string | null;
|
notes?: string | null;
|
||||||
/** 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 */
|
||||||
|
transportFields?: TourDayPlanTransportFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TourDay {
|
export interface TourDay {
|
||||||
|
|||||||
@@ -277,10 +277,19 @@ export function runMigrations(db: Database): void {
|
|||||||
end_date TEXT,
|
end_date TEXT,
|
||||||
end_time TEXT,
|
end_time TEXT,
|
||||||
end_timezone TEXT,
|
end_timezone TEXT,
|
||||||
|
start_location TEXT,
|
||||||
|
end_location TEXT,
|
||||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||||
)
|
)
|
||||||
`);
|
`);
|
||||||
|
for (const col of ['start_location TEXT', 'end_location TEXT']) {
|
||||||
|
try {
|
||||||
|
db.run(`ALTER TABLE other_transports ADD COLUMN ${col}`);
|
||||||
|
} catch {
|
||||||
|
/* already exists */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Activity / restaurant details - links to a plan
|
// Activity / restaurant details - links to a plan
|
||||||
db.run(`
|
db.run(`
|
||||||
@@ -490,6 +499,8 @@ export function runMigrations(db: Database): void {
|
|||||||
end_date TEXT,
|
end_date TEXT,
|
||||||
end_time TEXT,
|
end_time TEXT,
|
||||||
end_timezone TEXT,
|
end_timezone TEXT,
|
||||||
|
start_location TEXT,
|
||||||
|
end_location TEXT,
|
||||||
booking_id TEXT,
|
booking_id TEXT,
|
||||||
total_cost REAL,
|
total_cost REAL,
|
||||||
description TEXT,
|
description TEXT,
|
||||||
@@ -503,12 +514,14 @@ export function runMigrations(db: Database): void {
|
|||||||
INSERT INTO operator_tour_day_plans__new (
|
INSERT INTO operator_tour_day_plans__new (
|
||||||
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
|
||||||
)
|
)
|
||||||
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
|
||||||
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');
|
||||||
@@ -534,6 +547,8 @@ export function runMigrations(db: Database): void {
|
|||||||
'end_date TEXT',
|
'end_date TEXT',
|
||||||
'end_time TEXT',
|
'end_time TEXT',
|
||||||
'end_timezone TEXT',
|
'end_timezone TEXT',
|
||||||
|
'start_location TEXT',
|
||||||
|
'end_location TEXT',
|
||||||
'booking_id TEXT',
|
'booking_id TEXT',
|
||||||
'total_cost REAL',
|
'total_cost REAL',
|
||||||
'description TEXT',
|
'description TEXT',
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ export interface OtherTransport {
|
|||||||
end_date: string | null;
|
end_date: string | null;
|
||||||
end_time: string | null;
|
end_time: string | null;
|
||||||
end_timezone: string | null;
|
end_timezone: string | null;
|
||||||
|
start_location: string | null;
|
||||||
|
end_location: string | null;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
}
|
}
|
||||||
@@ -28,6 +30,8 @@ export interface CreateOtherTransportInput {
|
|||||||
endDate?: string;
|
endDate?: string;
|
||||||
endTime?: string;
|
endTime?: string;
|
||||||
endTimezone?: string;
|
endTimezone?: string;
|
||||||
|
startLocation?: string;
|
||||||
|
endLocation?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateOtherTransportInput {
|
export interface UpdateOtherTransportInput {
|
||||||
@@ -42,6 +46,8 @@ export interface UpdateOtherTransportInput {
|
|||||||
endDate?: string;
|
endDate?: string;
|
||||||
endTime?: string;
|
endTime?: string;
|
||||||
endTimezone?: string;
|
endTimezone?: string;
|
||||||
|
startLocation?: string;
|
||||||
|
endLocation?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createOtherTransport(input: CreateOtherTransportInput): OtherTransport {
|
export function createOtherTransport(input: CreateOtherTransportInput): OtherTransport {
|
||||||
@@ -72,8 +78,9 @@ export function createOtherTransport(input: CreateOtherTransportInput): OtherTra
|
|||||||
`INSERT INTO other_transports (
|
`INSERT INTO other_transports (
|
||||||
id, plan_id,
|
id, plan_id,
|
||||||
start_date, start_time, start_timezone,
|
start_date, start_time, start_timezone,
|
||||||
end_date, end_time, end_timezone
|
end_date, end_time, end_timezone,
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
start_location, end_location
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
[
|
[
|
||||||
otherTransportId,
|
otherTransportId,
|
||||||
planId,
|
planId,
|
||||||
@@ -82,7 +89,9 @@ export function createOtherTransport(input: CreateOtherTransportInput): OtherTra
|
|||||||
input.startTimezone ?? null,
|
input.startTimezone ?? null,
|
||||||
input.endDate ?? null,
|
input.endDate ?? null,
|
||||||
input.endTime ?? null,
|
input.endTime ?? null,
|
||||||
input.endTimezone ?? null
|
input.endTimezone ?? null,
|
||||||
|
input.startLocation?.trim() || null,
|
||||||
|
input.endLocation?.trim() || null
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -100,18 +109,14 @@ export function updateOtherTransport(input: UpdateOtherTransportInput): void {
|
|||||||
|
|
||||||
db.run(
|
db.run(
|
||||||
`UPDATE plans SET status = ?, title = ?, notes = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`,
|
`UPDATE plans SET status = ?, title = ?, notes = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?`,
|
||||||
[
|
[input.status ?? 'idea', input.title.trim(), input.notes?.trim() || null, existing.plan_id]
|
||||||
input.status ?? 'idea',
|
|
||||||
input.title.trim(),
|
|
||||||
input.notes?.trim() || null,
|
|
||||||
existing.plan_id
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
db.run(
|
db.run(
|
||||||
`UPDATE other_transports
|
`UPDATE other_transports
|
||||||
SET start_date = ?, start_time = ?, start_timezone = ?,
|
SET start_date = ?, start_time = ?, start_timezone = ?,
|
||||||
end_date = ?, end_time = ?, end_timezone = ?,
|
end_date = ?, end_time = ?, end_timezone = ?,
|
||||||
|
start_location = ?, end_location = ?,
|
||||||
updated_at = CURRENT_TIMESTAMP
|
updated_at = CURRENT_TIMESTAMP
|
||||||
WHERE id = ?`,
|
WHERE id = ?`,
|
||||||
[
|
[
|
||||||
@@ -121,6 +126,8 @@ export function updateOtherTransport(input: UpdateOtherTransportInput): void {
|
|||||||
input.endDate ?? null,
|
input.endDate ?? null,
|
||||||
input.endTime ?? null,
|
input.endTime ?? null,
|
||||||
input.endTimezone ?? null,
|
input.endTimezone ?? null,
|
||||||
|
input.startLocation?.trim() || null,
|
||||||
|
input.endLocation?.trim() || null,
|
||||||
input.otherTransportId
|
input.otherTransportId
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -339,7 +339,9 @@ export function cloneTemplateDaysToTour(input: {
|
|||||||
startTimezone: dp.start_timezone ?? undefined,
|
startTimezone: dp.start_timezone ?? undefined,
|
||||||
endDate: dp.end_date ?? undefined,
|
endDate: dp.end_date ?? undefined,
|
||||||
endTime: dp.end_time ?? undefined,
|
endTime: dp.end_time ?? undefined,
|
||||||
endTimezone: dp.end_timezone ?? undefined
|
endTimezone: dp.end_timezone ?? undefined,
|
||||||
|
startLocation: dp.start_location ?? undefined,
|
||||||
|
endLocation: dp.end_location ?? undefined
|
||||||
});
|
});
|
||||||
} else if (dp.type === 'activity' || dp.type === 'restaurant') {
|
} else if (dp.type === 'activity' || dp.type === 'restaurant') {
|
||||||
createExperience({
|
createExperience({
|
||||||
|
|||||||
@@ -100,7 +100,31 @@ export const actions: Actions = {
|
|||||||
dayNumber: number;
|
dayNumber: number;
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
dayPlans?: { type: 'transport' | 'lodging'; title: string; notes?: string | null }[];
|
dayPlans?: Array<{
|
||||||
|
type: 'transport' | 'lodging';
|
||||||
|
title: string;
|
||||||
|
notes?: string | null;
|
||||||
|
lodgingFields?: {
|
||||||
|
chain?: string | null;
|
||||||
|
address_line1?: string | null;
|
||||||
|
address_line2?: string | null;
|
||||||
|
city_name?: string | null;
|
||||||
|
country?: string | null;
|
||||||
|
country_code?: string | null;
|
||||||
|
postal_code?: string | null;
|
||||||
|
};
|
||||||
|
transportFields?: {
|
||||||
|
transport_kind?: string | null;
|
||||||
|
start_location?: string | null;
|
||||||
|
end_location?: string | null;
|
||||||
|
start_date?: string | null;
|
||||||
|
start_time?: string | null;
|
||||||
|
start_timezone?: string | null;
|
||||||
|
end_date?: string | null;
|
||||||
|
end_time?: string | null;
|
||||||
|
end_timezone?: string | null;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
}[] = [];
|
}[] = [];
|
||||||
|
|
||||||
if (provider && providerId) {
|
if (provider && providerId) {
|
||||||
@@ -126,7 +150,8 @@ export const actions: Actions = {
|
|||||||
plan.type,
|
plan.type,
|
||||||
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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,6 +111,8 @@ export const actions: Actions = {
|
|||||||
end_date: string | null;
|
end_date: string | null;
|
||||||
end_time: string | null;
|
end_time: string | null;
|
||||||
end_timezone: string | null;
|
end_timezone: string | null;
|
||||||
|
start_location: string | null;
|
||||||
|
end_location: string | null;
|
||||||
}
|
}
|
||||||
| undefined;
|
| undefined;
|
||||||
let experienceFields:
|
let experienceFields:
|
||||||
@@ -175,7 +177,9 @@ export const actions: Actions = {
|
|||||||
start_timezone: null,
|
start_timezone: null,
|
||||||
end_date: arrDate,
|
end_date: arrDate,
|
||||||
end_time: arrTime,
|
end_time: arrTime,
|
||||||
end_timezone: null
|
end_timezone: null,
|
||||||
|
start_location: null,
|
||||||
|
end_location: null
|
||||||
};
|
};
|
||||||
} else if (transportationType === 'private_vehicle') {
|
} else if (transportationType === 'private_vehicle') {
|
||||||
const startAddress = get('start_address');
|
const startAddress = get('start_address');
|
||||||
@@ -188,7 +192,9 @@ export const actions: Actions = {
|
|||||||
start_timezone: get('departure_timezone') ?? null,
|
start_timezone: get('departure_timezone') ?? null,
|
||||||
end_date: get('arrival_date') ?? null,
|
end_date: get('arrival_date') ?? null,
|
||||||
end_time: get('arrival_time') ?? null,
|
end_time: get('arrival_time') ?? null,
|
||||||
end_timezone: get('arrival_timezone') ?? null
|
end_timezone: get('arrival_timezone') ?? null,
|
||||||
|
start_location: startAddress ?? null,
|
||||||
|
end_location: endAddress ?? null
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
transportFields = {
|
transportFields = {
|
||||||
@@ -198,7 +204,9 @@ export const actions: Actions = {
|
|||||||
start_timezone: get('start_timezone') ?? null,
|
start_timezone: get('start_timezone') ?? null,
|
||||||
end_date: get('end_date') ?? null,
|
end_date: get('end_date') ?? null,
|
||||||
end_time: get('end_time') ?? null,
|
end_time: get('end_time') ?? null,
|
||||||
end_timezone: get('end_timezone') ?? null
|
end_timezone: get('end_timezone') ?? null,
|
||||||
|
start_location: get('start_location') ?? null,
|
||||||
|
end_location: get('end_location') ?? null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -290,6 +298,8 @@ export const actions: Actions = {
|
|||||||
end_date: get('end_date') ?? undefined,
|
end_date: get('end_date') ?? undefined,
|
||||||
end_time: get('end_time') ?? undefined,
|
end_time: get('end_time') ?? undefined,
|
||||||
end_timezone: get('end_timezone') ?? undefined,
|
end_timezone: get('end_timezone') ?? undefined,
|
||||||
|
start_location: get('start_location') ?? undefined,
|
||||||
|
end_location: get('end_location') ?? undefined,
|
||||||
booking_id: get('booking_id') ?? undefined,
|
booking_id: get('booking_id') ?? undefined,
|
||||||
total_cost: (() => {
|
total_cost: (() => {
|
||||||
const raw = get('total_cost');
|
const raw = get('total_cost');
|
||||||
|
|||||||
@@ -195,6 +195,8 @@
|
|||||||
end_date: editingTransportPlan.end_date ?? null,
|
end_date: editingTransportPlan.end_date ?? null,
|
||||||
end_time: editingTransportPlan.end_time ?? null,
|
end_time: editingTransportPlan.end_time ?? null,
|
||||||
end_timezone: editingTransportPlan.end_timezone ?? null,
|
end_timezone: editingTransportPlan.end_timezone ?? null,
|
||||||
|
start_location: editingTransportPlan.start_location ?? null,
|
||||||
|
end_location: editingTransportPlan.end_location ?? null,
|
||||||
created_at: '',
|
created_at: '',
|
||||||
updated_at: ''
|
updated_at: ''
|
||||||
}
|
}
|
||||||
@@ -474,6 +476,8 @@
|
|||||||
end_date: plan.end_date ?? null,
|
end_date: plan.end_date ?? null,
|
||||||
end_time: plan.end_time ?? null,
|
end_time: plan.end_time ?? null,
|
||||||
end_timezone: plan.end_timezone ?? null,
|
end_timezone: plan.end_timezone ?? null,
|
||||||
|
start_location: plan.start_location ?? null,
|
||||||
|
end_location: plan.end_location ?? null,
|
||||||
created_at: '',
|
created_at: '',
|
||||||
updated_at: ''
|
updated_at: ''
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -279,6 +279,8 @@ export const actions: Actions = {
|
|||||||
if (transportationType === 'other') {
|
if (transportationType === 'other') {
|
||||||
const title = (data.get('title') as string)?.trim();
|
const title = (data.get('title') as string)?.trim();
|
||||||
const notes = (data.get('notes') as string)?.trim() || undefined;
|
const notes = (data.get('notes') as string)?.trim() || undefined;
|
||||||
|
const startLocation = (data.get('start_location') as string)?.trim() || undefined;
|
||||||
|
const endLocation = (data.get('end_location') as string)?.trim() || undefined;
|
||||||
const startDate = (data.get('start_date') as string)?.trim() || undefined;
|
const startDate = (data.get('start_date') as string)?.trim() || undefined;
|
||||||
const startTime = (data.get('start_time') as string)?.trim() || undefined;
|
const startTime = (data.get('start_time') as string)?.trim() || undefined;
|
||||||
const startTimezone = (data.get('start_timezone') as string)?.trim() || undefined;
|
const startTimezone = (data.get('start_timezone') as string)?.trim() || undefined;
|
||||||
@@ -296,6 +298,8 @@ export const actions: Actions = {
|
|||||||
status: ['idea', 'tentative', 'confirmed'].includes(status) ? status : 'idea',
|
status: ['idea', 'tentative', 'confirmed'].includes(status) ? status : 'idea',
|
||||||
title,
|
title,
|
||||||
notes,
|
notes,
|
||||||
|
startLocation,
|
||||||
|
endLocation,
|
||||||
startDate,
|
startDate,
|
||||||
startTime,
|
startTime,
|
||||||
startTimezone,
|
startTimezone,
|
||||||
@@ -484,6 +488,8 @@ export const actions: Actions = {
|
|||||||
const otherTransportId = (data.get('other_transport_id') as string)?.trim();
|
const otherTransportId = (data.get('other_transport_id') as string)?.trim();
|
||||||
const title = (data.get('title') as string)?.trim();
|
const title = (data.get('title') as string)?.trim();
|
||||||
const notes = (data.get('notes') as string)?.trim() || undefined;
|
const notes = (data.get('notes') as string)?.trim() || undefined;
|
||||||
|
const startLocation = (data.get('start_location') as string)?.trim() || undefined;
|
||||||
|
const endLocation = (data.get('end_location') as string)?.trim() || undefined;
|
||||||
const startDate = (data.get('start_date') as string)?.trim() || undefined;
|
const startDate = (data.get('start_date') as string)?.trim() || undefined;
|
||||||
const startTime = (data.get('start_time') as string)?.trim() || undefined;
|
const startTime = (data.get('start_time') as string)?.trim() || undefined;
|
||||||
const startTimezone = (data.get('start_timezone') as string)?.trim() || undefined;
|
const startTimezone = (data.get('start_timezone') as string)?.trim() || undefined;
|
||||||
@@ -501,6 +507,8 @@ export const actions: Actions = {
|
|||||||
status: ['idea', 'tentative', 'confirmed'].includes(status) ? status : 'idea',
|
status: ['idea', 'tentative', 'confirmed'].includes(status) ? status : 'idea',
|
||||||
title,
|
title,
|
||||||
notes,
|
notes,
|
||||||
|
startLocation,
|
||||||
|
endLocation,
|
||||||
startDate,
|
startDate,
|
||||||
startTime,
|
startTime,
|
||||||
startTimezone,
|
startTimezone,
|
||||||
|
|||||||
Reference in New Issue
Block a user