← Files WixARCHIVED FILE

skills/wix-headless-templates/restaurants/project/src/wix/restaurants/reservations.ts

5.53 KB · Oct 8, 2026 · 12:02 UTC

↓ Download file

See the change to this file →

// Table reservations (@wix/table-reservations) over the SDK — the only file that touches raw
// reservation entities on this transport. Rules and mappers live in ./reservations-core (shared
// with the REST twin in templates/restaurants/rest/); this file is the transport only. Copy
// as-is; extend by calling these exports, never by editing them.
//
// getTimeSlots takes POSITIONAL args and a Date (not an ISO string); reserveReservation takes
// THREE positional args (id, reservee, revision) and the only exit from HELD is reserve;
// createReservation (manual approval, no hold) returns the reservation itself, unwrapped.
// Failures are loud — surface the message, don't swallow it.
// docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/reservation-locations/list-reservation-locations.md
// docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/time-slots/get-time-slots.md
// docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/reservations/create-held-reservation.md
// docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/reservations/reserve-reservation.md
// docs: https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/reservations/create-reservation.md
import { reservationLocations, timeSlots, reservations as reservationsModule } from "@wix/table-reservations";
import { wixModule } from "../sdk";
import { availableSlots, reservationBody, reserveeBody, toConfirmation, toHold, toLocations } from "./reservations-core";
import type { Raw } from "./menu-core";
import type {
  ReservationConfirmation,
  ReservationFormConfig,
  ReservationHold,
  ReservationLocationInfo,
  ReservationReservee,
  ReservationSlot,
} from "./types";

const locationsApi = wixModule(reservationLocations);
const timeSlotsApi = wixModule(timeSlots);
const reservationsApi = wixModule(reservationsModule);

/**
 * The reservation locations to offer: those with online reservations ENABLED (default first),
 * or only the default one when none is enabled — then `onlineReservationsEnabled: false` means
 * the premium-gated toggle is off and the surface renders an honest "reservations aren't open
 * yet" state. [] when Table Reservations isn't set up. Each carries its name, address, timezone,
 * approval rule, and the owner's form configuration.
 */
export async function fetchReservationLocations(): Promise<ReservationLocationInfo[]> {
  const res: Raw = await locationsApi.listReservationLocations();
  return toLocations(res.reservationLocations ?? []);
}

/**
 * AVAILABLE reservation slots around a moment for a party size — UNAVAILABLE and
 * NON_WORKING_HOURS slots are already filtered out (offering them makes the hold fail).
 * `aroundIso` anchors the fan-out (build it in the location's zone with `zonedIso`);
 * `timeZone` is the location's — labels and day keys are computed in it.
 */
export async function fetchReservationSlots(
  locationId: string,
  aroundIso: string,
  partySize: number,
  { timeZone = "", slotsBefore = 6, slotsAfter = 6 }: { timeZone?: string; slotsBefore?: number; slotsAfter?: number } = {},
): Promise<ReservationSlot[]> {
  // The date param is a Date — the SDK types it as Date, not the ISO string the docs show.
  const res: Raw = await timeSlotsApi.getTimeSlots(locationId, new Date(aroundIso), partySize, { slotsBefore, slotsAfter });
  return availableSlots(res.timeSlots ?? [], timeZone);
}

/**
 * Hold a slot for 10 minutes while the visitor enters their details (AUTOMATIC approval only).
 * The returned hold carries the { reservationId, revision } that completeReservation NEEDS —
 * keep both — and `expiresAtIso` for a countdown.
 */
export async function holdReservation(locationId: string, startIso: string, partySize: number): Promise<ReservationHold> {
  const res: Raw = await reservationsApi.createHeldReservation({
    reservationLocationId: locationId,
    startDate: new Date(startIso),
    partySize,
  });
  return toHold(res, startIso, partySize);
}

/**
 * Complete a held reservation with the visitor's details, validated against the location's
 * `form` (firstName + phone always; lastName/email/custom fields as configured). A hold expires
 * after 10 minutes — on failure, start a fresh hold; never try to update a HELD reservation by
 * other means. The outcome carries the API's real status.
 */
export async function completeReservation(
  hold: Pick<ReservationHold, "reservationId" | "revision">,
  reservee: ReservationReservee,
  form: ReservationFormConfig,
): Promise<ReservationConfirmation> {
  const body = reserveeBody(reservee, form);
  const res: Raw = await reservationsApi.reserveReservation(hold.reservationId, body as any, hold.revision);
  return toConfirmation(res);
}

/**
 * Request a reservation that needs the restaurant's approval (MANUAL, or a large party under
 * MANUAL_FOR_LARGE_PARTIES): ONE createReservation with details + reservee, no hold — the path
 * Wix's own reservation code takes. The outcome is normally REQUESTED (pending), never confirmed.
 */
export async function requestReservation(
  locationId: string,
  startIso: string,
  partySize: number,
  reservee: ReservationReservee,
  form: ReservationFormConfig,
): Promise<ReservationConfirmation> {
  const body = reservationBody(locationId, startIso, partySize, reservee, form);
  const reservation: Raw = await reservationsApi.createReservation({
    details: { ...body.details, startDate: new Date(startIso) },
    reservee: body.reservee,
  } as any);
  return toConfirmation({ reservation });
}

SHA-256: 9f54eea12acb0afe83022e099164e471efaf9bc1720f904d5abdd015f7b65f08