mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-09 05:58:18 +00:00
Merge pull request #87 from Tria-plc/alpha
Add endpoint to retrieve bookings by device ID and update related API…
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { Body, Controller, Delete, Get, Param, Post, Patch, UseGuards, Query, Req } from '@nestjs/common';
|
import { Body, Controller, Delete, Get, Param, Post, Patch, UseGuards, Query, Req, BadRequestException } from '@nestjs/common';
|
||||||
import { ApiTags, ApiOperation, ApiBearerAuth, ApiResponse, ApiQuery } from '@nestjs/swagger';
|
import { ApiTags, ApiOperation, ApiBearerAuth, ApiResponse, ApiQuery } from '@nestjs/swagger';
|
||||||
import { BookingsService } from './bookings.service';
|
import { BookingsService } from './bookings.service';
|
||||||
import { GuestBookingService } from './guest-booking.service';
|
import { GuestBookingService } from './guest-booking.service';
|
||||||
@@ -15,7 +15,7 @@ export class BookingsController {
|
|||||||
private guestService: GuestBookingService,
|
private guestService: GuestBookingService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Get('my/bookings')
|
@Get('my')
|
||||||
@UseGuards(JwtGuard)
|
@UseGuards(JwtGuard)
|
||||||
@ApiBearerAuth('JWT-auth')
|
@ApiBearerAuth('JWT-auth')
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
@@ -44,6 +44,34 @@ export class BookingsController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get('by-device')
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'Get bookings by device ID',
|
||||||
|
description: 'Returns all bookings associated with a device ID (for guest users). Includes saved passenger details and booking history.'
|
||||||
|
})
|
||||||
|
@ApiQuery({ name: 'deviceId', required: true, description: 'Device identifier' })
|
||||||
|
@ApiQuery({ name: 'search', required: false, description: 'Search by booking reference or station names' })
|
||||||
|
@ApiQuery({ name: 'status', required: false, description: 'Filter by booking status' })
|
||||||
|
@ApiQuery({ name: 'page', required: false, description: 'Page number' })
|
||||||
|
@ApiQuery({ name: 'pageSize', required: false, description: 'Items per page' })
|
||||||
|
@ApiResponse({ status: 200, description: 'List of guest bookings and saved passengers for device' })
|
||||||
|
@ApiResponse({ status: 400, description: 'Device ID is required' })
|
||||||
|
getByDevice(
|
||||||
|
@Query('deviceId') deviceId?: string,
|
||||||
|
@Query('search') search?: string,
|
||||||
|
@Query('status') status?: string,
|
||||||
|
@Query('page') page?: string,
|
||||||
|
@Query('pageSize') pageSize?: string,
|
||||||
|
) {
|
||||||
|
if (!deviceId) throw new BadRequestException('Device ID is required');
|
||||||
|
return this.service.findByDeviceId(deviceId, {
|
||||||
|
search,
|
||||||
|
status,
|
||||||
|
page: page ? parseInt(page) : 1,
|
||||||
|
pageSize: pageSize ? parseInt(pageSize) : 20
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: 'List all bookings with filters (Admin/Agent)',
|
summary: 'List all bookings with filters (Admin/Agent)',
|
||||||
|
|||||||
@@ -102,6 +102,86 @@ export class BookingsService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async findByDeviceId(deviceId: string, filters: BookingFilters = {}) {
|
||||||
|
const { search, status, page = 1, pageSize = 20 } = filters;
|
||||||
|
const skip = (page - 1) * pageSize;
|
||||||
|
|
||||||
|
const where: any = { passenger: { user: { devices: { some: { id: deviceId } } } } };
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
where.OR = [
|
||||||
|
{ bookingRef: { contains: search, mode: 'insensitive' } },
|
||||||
|
{ schedule: { originStation: { name: { contains: search, mode: 'insensitive' } } } },
|
||||||
|
{ schedule: { destinationStation: { name: { contains: search, mode: 'insensitive' } } } },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
where.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [items, total] = await Promise.all([
|
||||||
|
this.prisma.booking.findMany({
|
||||||
|
where,
|
||||||
|
skip,
|
||||||
|
take: pageSize,
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
include: {
|
||||||
|
schedule: { include: { originStation: true, destinationStation: true, train: true } },
|
||||||
|
paymentIntent: true,
|
||||||
|
seats: { include: { seat: true } },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
this.prisma.booking.count({ where }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const savedPassengers = await this.prisma.savedPassengerProfile.findMany({
|
||||||
|
where: { deviceId },
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
bookings: items.map(booking => ({
|
||||||
|
id: booking.id,
|
||||||
|
bookingRef: booking.bookingRef,
|
||||||
|
status: booking.status,
|
||||||
|
totalMinor: booking.totalMinor,
|
||||||
|
currency: 'ETB',
|
||||||
|
displayCurrency: booking.displayCurrency,
|
||||||
|
displayTotalMinor: booking.displayTotalMinor,
|
||||||
|
adultCount: booking.adultCount,
|
||||||
|
childCount: booking.childCount,
|
||||||
|
createdAt: booking.createdAt,
|
||||||
|
schedule: {
|
||||||
|
train: booking.schedule.train,
|
||||||
|
originStation: booking.schedule.originStation,
|
||||||
|
destinationStation: booking.schedule.destinationStation,
|
||||||
|
departureAt: booking.schedule.departureAt,
|
||||||
|
arrivalAt: booking.schedule.arrivalAt,
|
||||||
|
},
|
||||||
|
paymentIntent: booking.paymentIntent,
|
||||||
|
seatCount: booking.seats.length,
|
||||||
|
})),
|
||||||
|
savedPassengers: savedPassengers.map(p => ({
|
||||||
|
id: p.id,
|
||||||
|
passengerName: p.passengerName,
|
||||||
|
dateOfBirth: p.dateOfBirth.toISOString().split('T')[0],
|
||||||
|
idDocumentType: p.idDocumentType,
|
||||||
|
passportNumber: p.passportNumber || undefined,
|
||||||
|
passportCountry: p.passportCountry || undefined,
|
||||||
|
nationality: p.nationality || undefined,
|
||||||
|
phone: p.phone || undefined,
|
||||||
|
email: p.email || undefined,
|
||||||
|
})),
|
||||||
|
meta: {
|
||||||
|
page,
|
||||||
|
pageSize,
|
||||||
|
total,
|
||||||
|
totalPages: Math.ceil(total / pageSize),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async findAll(filters: BookingFilters = {}) {
|
async findAll(filters: BookingFilters = {}) {
|
||||||
const { search, status, page = 1, pageSize = 20 } = filters;
|
const { search, status, page = 1, pageSize = 20 } = filters;
|
||||||
const skip = (page - 1) * pageSize;
|
const skip = (page - 1) * pageSize;
|
||||||
|
|||||||
@@ -18,6 +18,29 @@ export const bookingsApi = {
|
|||||||
return Array.isArray(response) ? { items: response } : response;
|
return Array.isArray(response) ? { items: response } : response;
|
||||||
},
|
},
|
||||||
getById: (id: string) => apiClient.get<any>(`/bookings/${id}`),
|
getById: (id: string) => apiClient.get<any>(`/bookings/${id}`),
|
||||||
|
getMy: async (params?: any) => {
|
||||||
|
const cleanParams = Object.fromEntries(
|
||||||
|
Object.entries(params || {}).filter(([_, value]) => value !== '' && value !== undefined && value !== null)
|
||||||
|
) as Record<string, string>;
|
||||||
|
const query = new URLSearchParams(cleanParams).toString();
|
||||||
|
const response = await apiClient.get<any>(`/bookings/my${query ? `?${query}` : ''}`);
|
||||||
|
if (response?.data) {
|
||||||
|
return Array.isArray(response.data) ? { items: response.data } : response;
|
||||||
|
}
|
||||||
|
return Array.isArray(response) ? { items: response } : response;
|
||||||
|
},
|
||||||
|
getByDevice: async (deviceId: string, params?: any) => {
|
||||||
|
const cleanParams = Object.fromEntries(
|
||||||
|
Object.entries({ ...params }).filter(([_, value]) => value !== '' && value !== undefined && value !== null)
|
||||||
|
) as Record<string, string>;
|
||||||
|
cleanParams['deviceId'] = deviceId;
|
||||||
|
const query = new URLSearchParams(cleanParams).toString();
|
||||||
|
const response = await apiClient.get<any>(`/bookings/by-device${query ? `?${query}` : ''}`);
|
||||||
|
if (response?.data) {
|
||||||
|
return Array.isArray(response.data) ? { items: response.data } : response;
|
||||||
|
}
|
||||||
|
return Array.isArray(response) ? { items: response } : response;
|
||||||
|
},
|
||||||
cancel: (id: string, data?: any) => apiClient.post<any>(`/bookings/${id}/cancel`, data),
|
cancel: (id: string, data?: any) => apiClient.post<any>(`/bookings/${id}/cancel`, data),
|
||||||
modify: (id: string, data: any) => apiClient.patch<any>(`/bookings/${id}`, data),
|
modify: (id: string, data: any) => apiClient.patch<any>(`/bookings/${id}`, data),
|
||||||
checkUsage: (id: string) => apiClient.get<any>(`/bookings/${id}/usage`),
|
checkUsage: (id: string) => apiClient.get<any>(`/bookings/${id}/usage`),
|
||||||
|
|||||||
Reference in New Issue
Block a user