import { Controller, Get, Post, Param, Query, Body } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiQuery, ApiBody } from '@nestjs/swagger'; import { Public } from '../decorators/public.decorator'; import { CsvBookingService } from '../services/csv-booking.service'; import { CarrierDocumentsResponseDto, VerifyDocumentAccessDto, DocumentAccessRequirementsDto, } from '../dto/carrier-documents.dto'; /** * CSV Booking Actions Controller (Public Routes) * * Handles public accept/reject actions from carrier emails * Separated from main controller to avoid routing conflicts */ @ApiTags('CSV Booking Actions') @Controller('csv-booking-actions') export class CsvBookingActionsController { constructor(private readonly csvBookingService: CsvBookingService) {} /** * Accept a booking request (PUBLIC - token-based) * * GET /api/v1/csv-booking-actions/accept/:token */ @Public() @Get('accept/:token') @ApiOperation({ summary: 'Accept booking request (public)', description: 'Public endpoint for carriers to accept a booking via email link. Updates booking status and notifies the user.', }) @ApiParam({ name: 'token', description: 'Booking confirmation token (UUID)' }) @ApiResponse({ status: 200, description: 'Booking accepted successfully.', }) @ApiResponse({ status: 404, description: 'Booking not found or invalid token' }) @ApiResponse({ status: 400, description: 'Booking cannot be accepted (invalid status or expired)', }) async acceptBooking(@Param('token') token: string) { // Accept the booking const booking = await this.csvBookingService.acceptBooking(token); // Return simple success response return { success: true, bookingId: booking.id, action: 'accepted', }; } /** * Reject a booking request (PUBLIC - token-based) * * GET /api/v1/csv-booking-actions/reject/:token */ @Public() @Get('reject/:token') @ApiOperation({ summary: 'Reject booking request (public)', description: 'Public endpoint for carriers to reject a booking via email link. Updates booking status and notifies the user.', }) @ApiParam({ name: 'token', description: 'Booking confirmation token (UUID)' }) @ApiQuery({ name: 'reason', required: false, description: 'Rejection reason', example: 'No capacity available', }) @ApiResponse({ status: 200, description: 'Booking rejected successfully.', }) @ApiResponse({ status: 404, description: 'Booking not found or invalid token' }) @ApiResponse({ status: 400, description: 'Booking cannot be rejected (invalid status or expired)', }) async rejectBooking(@Param('token') token: string, @Query('reason') reason: string) { // Reject the booking const booking = await this.csvBookingService.rejectBooking(token, reason); // Return simple success response return { success: true, bookingId: booking.id, action: 'rejected', reason: reason || null, }; } /** * Check document access requirements (PUBLIC - token-based) * * GET /api/v1/csv-booking-actions/documents/:token/requirements */ @Public() @Get('documents/:token/requirements') @ApiOperation({ summary: 'Check document access requirements (public)', description: 'Check if a password is required to access booking documents. Use this before showing the password form.', }) @ApiParam({ name: 'token', description: 'Booking confirmation token (UUID)' }) @ApiResponse({ status: 200, description: 'Access requirements retrieved successfully.', type: DocumentAccessRequirementsDto, }) @ApiResponse({ status: 404, description: 'Booking not found or invalid token' }) async getDocumentAccessRequirements( @Param('token') token: string ): Promise { return this.csvBookingService.checkDocumentAccessRequirements(token); } /** * Get booking documents for carrier with password verification (PUBLIC - token-based) * * POST /api/v1/csv-booking-actions/documents/:token */ @Public() @Post('documents/:token') @ApiOperation({ summary: 'Get booking documents with password (public)', description: 'Public endpoint for carriers to access booking documents after acceptance. Requires password verification. Returns booking summary and documents with signed download URLs.', }) @ApiParam({ name: 'token', description: 'Booking confirmation token (UUID)' }) @ApiBody({ type: VerifyDocumentAccessDto }) @ApiResponse({ status: 200, description: 'Booking documents retrieved successfully.', type: CarrierDocumentsResponseDto, }) @ApiResponse({ status: 404, description: 'Booking not found or invalid token' }) @ApiResponse({ status: 400, description: 'Booking has not been accepted yet' }) @ApiResponse({ status: 401, description: 'Invalid password' }) async getBookingDocumentsWithPassword( @Param('token') token: string, @Body() dto: VerifyDocumentAccessDto ): Promise { return this.csvBookingService.getDocumentsForCarrier(token, dto.password); } /** * Get booking documents for carrier (PUBLIC - token-based) - Legacy without password * Kept for backward compatibility with bookings created before password protection * * GET /api/v1/csv-booking-actions/documents/:token */ @Public() @Get('documents/:token') @ApiOperation({ summary: 'Get booking documents (public) - Legacy', description: 'Public endpoint for carriers to access booking documents. For new bookings, use POST with password instead.', }) @ApiParam({ name: 'token', description: 'Booking confirmation token (UUID)' }) @ApiResponse({ status: 200, description: 'Booking documents retrieved successfully.', type: CarrierDocumentsResponseDto, }) @ApiResponse({ status: 404, description: 'Booking not found or invalid token' }) @ApiResponse({ status: 400, description: 'Booking has not been accepted yet' }) @ApiResponse({ status: 401, description: 'Password required for this booking' }) async getBookingDocuments(@Param('token') token: string): Promise { return this.csvBookingService.getDocumentsForCarrier(token); } }