Nationality, guest booking, waafi adapter, overall booking flow updates

This commit is contained in:
Stephanos A
2026-05-24 16:20:55 +03:00
parent 2f17f9c9ce
commit 87a423c859
37 changed files with 2284 additions and 486 deletions

View File

@@ -1,19 +1,112 @@
import { Body, Controller, Get, Param, Post, UseGuards } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
import { ApiTags, ApiOperation, ApiBearerAuth, ApiResponse } from '@nestjs/swagger';
import { PassengersService } from './passengers.service';
import { CreateTravelerProfileDto, CreateSavedRouteDto } from './passengers.dto';
import { CreateTravelerProfileDto, CreateSavedRouteDto, VerifyFaydaDto, RegisterInternationalPassengerDto } from './passengers.dto';
import { JwtGuard } from '../../common/jwt.guard';
import { VerifaydaService } from '../verifayda/verifayda.service';
@ApiTags('Passenger')
@Controller('passengers')
@UseGuards(JwtGuard)
@ApiBearerAuth('JWT-auth')
export class PassengersController {
constructor(private service: PassengersService) {}
@Get(':id/profile') @ApiOperation({ summary: 'Get passenger profile' }) getProfile(@Param('id') id: string) { return this.service.getProfile(id); }
@Get(':id/stats') @ApiOperation({ summary: 'Get passenger stats' }) getStats(@Param('id') id: string) { return this.service.getStats(id); }
@Post('traveler-profiles') @ApiOperation({ summary: 'Add traveler profile (family member)' }) createTravelerProfile(@Body() dto: CreateTravelerProfileDto) { return this.service.createTravelerProfile(dto); }
@Get(':id/traveler-profiles') @ApiOperation({ summary: 'Get traveler profiles for passenger' }) getTravelerProfiles(@Param('id') id: string) { return this.service.getTravelerProfiles(id); }
@Post('saved-routes') @ApiOperation({ summary: 'Save a route' }) createSavedRoute(@Body() dto: CreateSavedRouteDto) { return this.service.createSavedRoute(dto); }
@Get(':id/saved-routes') @ApiOperation({ summary: 'Get saved routes' }) getSavedRoutes(@Param('id') id: string) { return this.service.getSavedRoutes(id); }
constructor(
private service: PassengersService,
private verifaydaService: VerifaydaService,
) {}
@Get(':id/profile')
@UseGuards(JwtGuard)
@ApiBearerAuth('JWT-auth')
@ApiOperation({ summary: 'Get passenger profile' })
getProfile(@Param('id') id: string) {
return this.service.getProfile(id);
}
@Get(':id/stats')
@UseGuards(JwtGuard)
@ApiBearerAuth('JWT-auth')
@ApiOperation({ summary: 'Get passenger stats' })
getStats(@Param('id') id: string) {
return this.service.getStats(id);
}
@Post('verify-fayda')
@ApiOperation({
summary: 'Verify Ethiopian national ID via Verifayda 2.0',
description: `Verifies Ethiopian national ID and retrieves passenger data from government database.
- Real-time verification via Verifayda 2.0 API
- Retrieves verified passenger data (name, DOB, gender, nationality)
- National IDs NOT stored (policy compliant)
- Only for Ethiopian nationals with national ID
- Non-Ethiopians should use passport (no verification required)
- Returns passenger details for booking form auto-fill`,
})
@ApiResponse({
status: 200,
description: 'Verification successful with passenger data',
schema: {
example: {
verified: true,
passengerData: {
fullName: 'Abebe Kebede',
dateOfBirth: '1985-03-15T00:00:00.000Z',
gender: 'Male',
nationality: 'Ethiopian'
}
}
}
})
@ApiResponse({ status: 400, description: 'Verification failed or Verifayda disabled' })
verifyFayda(@Body() dto: VerifyFaydaDto) {
return this.verifaydaService.verifyNationalId(dto.nationalId);
}
@Post('register-international')
@ApiOperation({
summary: 'Register international passenger with passport details',
description: `Saves international passenger profile for booking.
- For non-Ethiopian passengers (Djiboutian, Kenyan, etc.)
- Collects passport information
- No government verification required
- Profile saved for future bookings
- Can be used by logged-in users or guest users (via deviceId)`,
})
@ApiResponse({ status: 201, description: 'International passenger profile saved successfully' })
@ApiResponse({ status: 400, description: 'Invalid passport details' })
registerInternational(@Body() dto: RegisterInternationalPassengerDto) {
return this.service.registerInternational(dto);
}
@Post('traveler-profiles')
@UseGuards(JwtGuard)
@ApiBearerAuth('JWT-auth')
@ApiOperation({ summary: 'Add traveler profile (family member)' })
createTravelerProfile(@Body() dto: CreateTravelerProfileDto) {
return this.service.createTravelerProfile(dto);
}
@Get(':id/traveler-profiles')
@UseGuards(JwtGuard)
@ApiBearerAuth('JWT-auth')
@ApiOperation({ summary: 'Get traveler profiles for passenger' })
getTravelerProfiles(@Param('id') id: string) {
return this.service.getTravelerProfiles(id);
}
@Post('saved-routes')
@UseGuards(JwtGuard)
@ApiBearerAuth('JWT-auth')
@ApiOperation({ summary: 'Save a route' })
createSavedRoute(@Body() dto: CreateSavedRouteDto) {
return this.service.createSavedRoute(dto);
}
@Get(':id/saved-routes')
@UseGuards(JwtGuard)
@ApiBearerAuth('JWT-auth')
@ApiOperation({ summary: 'Get saved routes' })
getSavedRoutes(@Param('id') id: string) {
return this.service.getSavedRoutes(id);
}
}

