mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-21 06:11:45 +00:00
feat(reservations): native booking-confirmation import via KDE KItinerary (#1102)
* feat(reservations): native booking-confirmation import via KDE KItinerary
Adds a two-step preview → confirm flow for importing booking emails,
PDFs, PKPass and HTML confirmations. The server invokes the KDE
kitinerary-extractor binary, maps JSON-LD schema.org output to TREK
reservation shapes, and persists via the existing createReservation
pipeline (accommodations, budget, places, WebSocket broadcasts).
- NestJS BookingImportModule: preview + confirm endpoints under
/api/trips/:tripId/reservations/import/booking{,/confirm}
- KitineraryExtractorService: spawns the binary, filters stderr noise,
handles QDateTime (@value) timezone-aware datetimes
- kitinerary-mapper: FlightReservation, TrainReservation, BusReservation,
BoatReservation, LodgingReservation, FoodEstablishmentReservation,
RentalCarReservation, EventReservation → typed preview items
- BookingImportService: auto-creates place rows; geocodes venues without
coordinates via Nominatim (name+address → address → name fallback);
resolves day IDs for accommodation linking
- BookingImportModal: drag-and-drop multi-file upload, preview cards
with type icons, per-item exclude toggle, confirm step
- Shared Zod contracts: BookingImportPreviewItem, PreviewResponse,
ConfirmRequest, ConfirmResponse — consumed by controller, service,
API client and modal
- Dockerfile: node:24-trixie-slim runtime; amd64 downloads KDE static
binary + locales; arm64 installs libkitinerary-bin + symlinks to
fixed path; ENV KITINERARY_EXTRACTOR_PATH set for both arches
- /api/health/features exposes { bookingImport: boolean } so the UI
hides the Import button when the binary is absent
- i18n keys (English), wiki docs, API.md, README one-liner
* i18n: add booking import translations for all 19 non-English locales
Adds 17 reservations.import.* keys and undo.importBooking to ar, br, cs,
de, es, fr, gr, hu, id, it, ja, ko, nl, pl, ru, tr, uk, zh, zh-TW.
* chore: enforce i18n parity
* docs(wiki): add KItinerary local setup instructions to dev environment guide
This commit is contained in:
@@ -141,3 +141,66 @@ export const accommodationUpdateRequestSchema = open;
|
||||
export type AccommodationUpdateRequest = z.infer<
|
||||
typeof accommodationUpdateRequestSchema
|
||||
>;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Booking import (KItinerary)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const bookingImportEndpointSchema = z.object({
|
||||
role: z.enum(['from', 'to', 'stop']),
|
||||
sequence: z.number(),
|
||||
name: z.string(),
|
||||
code: z.string().nullable(),
|
||||
lat: z.number(),
|
||||
lng: z.number(),
|
||||
timezone: z.string().nullable(),
|
||||
local_time: z.string().nullable(),
|
||||
local_date: z.string().nullable(),
|
||||
});
|
||||
|
||||
const bookingImportVenueSchema = z.object({
|
||||
name: z.string(),
|
||||
lat: z.number().optional(),
|
||||
lng: z.number().optional(),
|
||||
address: z.string().optional(),
|
||||
website: z.string().optional(),
|
||||
phone: z.string().optional(),
|
||||
});
|
||||
|
||||
const bookingImportAccommodationSchema = z.object({
|
||||
check_in: z.string().optional(),
|
||||
check_out: z.string().optional(),
|
||||
confirmation: z.string().optional(),
|
||||
});
|
||||
|
||||
export const bookingImportPreviewItemSchema = z.object({
|
||||
type: z.string(),
|
||||
title: z.string().min(1),
|
||||
reservation_time: z.string().nullable().optional(),
|
||||
reservation_end_time: z.string().nullable().optional(),
|
||||
confirmation_number: z.string().nullable().optional(),
|
||||
location: z.string().nullable().optional(),
|
||||
metadata: z.record(z.string(), z.unknown()).optional(),
|
||||
endpoints: z.array(bookingImportEndpointSchema).optional(),
|
||||
needs_review: z.boolean().optional(),
|
||||
_venue: bookingImportVenueSchema.optional(),
|
||||
_accommodation: bookingImportAccommodationSchema.optional(),
|
||||
source: z.object({ fileName: z.string(), index: z.number() }),
|
||||
});
|
||||
export type BookingImportPreviewItem = z.infer<typeof bookingImportPreviewItemSchema>;
|
||||
|
||||
export const bookingImportPreviewResponseSchema = z.object({
|
||||
items: z.array(bookingImportPreviewItemSchema),
|
||||
warnings: z.array(z.string()),
|
||||
});
|
||||
export type BookingImportPreviewResponse = z.infer<typeof bookingImportPreviewResponseSchema>;
|
||||
|
||||
export const bookingImportConfirmRequestSchema = z.object({
|
||||
items: z.array(bookingImportPreviewItemSchema).min(1),
|
||||
});
|
||||
export type BookingImportConfirmRequest = z.infer<typeof bookingImportConfirmRequestSchema>;
|
||||
|
||||
export const bookingImportConfirmResponseSchema = z.object({
|
||||
created: z.array(reservationSchema),
|
||||
});
|
||||
export type BookingImportConfirmResponse = z.infer<typeof bookingImportConfirmResponseSchema>;
|
||||
|
||||
Reference in New Issue
Block a user