mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-09 10:58:14 +00:00
feat(group-booking): draft schedules, coach-scoped seating, mixed classes
This commit is contained in:
@@ -70,10 +70,10 @@ function emptySearchMessage(reason: SearchEmptyReason | undefined): string {
|
||||
case 'PACKAGE_ONLY':
|
||||
return `Departures on this date are reserved for travel packages, not regular ticketing.`;
|
||||
case 'GROUP_BOOKING_ONLY':
|
||||
// isGroupBookingOnly is an exclusive partition — this page only ever sees group-booking
|
||||
// schedules, so an empty result here means a regular (non-group) train runs on this date
|
||||
// but nothing has been set up for group booking specifically.
|
||||
return `A regular train runs from ${o} to ${d} on this date, but no schedule has been set up for group booking yet — ask fleet/schedule management to create one, or try another date.`;
|
||||
// The staff channel is a superset of the public one — it sees ordinary, group-reserved
|
||||
// and unpublished DRAFT trips alike — so the backend never returns this code here. Kept
|
||||
// only so the switch stays exhaustive against the shared SearchEmptyReasonCode union.
|
||||
return `No departures from ${o} to ${d} are available for booking on this date.`;
|
||||
case 'CHECKIN_CLOSED':
|
||||
return `Check-in has already closed for every departure on this date.`;
|
||||
case 'FULLY_BOOKED':
|
||||
@@ -285,7 +285,8 @@ function GroupBookingPageContent() {
|
||||
journeyType: tripType,
|
||||
returnDate: tripType === 'ROUND_TRIP' ? returnDate : undefined,
|
||||
nationality: fareTier === 'LOCAL' ? 'Ethiopian' : 'Other',
|
||||
channel: 'GROUP_BOOKING',
|
||||
// The staff channel comes from the route groupBookingApi.searchTrips calls
|
||||
// (POST /search/group), not from a field in the body.
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -13,10 +13,9 @@ export interface SearchTripsRequest {
|
||||
returnDate?: string;
|
||||
/** Drives which fare tier (Local vs International) gets quoted — see fareTier in the page component. */
|
||||
nationality?: string;
|
||||
/** Always 'GROUP_BOOKING' for this app — search returns ONLY schedules marked
|
||||
* isGroupBookingOnly (an exclusive partition, not additive): normal passenger-facing
|
||||
* schedules never show up here, and group-only schedules never show up in the portal. */
|
||||
channel?: 'PORTAL' | 'GROUP_BOOKING';
|
||||
// There is no `channel` field any more. It used to select the staff view from the request
|
||||
// BODY of the public `POST /search`, which meant anyone could ask for it. The channel is now
|
||||
// decided by the route: `POST /search/group` is permission-guarded (see searchTrips below).
|
||||
}
|
||||
|
||||
export interface ScheduleClassOption {
|
||||
@@ -227,8 +226,14 @@ export interface InitiatePaymentResponse {
|
||||
}
|
||||
|
||||
export const groupBookingApi = {
|
||||
/**
|
||||
* The staff search channel. Same body and response as the public `POST /search`, but this
|
||||
* route also returns unpublished DRAFT trips and trips reserved for groups — and, unlike
|
||||
* before, ordinary scheduled trips too, so a group can be booked onto a normal service.
|
||||
* Requires bookings:create / bookings:manage; the shared apiClient sends the IAM token.
|
||||
*/
|
||||
searchTrips: (dto: SearchTripsRequest) =>
|
||||
apiClient.post<SearchTripsResponse>('/search', dto),
|
||||
apiClient.post<SearchTripsResponse>('/search/group', dto),
|
||||
|
||||
getSeatClasses: () => apiClient.get<SeatClassOption[]>('/seat-classes'),
|
||||
|
||||
|
||||
Reference in New Issue
Block a user