View File

@@ -1,4 +1,4 @@
import { IsString, IsOptional, IsDateString } from 'class-validator';
import { IsString, IsOptional, IsDateString, IsEnum } from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
export class CreateTravelerProfileDto {
@@ -17,3 +17,52 @@ export class CreateSavedRouteDto {
@ApiProperty({ example: 'Addis Ababa' }) @IsString() fromName: string;
@ApiProperty({ example: 'Dire Dawa' }) @IsString() toName: string;
}
export class VerifyFaydaDto {
@ApiProperty({ example: 'ET123456789', description: 'Ethiopian national ID number' })
@IsString()
nationalId: string;
}
export class RegisterInternationalPassengerDto {
@ApiProperty({ example: 'John Smith', description: 'Full name as on passport' })
@IsString()
passengerName: string;
@ApiProperty({ example: '1990-07-20', description: 'Date of birth' })
@IsDateString()
dateOfBirth: string;
@ApiProperty({ example: 'P1234567', description: 'Passport number' })
@IsString()
passportNumber: string;
@ApiProperty({ example: 'Kenya', description: 'Passport issuing country' })
@IsString()
passportCountry: string;
@ApiPropertyOptional({ example: 'Kenyan', description: 'Nationality' })
@IsOptional()
@IsString()
nationality?: string;
@ApiPropertyOptional({ example: '+254712345678', description: 'Phone number' })
@IsOptional()
@IsString()
phone?: string;
@ApiPropertyOptional({ example: 'john@example.com', description: 'Email address' })
@IsOptional()
@IsString()
email?: string;
@ApiPropertyOptional({ description: 'User ID if logged in' })
@IsOptional()
@IsString()
userId?: string;
@ApiPropertyOptional({ description: 'Device ID for guest users' })
@IsOptional()
@IsString()
deviceId?: string;
}

View File

@@ -1,6 +1,11 @@
import { Module } from '@nestjs/common';
import { PassengersController } from './passengers.controller';
import { PassengersService } from './passengers.service';
import { VerifaydaModule } from '../verifayda/verifayda.module';
@Module({ controllers: [PassengersController], providers: [PassengersService] })
@Module({
imports: [VerifaydaModule],
controllers: [PassengersController],
providers: [PassengersService]
})
export class PassengersModule {}

View File

@@ -1,6 +1,6 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../../common/prisma.service';
import { CreateTravelerProfileDto, CreateSavedRouteDto } from './passengers.dto';
import { CreateTravelerProfileDto, CreateSavedRouteDto, RegisterInternationalPassengerDto } from './passengers.dto';
@Injectable()
export class PassengersService {
@@ -45,6 +45,32 @@ export class PassengersService {
return { totalTrips, totalSpend, loyaltyPoints: loyalty?.pointsBalance ?? 0, co2Saved: totalTrips * 6 };
}
async registerInternational(dto: RegisterInternationalPassengerDto) {
const profile = await this.prisma.savedPassengerProfile.create({
data: {
userId: dto.userId,
deviceId: dto.deviceId,
passengerName: dto.passengerName,
dateOfBirth: new Date(dto.dateOfBirth),
idDocumentType: 'PASSPORT',
passportNumber: dto.passportNumber,
passportCountry: dto.passportCountry,
nationality: dto.nationality,
phone: dto.phone,
email: dto.email,
},
});
return {
id: profile.id,
passengerName: profile.passengerName,
dateOfBirth: profile.dateOfBirth,
passportNumber: profile.passportNumber,
passportCountry: profile.passportCountry,
nationality: profile.nationality,
message: 'International passenger profile saved successfully',
};
}
createTravelerProfile(dto: CreateTravelerProfileDto) {
return this.prisma.travelerProfile.create({ data: { ...dto, dateOfBirth: dto.dateOfBirth ? new Date(dto.dateOfBirth) : null } });
}