Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@peektravel/app-utilities",
"version": "0.2.9",
"version": "0.2.10",
"description": "GraphQL JS mapping utilities extracted from the Peek Pro Autopilot connector.",
"license": "MIT",
"repository": {
Expand Down
16 changes: 16 additions & 0 deletions src/internal/bookings/booking-converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@ import type {
ResourcePoolAssignment,
Ticket,
} from "../../models/booking.js";
import { normalizeBookingId } from "./booking-queries.js";
import type { BookingGuestNode, BookingNode } from "./booking-queries.js";

const UNKNOWN = "unknown";

const PEEK_PRO_DEEP_LINK_BASE = "http://pro-app.peek.com/-/order";

const SOURCE_SOURCE_MAP: Record<string, string> = {
APP_REGISTRY: "app",
BOOKING_IMPORTER_FUTURE: "importer",
Expand Down Expand Up @@ -160,6 +163,7 @@ export function fromBookingNode(
resellerName: resellerNameFromChannelSnapshot(data.order?.channelSnapshot),

orderId: data.order?.id || "",
peekProBookingDeepLink: buildPeekProBookingDeepLink(data.order?.id, data.id),

convenienceFee: includePriceBreakdown ? mapPrice(data.value?.convenienceFee) : undefined,
deposit: includePriceBreakdown ? mapPrice(data.value?.deposit) : undefined,
Expand Down Expand Up @@ -223,6 +227,18 @@ export function mapGuestNode(guestNode: BookingGuestNode, isPrimary: boolean): G
};
}

/**
* Builds the Peek Pro app deep link from an order id and booking id. Both ids
* are normalized (lowercased, `-` → `_`); returns `""` when either is absent.
*/
function buildPeekProBookingDeepLink(
orderId: string | null | undefined,
bookingId: string | null | undefined,
): string {
if (!orderId || !bookingId) return "";
return `${PEEK_PRO_DEEP_LINK_BASE}%2F${normalizeBookingId(orderId)}%3FsaleId=${normalizeBookingId(bookingId)}`;
}

function sourceFromApp(app: string | null): string {
if (!app) return UNKNOWN;
return SOURCE_SOURCE_MAP[app] ?? UNKNOWN;
Expand Down
6 changes: 6 additions & 0 deletions src/models/booking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,12 @@ export interface Booking {
/** The order id this booking belongs to. `""` if absent. */
orderId: string;

/**
* Deep link into the Peek Pro app for this booking, derived from the order id
* and booking id. `""` when either id is absent.
*/
peekProBookingDeepLink: string;

/** Custom question answers captured at the booking level. */
customQuestionAnswers: CustomQuestionAnswer[];
/** Custom question answers captured per guest/ticket. */
Expand Down
14 changes: 14 additions & 0 deletions test/bookings/booking-converter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ describe("fromBookingNode", () => {
expect(booking.resellerId).toBe("ch-1");
expect(booking.resellerName).toBe("Acme - Jane");
expect(booking.orderId).toBe("o_1");
expect(booking.peekProBookingDeepLink).toBe(
"http://pro-app.peek.com/-/order%2Fo_1%3FsaleId=b_1",
);
expect(booking.convenienceFee).toEqual({ amount: "2.00", display: "$2.00" });
expect(booking.price).toEqual({ amount: "90.00", display: "$90.00" });
expect(booking.taxes).toBeUndefined();
Expand Down Expand Up @@ -189,6 +192,7 @@ describe("fromBookingNode", () => {
resellerId: null,
resellerName: null,
orderId: "",
peekProBookingDeepLink: "",
customQuestionAnswers: [],
customGuestQuestionAnswers: [],
});
Expand All @@ -209,6 +213,16 @@ describe("fromBookingNode", () => {
expect(booking.isRentalProduct).toBe(false);
});

it("normalizes ids when building the Peek Pro deep link", () => {
const node: BookingNode = {
id: "B-2",
order: { id: "O-2" },
};
expect(fromBookingNode(node).peekProBookingDeepLink).toBe(
"http://pro-app.peek.com/-/order%2Fo_2%3FsaleId=b_2",
);
});

it("uses just the channel name when there is no agent", () => {
const node: BookingNode = {
order: { channelSnapshot: { id: "ch", name: "Acme", agent: null } },
Expand Down
Loading